背景:在没有cesiumlab官方授权的情况下,对于fbx,obj等模型无法使用坐标系和偏移量进行切片处理 ,此时用cesiumlab的默认参数生产的切片模型数据,默认位置在北京天安门上空,与实际位置有距离很大偏差,不能只通过平移矩阵移动模型到实际位置,还需要对应的旋转矩阵。
目的:通过cesium的内置函数,将模型移动到实际位置。
思路顺序:
-
将模型平移回世界坐标系原点(地心)
-
将局部坐标Z轴调整到与世界坐标Z轴重合
-
将局部坐标X,Y轴调整到与世界坐标X,Y轴重合
-
将目标位置的eastNorthUp局部坐标系平移回世界坐标系原点(地心)
-
旋转物体坐标系与目标坐标系重合
-
平移到目标位置,即为最终变换矩阵
具体思路细节可参考csdn文章: Cesium加载3Dtiles模型的平移和旋转
矩阵很抽象,想要了解矩阵相关的详细信息,推荐观看书籍《3D数学基础:图形与游戏开发(第一版)》,网络上很多pdf,注意,请看第一版,不要看第二版,第二版可能是机翻,很多语句难以理解。
function moveModel(tileset,longitude,latitude,height) {
//计算世界坐标系中的目标位置offset
var cartographic = new Cesium.Cartographic.fromCartesian(
tileset.boundingSphere.center
);
var offset = Cesium.Cartesian3.fromDegrees(longitude,latitude,cartographic.height+height);
//将模型位移至地心
const origin = tileset.boundingSphere.center;
const originMatrix = tileset.modelMatrix;//模型的初始变换矩阵
const backToEarthCenter = new Cesium.Cartesian3(-origin.x,-origin.y,-origin.z);//初始位置到地心的位移向量
let backToEarthCenterMatrix = Cesium.Matrix4.fromTranslation(backToEarthCenter);//初始位置到地心的变换矩阵
Cesium.Matrix4.multiply(backToEarthCenterMatrix, originMatrix, backToEarthCenterMatrix);//移动模型到地心的矩阵
// 旋转模型使得Z轴与世界坐标Z轴重合
let arrowX = new Cesium.Cartesian3(1, 0, 0);
let arrowZ = new Cesium.Cartesian3(0, 0, 1);
let angleToXZ = Cesium.Cartesian3.angleBetween(arrowX, new Cesium.Cartesian3(origin.x, origin.y, 0));//局部Z轴在世界坐标系XY平面上投影到X轴角度,即绕Z顺时针旋转这个角度可以到XZ平面上
let angleToZ = Cesium.Cartesian3.angleBetween(origin, arrowZ);//然后绕Y轴顺时针旋转此角度可使得Z轴与世界坐标系Z轴重合
const rotationAngleToXZ = Cesium.Matrix3.fromRotationZ((origin.y>0?-1:+1)*angleToXZ);//绕Z轴旋转的Matrix3矩阵,正角度逆时针旋转
const rotationAngleToZ = Cesium.Matrix3.fromRotationY(-angleToZ);//绕Y轴旋转的Matrix3矩阵,负角度顺时针旋转
let rotationAngleToZMatrix = Cesium.Matrix3.multiply(rotationAngleToZ, rotationAngleToXZ, new Cesium.Matrix3);//连续旋转的Matrix3矩阵,即先绕Z轴旋转,后绕Y旋转的矩阵。
rotationAngleToZMatrix = Cesium.Matrix4.fromRotationTranslation(rotationAngleToZMatrix);//连续旋转的Matrix4矩阵
Cesium.Matrix4.multiply(rotationAngleToZMatrix, backToEarthCenterMatrix, rotationAngleToZMatrix);//将移动至地心模型,旋转至Z轴重合的矩阵
// 旋转模型使得X,Y轴与世界坐标X,Y轴重合
const rotationZ = Cesium.Matrix3.fromRotationZ(-Math.PI/2); // 绕Z轴旋转90°的Matrix3变换矩阵
let rotationMatrix = Cesium.Matrix4.fromRotationTranslation(rotationZ); // 绕Z轴旋转90°的Matrix4变换矩阵
Cesium.Matrix4.multiply(rotationMatrix, rotationAngleToZMatrix, rotationMatrix);//将移动至地心模型的物体坐标系,旋转到与世界坐标系重合的矩阵
//在地心位置,旋转物体坐标系和世界坐标系重合的模型,使得与目标坐标系重合
const offsetToWorldMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(offset);//获取到以目标位置为原点,的eastNorthUp局部坐标系的变换矩阵
const backToEarthCenterOffset = new Cesium.Cartesian3(-offset.x, -offset.y, -offset.z);//目标位置到地心的位移向量
let backToEarthCenterMatrixOffset = Cesium.Matrix4.fromTranslation(backToEarthCenterOffset);//目标位置到地心的变换矩阵
Cesium.Matrix4.multiply(backToEarthCenterMatrixOffset, offsetToWorldMatrix, backToEarthCenterMatrixOffset);//获得从世界坐标系旋转至目标坐标系的旋转矩阵(只有旋转,没有位移)
Cesium.Matrix4.multiply(backToEarthCenterMatrixOffset, rotationMatrix, backToEarthCenterMatrixOffset);//将移动至地心模型的物体坐标系,旋转到与目标坐标系重合的矩阵(完成模型的最终旋转,没有位移)
//移动到目标位置
const backToOriginMatrix = Cesium.Matrix4.fromTranslation(offset);//地心到目标位置位移向量
const lastMatrix = Cesium.Matrix4.multiply(backToOriginMatrix,backToEarthCenterMatrixOffset,new Cesium.Matrix4());//最终矩阵,即将地心位置的模型移动到目标位置(完成模型的最终旋转,最终位移)
console.log('最终变换矩阵',lastMatrix);
return lastMatrix //返回最终变换矩阵
}
let tileset = viewer.scene.primitives.add(
new Cesium.Cesium3DTileset({
url: ".../tileset.json",
})
);
tileset.readyPromise.then(function (tileset) {
window.tileset = tileset
let longitude = 104.98680
let latitude = 32.20795
let height = 100
let modelMatrix = moveModel(tileset,longitude,latitude,height)
tileset.modelMatrix = modelMatrix;//移动模型
// 创建圆形包围盒
let boundingSphere = new Cesium.BoundingSphere(
tileset.boundingSphere.center,
tileset.boundingSphere.radius
);
//飞向该包围盒
viewer.camera.flyToBoundingSphere(boundingSphere);
});