Cesium中常用的一些数学计算(矩阵、向量)用法——矩阵

文章介绍了数字孪生中涉及的三维空间线性代数知识,包括矩阵乘向量的几何意义、Cesium库中Matrix4类的常用方法,如旋转、平移、缩放等操作,以及如何通过矩阵变换可视化局部坐标系。强调了线性代数的几何含义在解决问题中的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

刚好本人最近在研究数字孪生模拟相关的专题,涉及到三维空间线代向量、矩阵相关的计算,顺便重温了一下线代,在使用的过程中遇到问题的一些总结和实用技巧在下头阐述,相信这篇文章能够给短时间接触这些API的人一些启发。

在三维中可以把矩阵的列看出变换后的基向量:
通常而言,表示坐标系的i、j向量为(1,0)、(0,1),当我们把坐标轴逆时针旋转90°后,坐标系的基向量发生成了变化,i–>(0,1)、(-1, 0);

矩阵乘以一个向量有什么几何意义
矩阵乘向量就是把这个向量旋转,而且向量的大小也会改变,可以看出某空间下的向量到另一个空间的映射,其实就是向量空间的线性变换。

对于这一块理解比较模糊的同学推荐看一下国外的Blue1Brown视频->线性代数的本质

来看一下一些常见的用法:
特别的仿射四维矩阵
左上角是旋转矩阵、右上角是平移矩阵,旋转矩阵的最后一维是0,平移矩阵的最后一维是1。

总结一下Cesium 中Matrix4的一些常见用法:

Cesium.Matrix4,4x4矩阵,一般用于描述旋转加平移变换。
在计算机图形学中,Cesium.Matrix3,3x3矩阵,一般用于描述旋转变换。

矩阵和标量相乘

Cesium.Matrix4.multiplyByScalar (matrix, scalar, new Cesium.Matrix4())

矩阵的缩放倍数

  result[0] = matrix[0] * scalar;
  result[1] = matrix[1] * scalar;
  result[2] = matrix[2] * scalar;
  result[3] = matrix[3] * scalar;
  result[4] = matrix[4] * scalar;
  result[5] = matrix[5] * scalar;
  result[6] = matrix[6] * scalar;
  result[7] = matrix[7] * scalar;
  result[8] = matrix[8] * scalar;
  result[9] = matrix[9] * scalar;
  result[10] = matrix[10] * scalar;
  result[11] = matrix[11] * scalar;
  result[12] = matrix[12] * scalar;
  result[13] = matrix[13] * scalar;
  result[14] = matrix[14] * scalar;
  result[15] = matrix[15] * scalar;
  return result;
 * // m = [10.0, 11.0, 12.0, 13.0]
 * //     [14.0, 15.0, 16.0, 17.0]
 * //     [18.0, 19.0, 20.0, 21.0]
 * //     [22.0, 23.0, 24.0, 25.0]
 *
 * const a = Cesium.Matrix4.multiplyByScalar(m, -2, new Cesium.Matrix4());
 *
 * // m remains the same
 * // a = [-20.0, -22.0, -24.0, -26.0]
 * //     [-28.0, -30.0, -32.0, -34.0]
 * //     [-36.0, -38.0, -40.0, -42.0]
 * //     [-44.0, -46.0, -48.0, -50.0]
 */

矩阵和向量相乘部分

Cesium.Matrix4.multiplyByScale (matrix, scale, new Cesium.Matrix4())

对矩阵空间中的列向量进行成比例缩放,缩放了空间基向量的长度

  const scaleX = scale.x;
  const scaleY = scale.y;
  const scaleZ = scale.z;

  // Faster than Cartesian3.equals
  if (scaleX === 1.0 && scaleY === 1.0 && scaleZ === 1.0) {
    return Matrix4.clone(matrix, result);
  }

  result[0] = scaleX * matrix[0];
  result[1] = scaleX * matrix[1];
  result[2] = scaleX * matrix[2];
  result[3] = matrix[3];

  result[4] = scaleY * matrix[4];
  result[5] = scaleY * matrix[5];
  result[6] = scaleY * matrix[6];
  result[7] = matrix[7];

  result[8] = scaleZ * matrix[8];
  result[9] = scaleZ * matrix[9];
  result[10] = scaleZ * matrix[10];
  result[11] = matrix[11];

  result[12] = matrix[12];
  result[13] = matrix[13];
  result[14] = matrix[14];
  result[15] = matrix[15];

  return result;

Cesium.Matrix4.multiplyByTranslation (matrix, translation, new Cesium.Matrix4())

  const x = translation.x;
  const y = translation.y;
  const z = translation.z;

  const tx = x * matrix[0] + y * matrix[4] + z * matrix[8] + matrix[12];
  const ty = x * matrix[1] + y * matrix[5] + z * matrix[9] + matrix[13];
  const tz = x * matrix[2] + y * matrix[6] + z * matrix[10] + matrix[14];

  result[0] = matrix[0];
  result[1] = matrix[1];
  result[2] = matrix[2];
  result[3] = matrix[3];
  result[4] = matrix[4];
  result[5] = matrix[5];
  result[6] = matrix[6];
  result[7] = matrix[7];
  result[8] = matrix[8];
  result[9] = matrix[9];
  result[10] = matrix[10];
  result[11] = matrix[11];
  result[12] = tx;
  result[13] = ty;
  result[14] = tz;
  result[15] = matrix[15];

Cesium.Matrix4.multiplyByVector(matrix, cartesian, new Cesium.Cartesian3())

将一个3D向量进行线性变换,使其沿着矩阵的行方向进行缩放、旋转和平移
cartesian的w分量为0,表示这个结果是一个向量,而不是一个点,你可以用这个函数获取一个向量。

  // 源码
  const vX = cartesian.x;
  const vY = cartesian.y;
  const vZ = cartesian.z;
  const vW = cartesian.w;

  const x = matrix[0] * vX + matrix[4] * vY + matrix[8] * vZ + matrix[12] * vW;
  const y = matrix[1] * vX + matrix[5] * vY + matrix[9] * vZ + matrix[13] * vW;
  const z = matrix[2] * vX + matrix[6] * vY + matrix[10] * vZ + matrix[14] * vW;
  const w = matrix[3] * vX + matrix[7] * vY + matrix[11] * vZ + matrix[15] * vW;

  result.x = x;
  result.y = y;
  result.z = z;
  result.w = w;

将这个向量乘以矩阵,得到一个新的向量,即将原向量进行变换后得到新向量。

Cesium.Matrix4.multiplyByPoint(matrix, cartesian, new Cesium.Cartesian3())

cartesian的w分量为1,表示这个结果是一个点,不是一个向量,你可以用这个函数获取一个坐标点。

  // 源码
  const vX = cartesian.x;
  const vY = cartesian.y;
  const vZ = cartesian.z;

  const x = matrix[0] * vX + matrix[4] * vY + matrix[8] * vZ + matrix[12];
  const y = matrix[1] * vX + matrix[5] * vY + matrix[9] * vZ + matrix[13];
  const z = matrix[2] * vX + matrix[6] * vY + matrix[10] * vZ + matrix[14];

  result.x = x;
  result.y = y;
  result.z = z;

常用于,将模型从局部空间转换到世界空间,或者相机空间等。任何位移,旋转,缩放的组合都可以用一个4x4矩阵来表示,这样就可以简单地使用矩阵乘法来变换点和向量。
通俗一点,即将局部坐标系下的点坐标转化为世界坐标的点。以下点A通过矩阵变换:
Cesium.Matrix4.multiplyByPoint(局部坐标系, A在局部坐标的位置, A在世界坐标系下的点位置)

multiplyByPoint示意图

Cesium.Matrix4.multiplyByPointAsVector(matrix, new Cesium.Cartesian3(x, y, z), new Cesium.Cartesian3());

w分量为0,等效于调用 Matrix4.multiplyByVector,但是multiplyByPointAsVector方法返回的是一个Cartesian4对象,表示这个结果是一个向量,而不是一个点

  // 源码
  const vX = cartesian.x;
  const vY = cartesian.y;
  const vZ = cartesian.z;

  const x = matrix[0] * vX + matrix[4] * vY + matrix[8] * vZ;
  const y = matrix[1] * vX + matrix[5] * vY + matrix[9] * vZ;
  const z = matrix[2] * vX + matrix[6] * vY + matrix[10] * vZ;

  result.x = x;
  result.y = y;
  result.z = z;

Cesium.Matrix4.setTranslation (matrix, translation, new Cesium.Matrix4())

设置平移矩阵

  result[0] = matrix[0];
  result[1] = matrix[1];
  result[2] = matrix[2];
  result[3] = matrix[3];

  result[4] = matrix[4];
  result[5] = matrix[5];
  result[6] = matrix[6];
  result[7] = matrix[7];

  result[8] = matrix[8];
  result[9] = matrix[9];
  result[10] = matrix[10];
  result[11] = matrix[11];

  result[12] = translation.x;
  result[13] = translation.y;
  result[14] = translation.z;
  result[15] = matrix[15];

Cesium.Matrix4.setScale (matrix, scale, new Cesium.Matrix4())

设置矩阵的比例缩放。

  const existingScale = Matrix4.getScale(matrix, scaleScratch1);
  const scaleRatioX = scale.x / existingScale.x;
  const scaleRatioY = scale.y / existingScale.y;
  const scaleRatioZ = scale.z / existingScale.z;

  result[0] = matrix[0] * scaleRatioX;
  result[1] = matrix[1] * scaleRatioX;
  result[2] = matrix[2] * scaleRatioX;
  result[3] = matrix[3];

  result[4] = matrix[4] * scaleRatioY;
  result[5] = matrix[5] * scaleRatioY;
  result[6] = matrix[6] * scaleRatioY;
  result[7] = matrix[7];

  result[8] = matrix[8] * scaleRatioZ;
  result[9] = matrix[9] * scaleRatioZ;
  result[10] = matrix[10] * scaleRatioZ;
  result[11] = matrix[11];

  result[12] = matrix[12];
  result[13] = matrix[13];
  result[14] = matrix[14];
  result[15] = matrix[15];

矩阵和矩阵相乘部分

Cesium.Matrix4.multiplyByMatrix3 (matrix, rotation, new Cesium.Matrix4())

乘以一个转换矩阵(3x3旋转矩阵),优化 Matrix4.multiply(m,Matrix4.fromRotationTranslation(rotation),m)的计算,使得分配和运算量较少。

Cesium.Matrix4.multiply(left, right, new Cesium.Matrix4());

四维矩阵相乘。

A * B B左乘A, 可以把A看做坐标系,A的行向量是坐标系的基向量。把B看做列向量集合。 A * B就是把B中的列向量投影到A的所有基向量上。计算过程就是设b为B中的一个列向量。b点乘A中的所有基向量得到b在A的所有基向量上的投影,得到b向量在A坐标系中的表示方法。所以B做成A就是,将B中的所有向量转换为在A坐标空间中的表示方法。

Cesium.Matrix4.multiplyTransformation(left, right, new Cesium.Matrix4()) ;

当两个矩阵都是左上角旋转矩阵+右上角平移矩阵+底为[0,0,0,1]的这种形式的,用这种方法计算比一般用Matrix4.multiply 的速度更快。

	// 源码
  const column0Row0 = left0 * right0 + left4 * right1 + left8 * right2;
  const column0Row1 = left1 * right0 + left5 * right1 + left9 * right2;
  const column0Row2 = left2 * right0 + left6 * right1 + left10 * right2;

  const column1Row0 = left0 * right4 + left4 * right5 + left8 * right6;
  const column1Row1 = left1 * right4 + left5 * right5 + left9 * right6;
  const column1Row2 = left2 * right4 + left6 * right5 + left10 * right6;

  const column2Row0 = left0 * right8 + left4 * right9 + left8 * right10;
  const column2Row1 = left1 * right8 + left5 * right9 + left9 * right10;
  const column2Row2 = left2 * right8 + left6 * right9 + left10 * right10;

  const column3Row0 =
    left0 * right12 + left4 * right13 + left8 * right14 + left12;
  const column3Row1 =
    left1 * right12 + left5 * right13 + left9 * right14 + left13;
  const column3Row2 =
    left2 * right12 + left6 * right13 + left10 * right14 + left14;

  result[0] = column0Row0;
  result[1] = column0Row1;
  result[2] = column0Row2;
  result[3] = 0.0;
  result[4] = column1Row0;
  result[5] = column1Row1;
  result[6] = column1Row2;
  result[7] = 0.0;
  result[8] = column2Row0;
  result[9] = column2Row1;
  result[10] = column2Row2;
  result[11] = 0.0;
  result[12] = column3Row0;
  result[13] = column3Row1;
  result[14] = column3Row2;
  result[15] = 1.0;

矩阵的常见用法

Cesium.Matrix4.subtract (left, right, new Cesium.Matrix4())

矩阵求差

Cesium.Matrix4.add (left, right, new Cesium.Matrix4())

矩阵求和

Cesium.Matrix4.abs (matrix, new Cesium.Matrix4())

求矩阵绝对值

Cesium.Matrix4.clone (matrix, new Cesium.Matrix4())

克隆当前矩阵实例

Cesium.Matrix4.equals(left, right)

对比矩阵的每个元素值是否相等

逆矩阵部分

Cesium.Matrix4.inverse(matrix, new Cesium.Matrix4()) ;

逆矩阵是和这个矩阵相乘以后成为单位矩阵的矩阵。 当行列式不为0,矩阵才有逆矩阵。如果输入矩阵不可逆,则会返回一个全零并且平移为负的矩阵。计算矩阵的逆矩阵,从世界坐标系到局部坐标系的变换矩阵。

Cesium.Matrix4.inverseTransformation(matrix, new Cesium.Matrix4());

当其中左上3x3元素是旋转矩阵,第四列中的上三个元素是平移矩阵,最下面的一行是[0,0,0,1]时求矩阵的逆矩阵,用inverseTransformation这种方法比计算一般4x4矩阵使用 Matrix4.multiply的速度更快

 const matrix0 = matrix[0];
  const matrix1 = matrix[1];
  const matrix2 = matrix[2];
  const matrix4 = matrix[4];
  const matrix5 = matrix[5];
  const matrix6 = matrix[6];
  const matrix8 = matrix[8];
  const matrix9 = matrix[9];
  const matrix10 = matrix[10];

  const vX = matrix[12];
  const vY = matrix[13];
  const vZ = matrix[14];

  const x = -matrix0 * vX - matrix1 * vY - matrix2 * vZ;
  const y = -matrix4 * vX - matrix5 * vY - matrix6 * vZ;
  const z = -matrix8 * vX - matrix9 * vY - matrix10 * vZ;

  result[0] = matrix0;
  result[1] = matrix4;
  result[2] = matrix8;
  result[3] = 0.0;
  result[4] = matrix1;
  result[5] = matrix5;
  result[6] = matrix9;
  result[7] = 0.0;
  result[8] = matrix2;
  result[9] = matrix6;
  result[10] = matrix10;
  result[11] = 0.0;
  result[12] = x;
  result[13] = y;
  result[14] = z;
  result[15] = 1.0;
  return result;

inverse方法仅计算输入矩阵的逆矩阵,而inverseTransformation方法计算输入矩阵的逆矩阵和平移向量的相反数。在不同的场景下,选择不同的方法可以更方便地实现所需的功能。

Cesium.Matrix4.transpose (matrix, new Cesium.Matrix4())

矩阵的转置,将矩阵的行列互换得到的新矩阵称为转置矩阵,转置矩阵的行列式不变。

//returns transpose of a Matrix4
// m = [10.0, 11.0, 12.0, 13.0]
//     [14.0, 15.0, 16.0, 17.0]
//     [18.0, 19.0, 20.0, 21.0]
//     [22.0, 23.0, 24.0, 25.0]

var a = Cesium.Matrix4.transpose(m, new Cesium.Matrix4());

// m remains the same
// a = [10.0, 14.0, 18.0, 22.0]
//     [11.0, 15.0, 19.0, 23.0]
//     [12.0, 16.0, 20.0, 24.0]
//     [13.0, 17.0, 21.0, 25.0]

转置的作用相当于对矩阵进行旋转,使得行变成列,列变成列。

Cesium.Matrix4.inverseTranspose(matrix,result)

求逆转置

// 源码
return Matrix4.inverse(
    Matrix4.transpose(matrix, scratchTransposeMatrix),
    result
  );

在模型视图变换时,顶点乘模型视图变换矩阵,而顶点对应的顶点法线向量(或其他的法线向量)则要乘模型视图矩阵的逆转置矩阵。
https://blog.51cto.com/u_15127690/4750355

Cesium.Matrix4.negate (matrix, result)

对矩阵每一个元素取相反数

// 实现逻辑
//create a new Matrix4 instance which is a negation of a Matrix4
// m = [10.0, 11.0, 12.0, 13.0]
//     [14.0, 15.0, 16.0, 17.0]
//     [18.0, 19.0, 20.0, 21.0]
//     [22.0, 23.0, 24.0, 25.0]

var a = Cesium.Matrix4.negate(m, new Cesium.Matrix4());

// m remains the same
// a = [-10.0, -11.0, -12.0, -13.0]
//     [-14.0, -15.0, -16.0, -17.0]
//     [-18.0, -19.0, -20.0, -21.0]
//     [-22.0, -23.0, -24.0, -25.0]

用Matrix4类的API生成四维矩阵

Cesium.Matrix4.fromTranslation(translation, new Cesium.Matrix4());

if (!defined(result)) {
    return new Matrix4(
      1.0,
      0.0,
      0.0,
      translation.x,
      0.0,
      1.0,
      0.0,
      translation.y,
      0.0,
      0.0,
      1.0,
      translation.z,
      0.0,
      0.0,
      0.0,
      1.0
    );
  }

Cesium.Matrix4.getTranslation(matrix, new Cesium.Cartesian3())

从矩阵获取平移向量, 用于获取一个4x4矩阵的平移部分,返回的是矩阵的平移部分,也就是矩阵的第4列的前3个元素。这个结果是一个Cartesian3对象,表示3D空间中的一个点,这个点的坐标就是矩阵所表示的变换中的平移部分。
通常,一个4x4的矩阵可以分解为三个部分:平移部分、旋转部分和缩放部分。
同理, Cesium.Matrix4.getScale(matrix, new Cesium.Cartesian3())Cesium.Matrix4.getRotation(matrix, new Cesium. Matrix3()) ,分别用于获取缩放比例和旋转部分。

Cesium.Matrix4.fromRotationTranslation(rotation, translation, result)

Cesium.Matrix4.fromScale(scale, result)

Cesium.Matrix4.fromTranslationQuaternionRotationScale(translation, rotation, scale, result)

Cesium.Matrix4.fromTranslationRotationScale(translationRotationScale, result)

如何可视化世界矩阵的局部坐标系?

  // 局部坐标系的局部坐标轴 ---核心代码
  addlocalAxis(parentMatrix, color) {
    let parentPosition = Cesium.Matrix4.getTranslation(parentMatrix, new Cesium.Cartesian3());
    this.addEntity(parentPosition, Cesium.Color.RED);
    // 求矩阵的x轴基向量
    // Cesium.Cartesian3.UNIT_X <=> new Cesium.Cartesian3(1, 0, 0)
    const localAxisX = Cesium.Matrix4.multiplyByPointAsVector(parentMatrix, new Cesium.Cartesian3(1, 0, 0), new Cesium.Cartesian3());
    // 求矩阵的y轴基向量
    // Cesium.Cartesian3.UNIT_Y <=> new Cesium.Cartesian3(0, 1, 0)
    const localAxisY = Cesium.Matrix4.multiplyByPointAsVector(parentMatrix, new Cesium.Cartesian3(0, 1, 0), new Cesium.Cartesian3()); 
    // 求矩阵的z轴基向量
    // Cesium.Cartesian3.UNIT_Z <=> new Cesium.Cartesian3(0, 0, 1)
    const localAxisZ = Cesium.Matrix4.multiplyByPointAsVector(parentMatrix, new Cesium.Cartesian3(0, 0, 1), new Cesium.Cartesian3());
 
    this.addPolyline(parentPosition, localAxisX, color);
    this.addPolyline(parentPosition, localAxisY, color);
    this.addPolyline(parentPosition, localAxisZ, color);
  }

  addPolyline(position, vector, color) {
    return this.viewer.entities.add({
      polyline: {
        width: 10,
        positions: [position, Cesium.Cartesian3.add(position, vector, new Cesium.Cartesian3())],
        material: new Cesium.PolylineArrowMaterialProperty(
          color
        ),
      },
    });
  }
  // 添加entity点
  addEntity(position, color) {
    return this.viewer.entities.add({
      position: position,
      point: {
        color: color,
        pixelSize: 10,
      },
    })
  }

线性代数,可能我们只学了它的计算,但其实更重要的是它的几何含义。计算只是解决问题的工具,而明白其几何含义帮助我们知道解决什么问题要用什么工具,以及如何解读最终结果的含义,学无止境,继续加油吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值