演示
如果只是想要代码那么可以接着往下看,如果想要详细的了解思路,那么最好移步b站观看详细的视频教程,视频链接。里面我会详细的说明每一个步骤的作用。
一、说明
在cesium中对3DTileset进行变换是非常非常常见的操作,因为3DTileset数据总是不能很好的拟合底图。大部分人这时候去查找相关解决办法是非常折磨人的,因为我也搜素查找了一番,真的就是那啥里淘金,而这篇文章就可以帮助大家直接解决这个问题。
我们一般平移、旋转、缩放都是按照某个轴来进行的,例如X轴方向平移50米、Y轴方向缩放1.2倍、绕Z轴旋转多少多少度来着。这里的XYZ轴,大家肯定希望的都是ENU坐标系(也叫站心坐标系)下的轴向(也就是人站在地面上,面朝北方,右手X轴正方向、面前Y轴正方向,指向天为Z轴正方向),肯定不会想要的是世界坐标系下的轴向。
这里附上两个坐标系在一起的图片(绿色的是ENU坐标系,红色的就是世界坐标系,也就是大家了解的WGS-84坐标系)
二、代码
废话不多说,直接上代码,步骤解释都写在注释里了(基于TS,做了完备的封装)
0、模块导入
import {
Cartesian3,
Matrix3,
Matrix4,
Transforms,
Math as CesiumMath,
Cesium3DTileset,
} from 'cesium'
1、平移
/**基于本地的ENU坐标系的偏移,也就是垂直于地表向上为Z,东为X,北为Y
* @param tileset Cesium3DTileset
* @param dx x轴偏移量。单位:米
* @param dy y轴偏移量。单位:米
* @param dz z轴偏移量。单位:米
*/
function translate(tileset: Cesium3DTileset, dx: number, dy: number, dz: number) {
if (dx === 0 && dy === 0 && dz === 0) return
// 对于3DTileset,我们需要的结果是一个模型矩阵,那么平移就是计算一个世界坐标下的平移矩阵。
// 获取中心点
const origin = tileset.boundingSphere.center
// 以该点建立ENU坐标系
const toWorldMatrix = Transforms.eastNorthUpToFixedFrame(origin)
// 该坐标系下平移后的位置
const translatePosition = new Cartesian3(dx, dy, dz)
// 获取平移后位置的世界坐标
const worldPosition = Matrix4.multiplyByPoint(toWorldMatrix, translatePosition, new Cartesian3())
// 计算世界坐标下的各个平移量
const offset = Cartesian3.subtract(worldPosition, origin, new Cartesian3())
// 从世界坐标下的平移量计算世界坐标的平移矩阵
const translateMatrix = Matrix4.fromTranslation(offset)
// 应用平移矩阵。这里应该与原本的模型矩阵点乘,而不是直接赋值
tileset.modelMatrix = Matrix4.multiply(translateMatrix, tileset.modelMatrix, new Matrix4())
}
2、缩放
/**基于本地的ENU坐标系的缩放,也就是垂直于地表向上为Z,东为X,北为Y
* @param tileset Cesium3DTileset
* @param sx x轴缩放倍数
* @param sy y轴缩放倍数
* @param sz z轴缩放倍数
*/
function scale(tileset: Cesium3DTileset, sx: number, sy: number, sz: number) {
if (sx <= 0 || sy <= 0 || sz <= 0) throw Error('缩放倍数必须大于0')
if (sx === 1 && sy === 1 && sz === 1) return
// 具体步骤是将3DTileset先转为ENU坐标系,再在ENU坐标系下计算缩放后的结果,再转回世界坐标系。一个步骤代表一个矩阵
// 获取中心点。
const origin = tileset.boundingSphere.center
// 以该点建立ENU坐标系
const toWorldMatrix = Transforms.eastNorthUpToFixedFrame(origin)
// 获取ENU矩阵的逆矩阵。也就是可以将世界坐标重新转为ENU坐标系的矩阵
const toLocalMatrix = Matrix4.inverse(toWorldMatrix, new Matrix4())
// 计算缩放矩阵
const scaleMatrix = Matrix4.fromScale(new Cartesian3(sx, sy, sz))
// ENU坐标系下的结果矩阵
const localResultMatrix = Matrix4.multiply(scaleMatrix, toLocalMatrix, new Matrix4())
// 世界坐标系下的结果矩阵
const worldResultMatrix = Matrix4.multiply(toWorldMatrix, localResultMatrix, new Matrix4())
// 应用结果
tileset.modelMatrix = Matrix4.multiply(worldResultMatrix, tileset.modelMatrix, new Matrix4())
}
3、旋转
/**基于本地的ENU坐标系的旋转,也就是垂直于地表向上为Z,东为X,北为Y
* @param tileset Cesium3DTileset
* @param rx 绕X轴旋转的角度。单位:度
* @param ry 绕Y轴旋转的角度。单位:度
* @param rz 绕Z轴旋转的角度。单位:度
*/
function rotate(tileset: Cesium3DTileset, rx: number, ry: number, rz: number) {
if (rx === 0 && ry === 0 && rz === 0) return
// 获取中心点。
const origin = tileset.boundingSphere.center
// 以该点建立ENU坐标系
const toWorldMatrix = Transforms.eastNorthUpToFixedFrame(origin)
// 获取ENU矩阵的逆矩阵。也就是可以将世界坐标重新转为ENU坐标系的矩阵
const toLocalMatrix = Matrix4.inverse(toWorldMatrix, new Matrix4())
// 计算旋转矩阵
const rotateMatrix = Matrix4.clone(Matrix4.IDENTITY)
if (rx !== 0) {
const rotateXMatrix = Matrix4.fromRotation(Matrix3.fromRotationX(CesiumMath.toRadians(rx)))
Matrix4.multiply(rotateXMatrix, rotateMatrix, rotateMatrix)
}
if (ry !== 0) {
const rotateYMatrix = Matrix4.fromRotation(Matrix3.fromRotationY(CesiumMath.toRadians(ry)))
Matrix4.multiply(rotateYMatrix, rotateMatrix, rotateMatrix)
}
if (rz !== 0) {
const rotateZMatrix = Matrix4.fromRotation(Matrix3.fromRotationZ(CesiumMath.toRadians(rz)))
Matrix4.multiply(rotateZMatrix, rotateMatrix, rotateMatrix)
}
// ENU坐标系下的结果矩阵
const localResultMatrix = Matrix4.multiply(rotateMatrix, toLocalMatrix, new Matrix4())
// 世界坐标系下的结果矩阵
const worldResultMatrix = Matrix4.multiply(toWorldMatrix, localResultMatrix, new Matrix4())
// 应用结果
tileset.modelMatrix = Matrix4.multiply(worldResultMatrix, tileset.modelMatrix, new Matrix4())
}
最后
每个函数都可以单独使用,代码去掉TS类型注释就可以在JS中使用了
这是我的第一篇文章,如果本篇文章对你有帮助的话不要忘了点赞收藏哦。