计算机图形学:(五)坐标系
"全局"坐标系
-
① ECEF坐标系(蓝色)
地心地固坐标系(Earth-Centered, Earth-Fixed,简称ECEF)简称地心坐标系,是一种以地心为原点的地固坐标系,是一种笛卡儿坐标系。其原点为地球的质心,z轴延伸通过的北极(即与地球旋转轴重合),x轴延伸通过本初子午线(0度经度)和赤道(0度纬度)的交点,y 轴垂直于xOz平面(即东经90度与赤道的交点)构成右手坐标系。
引入ECEF坐标系的主要原因是为了在全球范围内提供统一的三维坐标参考系。这在卫星导航、地球观测和全球地理信息系统中非常重要。在计算机图形学中,三维笛卡尔坐标系更适合进行旋转、缩放等几何变换。
-
② BLH(橙色)
大地坐标系,其中B、L、H分别代表纬度、经度、高程。
两者都可用于表达空间中的某点,只不过一个是经纬度坐标(BLH),一个是笛卡尔坐标(XYZ),而两者是可以相互转换的。篇幅有限,推导部分可参看 👇
链接:大地经纬度坐标与地心地固坐标的的转换-CSDN博客
"局部"坐标系
-
① ENU(绿色)
简介:站心坐标系,指通过给定一个世界坐标点来创建一个局部坐标系。这个局部坐标系以X轴指向东,Y轴指向北,Z轴指向垂直向上的方向,它是一种局部空间直角坐标系。
用处:引入ENU,是为了方便局部测量和导航。它直观地反应了观测者周围的空间关系,便于理解和使用。
注意:站心天向(Z轴)与赤道面相交不一定会经过球心。
比较:相较于用ECEF表示物体位置,ENU可以精简数据体量,比如倾斜摄影数据会有一个xml文件记录建模原点,这个建模原点很多就是ENU坐标系原点在地球上的位置,而此时倾斜摄影的顶点数据就是在ENU坐标系进行描述的,极大的减少了顶点数据的体量,便于进行空间计算。
ENU与ECEF的转换同样可参看 👇
链接:地心地固坐标系(ECEF)与站心坐标系(ENU)的转换-CSDN博客
示例
通常我们会给模型数据一个模糊的大致位置,但放入场景中有时必须给模型定位、微调位置以适应场景。
当模型定位的位置不是我们想要的位置,可以通过修改该3dtiles的矩阵来改变它,如平移:
/**基于本地的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())
}

同理对模型做旋转操作:
/**基于本地的ENU坐标系的偏移,也就是垂直于地表向上为Z,东为X,北为Y* @param tileset Cesium3DTileset* @param rx X轴方向旋转角度(单位:度)* @param ry Y轴方向旋转角度(单位:度)* @param rz Z轴方向旋转角度(单位:度)*/
function rotation(tileset: Cesium3DTileset, rx: number, ry: number, rz: number) {const origin = tileset.boundingSphere.center;// 以该点建立ENU坐标系const m = Transforms.eastNorthUpToFixedFrame(origin);//旋转var mx = Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(rx));var my = Cesium.Matrix3.fromRotationY(Cesium.Math.toRadians(ry));var mz = Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(rz));var rotationX = Cesium.Matrix4.fromRotationTranslation(mx);var rotationY = Cesium.Matrix4.fromRotationTranslation(my);var rotationZ = Cesium.Matrix4.fromRotationTranslation(mz);// 旋转矩阵相乘Cesium.Matrix4.multiply(m, rotationX, m);Cesium.Matrix4.multiply(m, rotationY, m);Cesium.Matrix4.multiply(m, rotationZ, m);// 赋值给tilesettileset._root.transform = m;
}