关于cesium的primitive的modelMatrix的应用

在阅读这篇文章之前,建议先了解什么是仿射变换矩阵

序. 仿射变换矩阵

1、核心概念:什么是仿射变换?

仿射变换是一类 "线性变换 + 平移" 的组合变换,具体包括:

  • 线性变换:旋转(Rotation)、缩放(Scaling)、剪切(Shearing);
  • 平移变换:将图形沿某个方向移动(Translation)。

例如:把一张图片旋转 30° 后再向右移动 100 像素,就是一次仿射变换。

2、仿射变换矩阵的结构(以 3D 为例,Cesium 中最常用)

在 3D 空间中,仿射变换矩阵是一个4×4 的矩阵(齐次坐标矩阵),结构如下(列优先存储,Cesium 默认格式):

arduino 复制代码
[
  a, d, g, j,  // 第1列:X轴线性变换参数
  b, e, h, k,  // 第2列:Y轴线性变换参数
  c, f, i, l,  // 第3列:Z轴线性变换参数
  0, 0, 0, 1   // 第4列:齐次坐标固定项(确保矩阵乘法合法性)
]
  • 前 3×3 子矩阵(a-i) :负责线性变换(旋转、缩放、剪切);

    这里详细分解一下旋转和缩放:

    缩放变换:由前 3×3 子矩阵的 "对角线元素" 控制

arduino 复制代码
  Sx,  0,  0,  // X轴缩放因子(m00)
   0, Sy,  0,  // Y轴缩放因子(m05)
   0,  0, Sz   // Z轴缩放因子(m10)

旋转变换:由前 3×3 子矩阵的 "所有元素共同控制"(正交矩阵)

arduino 复制代码
// 绕 Z 轴旋转(航向角 Heading,对应 yaw),旋转角度为`θ`,矩阵元素:
[
  cosθ, -sinθ, 0,  // 第0列:m00=cosθ, m01=-sinθ, m02=0
  sinθ,  cosθ, 0,  // 第1列:m04=sinθ, m05=cosθ, m06=0
  0,      0,   1   // 第2列:m08=0, m09=0, m10=1
]

// 绕 X 轴旋转(翻滚角 Roll),旋转角度为`φ`,矩阵元素:
[
  1,   0,    0,   // 第0列:m00=1, m01=0, m02=0
  0, cosφ, -sinφ, // 第1列:m04=0, m05=cosφ, m06=-sinφ
  0, sinφ,  cosφ  // 第2列:m08=0, m09=sinφ, m10=cosφ
]

// 绕 Y 轴旋转(俯仰角 Pitch),旋转角度为`ψ`,矩阵元素:
[
  cosψ, 0, sinψ,  // 第0列:m00=cosψ, m01=0, m02=sinψ
  0,    1,    0,  // 第1列:m04=0, m05=1, m06=0
  -sinψ,0, cosψ   // 第2列:m08=-sinψ, m09=0, m10=cosψ
]
  • 第 4 列前 3 个元素(j, k, l) :负责平移变换(X、Y、Z 方向的平移量);

  • 最后一行(0,0,0,1) :齐次坐标的 "标识行",确保矩阵乘法能正确融合线性变换和平移。

3、为什么用 4×4 矩阵?(齐次坐标的作用)

线性变换(如旋转、缩放)可以用 3×3 矩阵表示,但平移无法用 3×3 矩阵单独实现(因为线性变换的原点映射后仍是原点,而平移会改变原点位置)。

通过引入 "齐次坐标"(3D 点用 4 个分量(x, y, z, 1)表示),4×4 矩阵可以统一处理线性变换和平移

  • 对一个点(x, y, z, 1)应用仿射变换矩阵后,结果为:x' = a*x + d*y + g*z + j``y' = b*x + e*y + h*z + k``z' = c*x + f*y + i*z + l(同时满足线性变换和平移的组合效果)

4、Cesium 中的仿射变换矩阵实例

Cesium 中几乎所有模型 / 物体的位置和姿态调整,都依赖 4×4 仿射变换矩阵(如modelMatrix):

a. 纯平移矩阵

go 复制代码
```
// 沿X轴平移10,Y轴平移20,Z轴平移30
const translateMatrix = Cesium.Matrix4.fromTranslation(new Cesium.Cartesian3(10, 20, 30));
```

矩阵结构为:

```
[1,0,0,10,  
 0,1,0,20,  
 0,0,1,30,  
 0,0,0,1]
```

b. 旋转 + 平移矩阵

go 复制代码
```
// 以center为原点,应用HPR旋转,生成包含旋转和平移的仿射矩阵
const hprMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(center, hpr);
```

其中前 3×3 子矩阵是旋转(线性变换),第 4 列是 center 的坐标(平移)。

5、核心作用:统一组合多个变换

仿射变换矩阵的最大价值是支持矩阵乘法组合多个变换。例如:先缩放→再旋转→最后平移,只需将三个变换矩阵按顺序相乘(注意顺序:从右到左应用):

arduino 复制代码
// Cesium中矩阵的乘法为右乘!即
//matA * matB,写作
 Cesium.Matrix4.multiply(matB, matA, new Cesium.Matrix4())
 
// 缩放矩阵 → 旋转矩阵 → 平移矩阵
const temp = Cesium.Matrix4.multiply(rotateMatrix, scaleMatrix)
const finalMatrix = Cesium.Matrix4.multiply(translateMatrix, temp);

一.平移

1. 世界坐标平移(原点目标点皆为世界坐标)

思路,获取向量差,将向量差转为平移矩阵

ini 复制代码
// origin 为原点坐标,target为目标坐标
const sub = Cesium.Cartesian3.subtract(target, origin, new Cesium.Cartesian3())
primitive.modelMatrix = Cesium.Matrix4.fromTranslation(sub)

2. 局部坐标平移(如模型向南平移200米)

思路,计算出目标点世界坐标,获取向量差,将向量差转为平移矩阵

ini 复制代码
// origin 为原点坐标,local_translation为平移向量
const originMatrix4 = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
// 平移向量的构建为:new Cesium.Cartesian3(东,北,上)
const local_translation = new Cesium.Cartesian3(300, 200, 100); 
const result = new Cesium.Cartesian3(0,0,0);
// 转换矩阵左乘局部平移向量,结果存储在 result 中,结果是世界坐标下的平移终点向量,即目标点的世界坐标
Cesium.Matrix4.multiplyByPoint(frompoint_to_world_matrix, local_translation, result); 
// 获取世界坐标平移向量
const sub = Cesium.Cartesian3.subtract(result, origin, new Cesium.Cartesian3())
// 应用平移
primitive.modelMatrix = Cesium.Matrix4.fromTranslation(sub)

二.旋转

思路,因为Cesium的Primitive旋转中心为坐标系中心, 所以必须先来到坐标系中心,再做操作,最后返回原点

arduino 复制代码
    // 获取从坐标系中心到原点的位移矩阵
    const backMat = Cesium.Matrix4.fromTranslation(origin)
    // 将向量取反,用于获取相反的位移矩阵,用于前往坐标系中心
    const toVec = new Cesium.Cartesian3(-origin.x, -origin.y, -origin.z)
    const toMat = Cesium.Matrix4.fromTranslation(toVec)
    
    // 获取旋转参数
    const hpr = new Cesium.HeadingPitchRoll(heading,pitch,roll)
    // 因为目前模型在坐标系中心,第一个参数要写Cesium.Cartesian3.ZERO
    const rotateMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(Cesium.Cartesian3.ZERO, hpr, Cesium.Ellipsoid.WGS84)
    
    // 获取完整矩阵,根据顺序应当 原点→中心→变换→原点,所以应当是 toMat * rotateMatrix * backMat
    // 注意Cesium矩阵相乘是右乘!!!!!!
    const temp = Cesium.Matrix4.multiply(rotateMatrix, toMat, new Cesium.Matrix4())
    const res = Cesium.Matrix4.multiply(backMat, temp, new Cesium.Matrix4())
    Primitive.modelMatrix = res

三.缩放

思路,依旧必须先来到坐标系中心,再做缩放,最后返回原点

arduino 复制代码
// 省略来回位移的代码,rotateMatrix替换成scaleMat
// 获取缩放参数
const scaleVec = new Cesium.Cartesian3(0.5, 0.5, 0.5)
const scaleMat = Cesium.Matrix4.fromScale(scaleVec, new Cesium.Matrix4())
相关推荐
消失的旧时光-19433 小时前
Kotlinx.serialization 对多态对象(sealed class )支持更好用
java·服务器·前端
少卿3 小时前
React Compiler 完全指南:自动化性能优化的未来
前端·javascript
广州华水科技3 小时前
水库变形监测推荐:2025年单北斗GNSS变形监测系统TOP5,助力基础设施安全
前端
广州华水科技3 小时前
北斗GNSS变形监测一体机在基础设施安全中的应用与优势
前端
七淮3 小时前
umi4暗黑模式设置
前端
8***B3 小时前
前端路由权限控制,动态路由生成
前端
爱隐身的官人3 小时前
beef-xss hook.js访问失败500错误
javascript·xss
军军3604 小时前
从图片到点阵:用JavaScript重现复古数码点阵艺术图
前端·javascript
znhy@1234 小时前
Vue基础知识(一)
前端·javascript·vue.js
terminal0074 小时前
浅谈useRef的使用和渲染机制
前端·react.js·面试