最近有一个需求是要实现primitive图层绕自身中心点旋转,如下图:
我本来以为是一个上网搜一搜就能解决的简单问题,没想到搜到的答案全是不可用的,包括gpt给出的答案也是不可用,通常是转一下图层就跑到外太空去了。
后来仔细调研了一下,发现是因为Cesium新版本中旋转是绕地球球心去转的,所以网上那些适用于老版本的方法全部不可用。本人用的是cesium的1.107版本,没办法,只能自己实现一个新版本中可用的旋转方法了。
首先普及一个数学知识:三维空间中绕任意向量轴的旋转都可以分解为绕x,y,z轴的旋转
可以看到,上图中的正方形是绕着地球球心到正方形中心点这条连线来旋转的,所以我们要把绕这条轴线的旋转拆解为绕cesium笛卡尔三维坐标系中x,y,z三轴的旋转。
具体数学原理请参考csdn某大佬的文章绕任意向量旋转分解到坐标系旋转_例.,求受换av,使过原点的向童v=(a,b,c)与z轴的正向一致。y yvx x现步骤:-CSDN博客)
清楚了数学原理,那么如何在cesium中实现上述动图中的旋转动画呢?下面直接上源码(强烈建议先看看上面那篇csdn文章里介绍的数学原理,讲得很清晰易懂):
// 绘制一个红色正方形primitive
const rectangle = new Cesium.RectangleGeometry({
ellipsoid: Cesium.Ellipsoid.WGS84,
rectangle: Cesium.Rectangle.fromDegrees(116, 16, 118, 18),
height: 10000.0,
});
const primitive = new Cesium.Primitive({
geometryInstances: new Cesium.GeometryInstance({
geometry: rectangle,
}),
appearance: new Cesium.EllipsoidSurfaceAppearance({
material: Cesium.Material.fromType("Color", {
color: new Cesium.Color(1.0, 0.0, 0.0, 1.0),
}),
}),
});
viewer.scene.primitives.add(primitive);
// 动画帧中持续旋转正方形
let angle = 0;
rotate();
function rotate() {
// 以地球中心到正方形中心点的连线为旋转轴,旋转正方形。(117,17)为上述正方形中心经纬度
const rotateAxis = Cesium.Cartesian3.fromDegrees(117, 17);
// modelMatrix为正方形primitive的模型矩阵,通过修改模型矩阵实现正方形的旋转
primitive.modelMatrix = getRotateMatrix(rotateAxis, ++angle);
requestAnimationFrame(rotate);
}
});
上面代码中的关键是getRotateMatrix函数,下面来看看这个函数的具体实现:
/**
* @param rotateAxis 旋转轴向量
* @param angle 旋转角度的角度值
* @returns 四维矩阵变换
*/
function getRotateMatrix(rotateAxis: Cesium.Cartesian3, angle: number) {
const { x, y, z } = rotateAxis;
// Alpha为将旋转轴向量绕x轴旋转到xoz平面所需的角度
// Beta为将已经旋转到xoz平面的旋转轴向量再次绕y轴旋转到yoz平面所需的角度
const sinAlpha = y / Math.sqrt(y * y + z * z);
const sinBeta = -x / Math.sqrt(x * x + y * y + z * z);
const alpha = Math.asin(sinAlpha);
const beta = Math.asin(sinBeta);
// step1 构造绕x轴旋转到xoz平面的旋转矩阵
const rotationX = Cesium.Matrix4.fromRotationTranslation(
Cesium.Matrix3.fromRotationX(alpha)
);
// step2 构造从xoz平面绕y轴旋转到yoz平面的旋转矩阵
const rotationY = Cesium.Matrix4.fromRotationTranslation(
Cesium.Matrix3.fromRotationY(beta)
);
// step3 经过上两步旋转后,旋转轴已经与z轴重合,此处继续构造绕z轴旋转angle角度的旋转矩阵
const rotationZ = Cesium.Matrix4.fromRotationTranslation(
Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(angle))
);
// 构造step2中的逆向旋转矩阵,恢复旋转轴绕y轴的旋转
const reverseRotationY = Cesium.Matrix4.fromRotationTranslation(
Cesium.Matrix3.fromRotationY(-beta)
);
// 构造step1中的逆向旋转矩阵,恢复旋转轴绕x轴的旋转
const reverseRotationX = Cesium.Matrix4.fromRotationTranslation(
Cesium.Matrix3.fromRotationX(-alpha)
);
// 初始单位矩阵依次左乘上述的旋转矩阵
const matrix4 = Cesium.Matrix4.multiply(
Cesium.Matrix4.IDENTITY,
Cesium.Matrix4.IDENTITY,
new Cesium.Matrix4()
);
Cesium.Matrix4.multiply(rotationX, matrix4, matrix4);
Cesium.Matrix4.multiply(rotationY, matrix4, matrix4);
Cesium.Matrix4.multiply(rotationZ, matrix4, matrix4);
Cesium.Matrix4.multiply(reverseRotationY, matrix4, matrix4);
Cesium.Matrix4.multiply(reverseRotationX, matrix4, matrix4);
return matrix4;
}
如果帮到你了,点一个赞再copy代码吧。有问题欢迎留言