详解Cesium3DTileset的平移、旋转、缩放(含封装好的代码)

演示

如果只是想要代码那么可以接着往下看,如果想要详细的了解思路,那么最好移步b站观看详细的视频教程,视频链接。里面我会详细的说明每一个步骤的作用。

一、说明

在cesium中对3DTileset进行变换是非常非常常见的操作,因为3DTileset数据总是不能很好的拟合底图。大部分人这时候去查找相关解决办法是非常折磨人的,因为我也搜素查找了一番,真的就是那啥里淘金,而这篇文章就可以帮助大家直接解决这个问题。

我们一般平移、旋转、缩放都是按照某个轴来进行的,例如X轴方向平移50米、Y轴方向缩放1.2倍、绕Z轴旋转多少多少度来着。这里的XYZ轴,大家肯定希望的都是ENU坐标系(也叫站心坐标系)下的轴向(也就是人站在地面上,面朝北方,右手X轴正方向、面前Y轴正方向,指向天为Z轴正方向),肯定不会想要的是世界坐标系下的轴向。

这里附上两个坐标系在一起的图片(绿色的是ENU坐标系,红色的就是世界坐标系,也就是大家了解的WGS-84坐标系)

1e93007c52b245eb8513cda36023b736.png

 

二、代码

废话不多说,直接上代码,步骤解释都写在注释里了(基于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中使用了

这是我的第一篇文章,如果本篇文章对你有帮助的话不要忘了点赞收藏哦。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值