当前位置:   article > 正文

Cesium加载3Dtiles模型的平移和旋转_3dtiles先旋转再平移示例

3dtiles先旋转再平移示例

Cesium加载3Dtiles模型的平移和旋转

一、基础说明

  • Cesium中点由Cartesian3 表示,类似POINT3D点,其形式为包含 (x, y, z)的一维向量

  • Cesium中默认坐标系以地心为原点,所有未指明的Cartesian3 都基于此坐标系

  • 我们需要进行的平移与旋转基于模型所在点为原点,正东为X,正北为Y的局部坐标系

  • 使用函数Cesium.Transforms.eastNorthUpToFixedFrame(origin) 获取上述局部坐标系中点转换为世界坐标系中的位置的变换矩阵,P世界=M变换矩阵P局部

  • 3Dtiles模型的变换由一个4维矩阵完成,通过不断左乘变换矩阵可以完成一系列的复杂变换

二、模型平移与调整透明度

使用vue2,参考官方调整模型高度的案例。

  1. 假设模型的原点在世界坐标系下坐标为(xorigin, yorigin, zorigin)

  2. 获取坐标系变换矩阵

  3. 假设模型需要平移(x, y, z)量,先计算局部坐标系中点(x, y, z)在世界坐标下的位置(xworld, yworld, zworld)

    const offset = Cesium.Matrix4.multiplyByPoint(m, tempTranslation, new Cesium.Cartesian3(0, 0, 0));

  4. 使用(xworld, yworld, zworld)-(xorigin, yorigin, zorigin)获得世界坐标系中平移向量

  5. 使用Cesium.Matrix4.fromTranslation(translation); 获取3Dtiles平移变换矩阵

  6. 透明度调整tileset.style = new Cesium.Cesium3DTileStyle({color: "color('rgba(255,255,255," + opacity + ")')"});

  1. transferModel(tileset, _tx, _ty, _tz,_opacity) {
  2. if(!this.checkModelLoad()){
  3. return
  4. }
  5. let tx = _tx ? _tx : 0;
  6. let ty = _ty ? _ty : 0;
  7. let tz = _tz ? _tz : 0;
  8. let opacity = _opacity ? _opacity/100 : 1
  9. const origin = tileset.boundingSphere.center;
  10. const m = Cesium.Transforms.eastNorthUpToFixedFrame(origin);//获取到以模型中心为原点,Z轴垂直地表的局部坐标系,以矩阵表示,此矩阵为将局部坐标系变换到世界坐标系的变换矩阵
  11. //平移
  12. const tempTranslation = new Cesium.Cartesian3(tx, ty, tz);//平移向量
  13. const offset = Cesium.Matrix4.multiplyByPoint(m, tempTranslation, new Cesium.Cartesian3(0, 0, 0));//局部坐标中(tx,ty,tz)在世界坐标系中位置
  14. const translation = Cesium.Cartesian3.subtract(offset, origin, new Cesium.Cartesian3());//终点世界坐标减去原点世界坐标得到世界坐标系下平移向量
  15. tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
  16. //透明度
  17. tileset.style = new Cesium.Cesium3DTileStyle({
  18. color: "color('rgba(255,255,255," + opacity + ")')",
  19. });
  20. },

三、模型旋转

模型旋转时,由于Cesium默认绕轴旋转是绕世界坐标系轴旋转,因此我们需要做如下操作完成绕局部坐标轴旋转

  1. 将模型原点平移回世界坐标系原点(地心),矩阵记为T1

  2. 将局部坐标Z轴调整到与世界坐标Z轴重合,矩阵记为R1

  3. 将模型绕世界坐标系的某个轴旋转(真正进行旋转的矩阵),此处以Z轴为例,矩阵记为R

  4. 将局部坐标Z轴旋转回原来的指向,矩阵记为R2

  5. 平移回初始位置,矩阵记为T2

最终模型旋转的矩阵为T2R2RR1T1

将旋转开始前模型的初始变换矩阵左乘此矩阵即可完成旋转变换

(一)回到地心

  1. const origin = tileset.boundingSphere.center;
  2. console.log("初始世界坐标", origin)
  3. const localToWorldMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);//获取到以模型中心为原点,Z轴垂直地表的局部坐标系的变换矩阵,左乘此矩阵可以将局部坐标变换为世界坐标
  4. const originMatrix = tileset.modelMatrix//贴地变换矩阵或者初始变换矩阵M0
  5. console.log("当前坐标变换矩阵", localToWorldMatrix)
  6. const backToEarthCenter = new Cesium.Cartesian3(-origin.x, -origin.y, -origin.z)//回到地心位移量
  7. let backToEarthCenterMatrix = Cesium.Matrix4.fromTranslation(backToEarthCenter);//回到地心变换矩阵
  8. Cesium.Matrix4.multiply(backToEarthCenterMatrix, originMatrix, backToEarthCenterMatrix)//贴地变换矩阵左乘回到地心矩阵 T1M0
  9. console.log("回到地心变换矩阵", backToEarthCenterMatrix)

(二)局部Z轴校正

坐标轴校正示意图

先将红线表示的向量(z'轴)绕Z轴顺时针旋转Φ度,再绕Y轴顺时针旋转θ度

1、通过一维向量表示坐标轴,通过左乘变换矩阵完成局部坐标轴在世界坐标系下的表示

  1. //旋转模型使得Z轴与世界坐标Z轴重合
  2. let arrowX = new Cesium.Cartesian3(1, 0, 0)
  3. let arrowY = new Cesium.Cartesian3(0, 1, 0)
  4. let arrowZ = new Cesium.Cartesian3(0, 0, 1)
  5. let localArrowX = Cesium.Matrix4.multiplyByPoint(localToWorldMatrix, new Cesium.Cartesian3(1, 0, 0), new Cesium.Cartesian3)
  6. let localArrowY = Cesium.Matrix4.multiplyByPoint(localToWorldMatrix, new Cesium.Cartesian3(0, 1, 0), new Cesium.Cartesian3)
  7. 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轴旋转需要做类似的处理

  1. let angleToXZ = Cesium.Cartesian3.angleBetween(arrowX, new Cesium.Cartesian3(localArrowZ.x, localArrowZ.y, 0))//局部Z轴在世界坐标系XY平面上投影到X轴角度,即绕Z顺时针旋转这个角度可以到XZ平面上
  2. let angleToZ = Cesium.Cartesian3.angleBetween(localArrowX, arrowZ)//然后绕Y轴顺时针旋转此角度可使得Z轴与世界坐标系Z轴重合
  3. const rotationAngleToXZ = Cesium.Matrix3.fromRotationZ(-angleToXZ);//此函数正方向为逆时针
  4. const rotationAngleToZ = Cesium.Matrix3.fromRotationY(-angleToZ);
  5. let rotationAngleToZMatrix = Cesium.Matrix3.multiply(rotationAngleToZ, rotationAngleToXZ, new Cesium.Matrix3)
  6. rotationAngleToZMatrix = Cesium.Matrix4.fromRotationTranslation(rotationAngleToZMatrix)
  7. Cesium.Matrix4.multiply(rotationAngleToZMatrix, backToEarthCenterMatrix, rotationAngleToZMatrix)//局部轴校正R1T1M0

(三)真实旋转矩阵

此处完成真正需要进行的旋转,此处以绕Z轴旋转为例

  1. // 绕Z轴旋转
  2. console.log(rz - this.originRz)
  3. const rotationZ = Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(rz - this.originRz)); // 绕Z轴旋转变换矩阵R
  4. let rotationMatrix = Cesium.Matrix4.fromRotationTranslation(rotationZ)
  5. 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)

  1. // 旋转模型回到原本朝向
  2. const rotationAngleLeaveXZ = Cesium.Matrix3.fromRotationZ(angleToXZ);
  3. const rotationAngleLeaveZ = Cesium.Matrix3.fromRotationY(angleToZ);
  4. let rotationAngleLeaveZMatrix = Cesium.Matrix3.multiply(rotationAngleLeaveXZ, rotationAngleLeaveZ, new Cesium.Matrix3)
  5. rotationAngleLeaveZMatrix = Cesium.Matrix4.fromRotationTranslation(rotationAngleLeaveZMatrix)// 局部Z轴回到原本方向
  6. Cesium.Matrix4.multiply(rotationAngleLeaveZMatrix, rotationMatrix, rotationAngleLeaveZMatrix)//R2RR1T1M0

(五)回到原本位置

位移量即为模型原本的世界坐标,直接可获得变换矩阵

  1. //回到原来位置
  2. const backToOriginMatrix = Cesium.Matrix4.fromTranslation(origin);//从地心回归原位T2

(六)绕Z轴旋转完整代码

  1. rotationModel(tileset,rz) {
  2. if(!this.checkModelLoad()){
  3. return
  4. }
  5. const origin = tileset.boundingSphere.center;
  6. console.log("初始世界坐标", origin)
  7. const localToWorldMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);//获取到以模型中心为原点,Z轴垂直地表的局部坐标系的变换矩阵,左乘此矩阵可以将局部坐标变换为世界坐标
  8. const originMatrix = tileset.modelMatrix//贴地变换矩阵或者初始变换矩阵M0
  9. console.log("当前坐标变换矩阵", localToWorldMatrix)
  10. const backToEarthCenter = new Cesium.Cartesian3(-origin.x, -origin.y, -origin.z)//回到地心位移量
  11. let backToEarthCenterMatrix = Cesium.Matrix4.fromTranslation(backToEarthCenter);//回到地心变换矩阵
  12. Cesium.Matrix4.multiply(backToEarthCenterMatrix, originMatrix, backToEarthCenterMatrix)//贴地变换矩阵左乘回到地心矩阵 T1M0
  13. console.log("回到地心变换矩阵", backToEarthCenterMatrix)
  14. // 旋转
  15. //旋转模型使得Z轴与世界坐标Z轴重合
  16. let arrowX = new Cesium.Cartesian3(1, 0, 0)
  17. let arrowY = new Cesium.Cartesian3(0, 1, 0)
  18. let arrowZ = new Cesium.Cartesian3(0, 0, 1)
  19. let localArrowX = Cesium.Matrix4.multiplyByPoint(localToWorldMatrix, new Cesium.Cartesian3(1, 0, 0), new Cesium.Cartesian3)
  20. let localArrowY = Cesium.Matrix4.multiplyByPoint(localToWorldMatrix, new Cesium.Cartesian3(0, 1, 0), new Cesium.Cartesian3)
  21. let localArrowZ = Cesium.Matrix4.multiplyByPoint(localToWorldMatrix, new Cesium.Cartesian3(0, 0, 1), new Cesium.Cartesian3)
  22. let angleToXZ = Cesium.Cartesian3.angleBetween(arrowX, new Cesium.Cartesian3(localArrowZ.x, localArrowZ.y, 0))//局部Z轴在世界坐标系XY平面上投影到X轴角度,即绕Z顺时针旋转这个角度可以到XZ平面上
  23. let angleToZ = Cesium.Cartesian3.angleBetween(localArrowX, arrowZ)//然后绕Y轴顺时针旋转此角度可使得Z轴与世界坐标系Z轴重合
  24. const rotationAngleToXZ = Cesium.Matrix3.fromRotationZ(-angleToXZ);//此函数正方向为逆时针
  25. const rotationAngleToZ = Cesium.Matrix3.fromRotationY(-angleToZ);
  26. let rotationAngleToZMatrix = Cesium.Matrix3.multiply(rotationAngleToZ, rotationAngleToXZ, new Cesium.Matrix3)
  27. rotationAngleToZMatrix = Cesium.Matrix4.fromRotationTranslation(rotationAngleToZMatrix)
  28. Cesium.Matrix4.multiply(rotationAngleToZMatrix, backToEarthCenterMatrix, rotationAngleToZMatrix)//局部轴校正R1T1M0
  29. // 绕Z轴旋转
  30. console.log(rz - this.originRz)
  31. const rotationZ = Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(rz - this.originRz)); // 绕Z轴旋转变换矩阵R
  32. let rotationMatrix = Cesium.Matrix4.fromRotationTranslation(rotationZ)
  33. Cesium.Matrix4.multiply(rotationMatrix, rotationAngleToZMatrix, rotationMatrix)//RR1T1M0
  34. // 旋转模型回到原本朝向
  35. const rotationAngleLeaveXZ = Cesium.Matrix3.fromRotationZ(angleToXZ);
  36. const rotationAngleLeaveZ = Cesium.Matrix3.fromRotationY(angleToZ);
  37. let rotationAngleLeaveZMatrix = Cesium.Matrix3.multiply(rotationAngleLeaveXZ, rotationAngleLeaveZ, new Cesium.Matrix3)
  38. rotationAngleLeaveZMatrix = Cesium.Matrix4.fromRotationTranslation(rotationAngleLeaveZMatrix)// 局部Z轴回到原本方向
  39. Cesium.Matrix4.multiply(rotationAngleLeaveZMatrix, rotationMatrix, rotationAngleLeaveZMatrix)//R2RR1T1M0
  40. //回到原来位置
  41. const backToOriginMatrix = Cesium.Matrix4.fromTranslation(origin);//从地心回归原位T2
  42. // 应用变换矩阵
  43. const lastMatrix = Cesium.Matrix4.multiply(backToOriginMatrix, rotationAngleLeaveZMatrix, new Cesium.Matrix4)//最终矩阵T2R2RR1T1M0
  44. tileset.modelMatrix = lastMatrix
  45. console.log("结束世界坐标", tileset.boundingSphere.center)
  46. this.originRz = rz
  47. },

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/573671
推荐阅读
相关标签
  

闽ICP备14008679号