Cesium加载3Dtiles模型的平移和旋转
一、基础说明
Cesium中点由
Cartesian3
表示,类似POINT3D点,其形式为包含 (x, y, z)的一维向量Cesium中默认坐标系以地心为原点,所有未指明的
Cartesian3
都基于此坐标系我们需要进行的平移与旋转基于模型所在点为原点,正东为X,正北为Y的局部坐标系
使用函数
Cesium.Transforms.eastNorthUpToFixedFrame(origin)
获取上述局部坐标系中点转换为世界坐标系中的位置的变换矩阵,P世界=M变换矩阵P局部3Dtiles模型的变换由一个4维矩阵完成,通过不断左乘变换矩阵可以完成一系列的复杂变换
二、模型平移与调整透明度
使用vue2,参考官方调整模型高度的案例。
假设模型的原点在世界坐标系下坐标为(xorigin, yorigin, zorigin)
获取坐标系变换矩阵
假设模型需要平移(x, y, z)量,先计算局部坐标系中点(x, y, z)在世界坐标下的位置(xworld, yworld, zworld)
const offset = Cesium.Matrix4.multiplyByPoint(m, tempTranslation, new Cesium.Cartesian3(0, 0, 0));
使用(xworld, yworld, zworld)-(xorigin, yorigin, zorigin)获得世界坐标系中平移向量
使用
Cesium.Matrix4.fromTranslation(translation);
获取3Dtiles平移变换矩阵透明度调整
tileset.style = new Cesium.Cesium3DTileStyle({color: "color('rgba(255,255,255," + opacity + ")')"});
transferModel(tileset, _tx, _ty, _tz,_opacity) {
if(!this.checkModelLoad()){
return
}
let tx = _tx ? _tx : 0;
let ty = _ty ? _ty : 0;
let tz = _tz ? _tz : 0;
let opacity = _opacity ? _opacity/100 : 1
const origin = tileset.boundingSphere.center;
const m = Cesium.Transforms.eastNorthUpToFixedFrame(origin);//获取到以模型中心为原点,Z轴垂直地表的局部坐标系,以矩阵表示,此矩阵为将局部坐标系变换到世界坐标系的变换矩阵
//平移
const tempTranslation = new Cesium.Cartesian3(tx, ty, tz);//平移向量
const offset = Cesium.Matrix4.multiplyByPoint(m, tempTranslation, new Cesium.Cartesian3(0, 0, 0));//局部坐标中(tx,ty,tz)在世界坐标系中位置
const translation = Cesium.Cartesian3.subtract(offset, origin, new Cesium.Cartesian3());//终点世界坐标减去原点世界坐标得到世界坐标系下平移向量
tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
//透明度
tileset.style = new Cesium.Cesium3DTileStyle({
color: "color('rgba(255,255,255," + opacity + ")')",
});
},
三、模型旋转
模型旋转时,由于Cesium默认绕轴旋转是绕世界坐标系轴旋转,因此我们需要做如下操作完成绕局部坐标轴旋转
将模型原点平移回世界坐标系原点(地心),矩阵记为T1
将局部坐标Z轴调整到与世界坐标Z轴重合,矩阵记为R1
将模型绕世界坐标系的某个轴旋转(真正进行旋转的矩阵),此处以Z轴为例,矩阵记为R
将局部坐标Z轴旋转回原来的指向,矩阵记为R2
平移回初始位置,矩阵记为T2
最终模型旋转的矩阵为T2R2RR1T1
将旋转开始前模型的初始变换矩阵左乘此矩阵即可完成旋转变换
(一)回到地心
const origin = tileset.boundingSphere.center;
console.log("初始世界坐标", origin)
const localToWorldMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);//获取到以模型中心为原点,Z轴垂直地表的局部坐标系的变换矩阵,左乘此矩阵可以将局部坐标变换为世界坐标
const originMatrix = tileset.modelMatrix//贴地变换矩阵或者初始变换矩阵M0
console.log("当前坐标变换矩阵", localToWorldMatrix)
const backToEarthCenter = new Cesium.Cartesian3(-origin.x, -origin.y, -origin.z)//回到地心位移量
let backToEarthCenterMatrix = Cesium.Matrix4.fromTranslation(backToEarthCenter);//回到地心变换矩阵
Cesium.Matrix4.multiply(backToEarthCenterMatrix, originMatrix, backToEarthCenterMatrix)//贴地变换矩阵左乘回到地心矩阵 T1M0
console.log("回到地心变换矩阵", backToEarthCenterMatrix)
(二)局部Z轴校正
先将红线表示的向量(z'轴)绕Z轴顺时针旋转Φ度,再绕Y轴顺时针旋转θ度
1、通过一维向量表示坐标轴,通过左乘变换矩阵完成局部坐标轴在世界坐标系下的表示
//旋转模型使得Z轴与世界坐标Z轴重合
let arrowX = new Cesium.Cartesian3(1, 0, 0)
let arrowY = new Cesium.Cartesian3(0, 1, 0)
let arrowZ = new Cesium.Cartesian3(0, 0, 1)
let localArrowX = Cesium.Matrix4.multiplyByPoint(localToWorldMatrix, new Cesium.Cartesian3(1, 0, 0), new Cesium.Cartesian3)
let localArrowY = Cesium.Matrix4.multiplyByPoint(localToWorldMatrix, new Cesium.Cartesian3(0, 1, 0), new Cesium.Cartesian3)
let localArrowZ = Cesium.Matrix4.multiplyByPoint(localToWorldMatrix, new Cesium.Cartesian3(0, 0, 1), new Cesium.Cartesian3)
2、先绕世界Z轴旋转局部Z轴到世界XOZ面上,再旋转到Z轴上
Cesium.Cartesian3.angleBetween
可计算两个向量间的弧度Cesium.Cartesian3.angleBetween(arrowX, new Cesium.Cartesian3(localArrowZ.x, localArrowZ.y, 0))
即为Φ对应弧度Cesium.Cartesian3.angleBetween(localArrowX, arrowZ)
即为θ对应弧度Cesium.Matrix3.fromRotationX、Cesium.Matrix3.fromRotationY、Cesium.Matrix3.fromRotationZ
的旋转正方向为逆时针,传入参数为弧度
3、需要注意由于只对齐了Z轴,如需绕X轴,Y轴旋转需要做类似的处理
let angleToXZ = Cesium.Cartesian3.angleBetween(arrowX, new Cesium.Cartesian3(localArrowZ.x, localArrowZ.y, 0))//局部Z轴在世界坐标系XY平面上投影到X轴角度,即绕Z顺时针旋转这个角度可以到XZ平面上
let angleToZ = Cesium.Cartesian3.angleBetween(localArrowX, arrowZ)//然后绕Y轴顺时针旋转此角度可使得Z轴与世界坐标系Z轴重合
const rotationAngleToXZ = Cesium.Matrix3.fromRotationZ(-angleToXZ);//此函数正方向为逆时针
const rotationAngleToZ = Cesium.Matrix3.fromRotationY(-angleToZ);
let rotationAngleToZMatrix = Cesium.Matrix3.multiply(rotationAngleToZ, rotationAngleToXZ, new Cesium.Matrix3)
rotationAngleToZMatrix = Cesium.Matrix4.fromRotationTranslation(rotationAngleToZMatrix)
Cesium.Matrix4.multiply(rotationAngleToZMatrix, backToEarthCenterMatrix, rotationAngleToZMatrix)//局部轴校正R1T1M0
(三)真实旋转矩阵
此处完成真正需要进行的旋转,此处以绕Z轴旋转为例
// 绕Z轴旋转
console.log(rz - this.originRz)
const rotationZ = Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(rz - this.originRz)); // 绕Z轴旋转变换矩阵R
let rotationMatrix = Cesium.Matrix4.fromRotationTranslation(rotationZ)
Cesium.Matrix4.multiply(rotationMatrix, rotationAngleToZMatrix, rotationMatrix)//RR1T1M0
(四)局部Z轴回位
将局部Z轴回到原本的指向,即刚刚旋转的角度取负值,此处需要注意变换的顺序,后进行的变换先复位
校正矩阵:
Cesium.Matrix3.multiply(rotationAngleToZ, rotationAngleToXZ, new Cesium.Matrix3)
复位矩阵:
Cesium.Matrix3.multiply(rotationAngleLeaveXZ, rotationAngleLeaveZ, new Cesium.Matrix3)
// 旋转模型回到原本朝向
const rotationAngleLeaveXZ = Cesium.Matrix3.fromRotationZ(angleToXZ);
const rotationAngleLeaveZ = Cesium.Matrix3.fromRotationY(angleToZ);
let rotationAngleLeaveZMatrix = Cesium.Matrix3.multiply(rotationAngleLeaveXZ, rotationAngleLeaveZ, new Cesium.Matrix3)
rotationAngleLeaveZMatrix = Cesium.Matrix4.fromRotationTranslation(rotationAngleLeaveZMatrix)// 局部Z轴回到原本方向
Cesium.Matrix4.multiply(rotationAngleLeaveZMatrix, rotationMatrix, rotationAngleLeaveZMatrix)//R2RR1T1M0
(五)回到原本位置
位移量即为模型原本的世界坐标,直接可获得变换矩阵
//回到原来位置
const backToOriginMatrix = Cesium.Matrix4.fromTranslation(origin);//从地心回归原位T2
(六)绕Z轴旋转完整代码
rotationModel(tileset,rz) {
if(!this.checkModelLoad()){
return
}
const origin = tileset.boundingSphere.center;
console.log("初始世界坐标", origin)
const localToWorldMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);//获取到以模型中心为原点,Z轴垂直地表的局部坐标系的变换矩阵,左乘此矩阵可以将局部坐标变换为世界坐标
const originMatrix = tileset.modelMatrix//贴地变换矩阵或者初始变换矩阵M0
console.log("当前坐标变换矩阵", localToWorldMatrix)
const backToEarthCenter = new Cesium.Cartesian3(-origin.x, -origin.y, -origin.z)//回到地心位移量
let backToEarthCenterMatrix = Cesium.Matrix4.fromTranslation(backToEarthCenter);//回到地心变换矩阵
Cesium.Matrix4.multiply(backToEarthCenterMatrix, originMatrix, backToEarthCenterMatrix)//贴地变换矩阵左乘回到地心矩阵 T1M0
console.log("回到地心变换矩阵", backToEarthCenterMatrix)
// 旋转
//旋转模型使得Z轴与世界坐标Z轴重合
let arrowX = new Cesium.Cartesian3(1, 0, 0)
let arrowY = new Cesium.Cartesian3(0, 1, 0)
let arrowZ = new Cesium.Cartesian3(0, 0, 1)
let localArrowX = Cesium.Matrix4.multiplyByPoint(localToWorldMatrix, new Cesium.Cartesian3(1, 0, 0), new Cesium.Cartesian3)
let localArrowY = Cesium.Matrix4.multiplyByPoint(localToWorldMatrix, new Cesium.Cartesian3(0, 1, 0), new Cesium.Cartesian3)
let localArrowZ = Cesium.Matrix4.multiplyByPoint(localToWorldMatrix, new Cesium.Cartesian3(0, 0, 1), new Cesium.Cartesian3)
let angleToXZ = Cesium.Cartesian3.angleBetween(arrowX, new Cesium.Cartesian3(localArrowZ.x, localArrowZ.y, 0))//局部Z轴在世界坐标系XY平面上投影到X轴角度,即绕Z顺时针旋转这个角度可以到XZ平面上
let angleToZ = Cesium.Cartesian3.angleBetween(localArrowX, arrowZ)//然后绕Y轴顺时针旋转此角度可使得Z轴与世界坐标系Z轴重合
const rotationAngleToXZ = Cesium.Matrix3.fromRotationZ(-angleToXZ);//此函数正方向为逆时针
const rotationAngleToZ = Cesium.Matrix3.fromRotationY(-angleToZ);
let rotationAngleToZMatrix = Cesium.Matrix3.multiply(rotationAngleToZ, rotationAngleToXZ, new Cesium.Matrix3)
rotationAngleToZMatrix = Cesium.Matrix4.fromRotationTranslation(rotationAngleToZMatrix)
Cesium.Matrix4.multiply(rotationAngleToZMatrix, backToEarthCenterMatrix, rotationAngleToZMatrix)//局部轴校正R1T1M0
// 绕Z轴旋转
console.log(rz - this.originRz)
const rotationZ = Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(rz - this.originRz)); // 绕Z轴旋转变换矩阵R
let rotationMatrix = Cesium.Matrix4.fromRotationTranslation(rotationZ)
Cesium.Matrix4.multiply(rotationMatrix, rotationAngleToZMatrix, rotationMatrix)//RR1T1M0
// 旋转模型回到原本朝向
const rotationAngleLeaveXZ = Cesium.Matrix3.fromRotationZ(angleToXZ);
const rotationAngleLeaveZ = Cesium.Matrix3.fromRotationY(angleToZ);
let rotationAngleLeaveZMatrix = Cesium.Matrix3.multiply(rotationAngleLeaveXZ, rotationAngleLeaveZ, new Cesium.Matrix3)
rotationAngleLeaveZMatrix = Cesium.Matrix4.fromRotationTranslation(rotationAngleLeaveZMatrix)// 局部Z轴回到原本方向
Cesium.Matrix4.multiply(rotationAngleLeaveZMatrix, rotationMatrix, rotationAngleLeaveZMatrix)//R2RR1T1M0
//回到原来位置
const backToOriginMatrix = Cesium.Matrix4.fromTranslation(origin);//从地心回归原位T2
// 应用变换矩阵
const lastMatrix = Cesium.Matrix4.multiply(backToOriginMatrix, rotationAngleLeaveZMatrix, new Cesium.Matrix4)//最终矩阵T2R2RR1T1M0
tileset.modelMatrix = lastMatrix
console.log("结束世界坐标", tileset.boundingSphere.center)
this.originRz = rz
},