Three.js矩阵与向量实战:从基础变换到逆矩阵应用
本文深入解析Three.js中矩阵与向量的核心用法。通过实例演示如何手动构建平移、旋转和缩放矩阵,利用applyMatrix实现复杂层级变换。详细讲解矩阵乘法的顺序敏感性、compose组合矩阵方法,以及利用inverse求解逆矩阵以撤销变换的操作,帮助开发者摆脱对自动更新的依赖,精准控制3D场景中的几何体状态。
Three.js矩阵与向量实战:从基础变换到逆矩阵应用
前言
提及矩阵,常令人联想到晦涩难懂的线性代数与离散数学。然而,在图形开发领域,矩阵是驱动每一次运动与变换的核心基石,正如鱼离不开水,它也是无法回避的关键话题。
值得庆幸的是,Three.js已将众多复杂的矩阵运算封装为高层级方法,并提供了卓越的数学库。开发者无需深究底层计算原理(HowToCalc),只需掌握调用方式(HowToUse),即可获取绝大多数所需功能。
本文将聚焦于如何在无需理解内部结构的前提下,在Three.js中高效使用矩阵与向量。
实例演示:手动构建矩阵变换
从一个例子开始
在阐述枯燥概念之前,先通过一个小案例简要说明使用矩阵方法的必要性。

这是我们最终要完成的效果。
首先,我们要创建三个几何体:
var box_geometry = new THREE.BoxGeometry();
var sphere_geometry = new THREE.SphereGeometry(0.5, 32, 32);
var cylinder_geometry = new THREE.CylinderGeometry(0.1, 0.1, 0.5);
var material = new THREE.MeshLambertMaterial({color: new THREE.Color(0.9, 0.55, 0.4)})
这三个几何体分别是盒子、球和圆柱体。
然后去创建三个网格,并将它们置入场景。
var box = new THREE.Mesh(box_geometry, material);
var sphere = new THREE.Mesh(sphere_geometry, material);
sphere.position.y += 1;
var cylinder = new THREE.Mesh(cylinder_geometry, material);
cylinder.position.y += 1.75;
scene.add(box);
scene.add(sphere);
scene.add(cylinder);
这段代码将生成如下场景:

虽然视觉效果并不完美,但作为示例已足够。现在,我希望将这组物体的尺寸减半。常规做法是将物体的scale属性减半,代码如下:
box.scale.multiplyScalar(0.5);
sphere.scale.multiplyScalar(0.5);
cylinder.scale.multiplyScalar(0.5);

结果与预期存在偏差。我的初衷是对整组物体进行统一缩放,保持它们之间的相对位置不变。为解决此问题,需根据其他对象的缩放重新计算每个对象的位置。但这并非难题,Three.js提供了一种优雅方案:定义一个空对象,将三个子对象添加至其中,并将缩放比例应用于父对象。
var pile = new THREE.Object3D();
pile.scale.multiplyScalar(0.5);
pile.add(box);
pile.add(sphere);
pile.add(cylinder);
scene.add(pile);

接下来我们做一点更有趣的事。
我将在这个物体组合里添加旋转,让我们尝试围绕球体表面旋转的那个圆柱体,就像他将要滑落一样。

它变成了这样,很明显,这不是我想要的东西。我们在这里有两个做法可供选择:第一,通过数学计算算出圆柱相对于球体的正确位置;第二,创建另一个Object3D,将圆柱和球放进去并旋转。这听上去挺复杂的,而且也很不酷。
所以,我们可以尝试自己去计算矩阵。
首先,我需要将属性maxtrixAutoUpdate设置为false,然后我就不能再通过position,scale和rotation去修改矩阵。
box.matrixAutoUpdate = false;
sphere.matrixAutoUpdate = false;
cylinder.matrixAutoUpdate = false;
现在,我将用applyMatrix方法来解决这个问题。具体做法是:为每个对象创建一个Matrix4,然后我们将矩阵与该矩阵相乘以应用后续操作。
var sphere_matrix = new THREE.Matrix4().makeTranslation(0.0, 1.0, 0.0);
sphere.applyMatrix(sphere_matrix);
var cylinder_matrix = sphere_matrix.clone(); cylinder_matrix.multiply(new THREE.Matrix4().makeTranslation(0.0, 0.75, 0.0));
cylinder.applyMatrix(cylinder_matrix);
这几步下来,可以让我们解锁很多知识,来看看这里发生了什么。
首先,我们把盒子单独留下,因为它不需要动。
接着,我创建了一个平移矩阵并把它应用到了球对象上。
最后,在圆柱体上,我clone了球的矩阵信息,并在此基础上又创建了一个新的平移矩阵,圆柱体将移动1.75。

理解了上面几步,你就会知道最后一步该做什么了。
只需要一行代码,作用在球上:
sphere_matrix.multiply(new THREE.Matrix4().makeRotationZ(-Math.PI * 0.25));

达成了想要的效果,很酷。
核心方法解析:平移、旋转与缩放
示例中用到的方法
在上面的示例中,我将球和圆柱体分别沿y轴移动了一定的距离,并使用了makeTranslation这个方法。这个方法的作用是创建了一个平移矩阵。紧接着,我又使用到了applyMatrix的方法。这个方法的作用是把平移矩阵作用在球和圆柱体上。
那么什么是平移矩阵?它又是如何完成一次平移呢?
Three.js中最常见的一种4x4的矩阵,被称为变换矩阵,它所表示的变换类型包括平移、旋转和缩放。
用一个简单的数学题来说明变换矩阵:

有一个起始点,用向量来表示即为Vector3(20,20,0);现在,我要把它移动到另一个位置,Vector3(30,60,0)。
接下来,我设置一个平移矩阵,来表示向量依照什么方式去移动。
t = |1 0 0 10|
|0 1 0 40|
|0 0 1 0 |
|0 0 0 1 |
最后,用起始的向量去乘以变换矩阵的向量。
|20| |1 0 0 10| |30|
|20| x |0 1 0 40| = |60|
|0 | |0 0 1 0 | |0 |
|1 | |0 0 0 1 | |1 |
变换公式如下:
transformedVector = vector * transformationMatrix
最终的变换向量 = 原始向量 * 变换矩阵
用我们上面例子中的方法来还原这个公式,即:
var vector = new THREE.Vector3(20, 20, 0);
var matrix = new THREE.Matrix4();
matrix.makeTranslation(10, 40, 0);
vector.applyMatrix4(matrix);
除了平移,Three的API中还提供了rotation和scale,scale变化很简单,它将使用makeScale(x, y, z)这个方法来表示缩放。
而旋转则相对复杂许多,Three.js提供以下旋转方法:
matrix.makeRotationX(angle);
matrix.makeRotationY(angle);
matrix.makeRotationZ(angle);
matrix.makeRotationAxis(axis, angle);
matrix.makeRotationFromEuler(euler);
matrix.makeRotationFromQuaternion(quaternion);
前三个方法分别代表的是绕X、Y、Z三个轴旋转,无需赘述。
第四个方法是前三个方法的整合版,第一个参数表示的是代表xyz的THREE.Vector3,第二个参数是旋转的弧度。下面两行代码是等价的:
matrix.makeRotationX(Math.PI);
matrix.makeRotationAxis(new THREE.Vector3(1, 0, 0), Math.PI);
第五个方法表示围绕x、y和z轴的旋转,这是表示旋转最常用的方式;第六个方法是一种基于轴和角度表示旋转的替代方法。
最后,Three.js api提供了一种方法来创建表示平移,旋转和缩放的组合的矩阵 -- matrix.compose:
var translation = new THREE.Vector3();
var rotation = new THREE.Quaternion();
var scale = new THREE.Vector3();
var matrix = new THREE.Matrix4();
matrix.compose(translation, rotation, scale);
矩阵运算进阶:乘法顺序与逆矩阵
矩阵相乘
矩阵乘法的意义在于叠加。

上图表示了三个变化:旋转、缩放和移动。
通过按次序相乘,三个变化矩阵可以得出一个最终的变化矩阵:
combinedMatrix = rotationMatrix * scaleMatrix * translationMatrix;
Three.js里提供了两种矩阵相乘的方法:
- matrix.multiply(otherMatrix)
- matrix.multiplyMatrices(matrixA, matrixB)
第一种方法表示将矩阵乘以另一个矩阵;而第二种方法代表的是将矩阵设置为matrixA * matrixB的结果。
我们在示例中也使用到了第一个方法:将圆柱体的矩阵乘以新的平移矩阵,和将球的矩阵乘以一个旋转矩阵。
需要注意的是,乘法交换律不适用于矩阵乘法,矩阵乘法是具有次序的,先旋转再移动和先移动再旋转的结果是完全不同的。
矩阵的逆
在数字的运算里,除法相当于是乘法的“撤销”操作:
4 x 5 = 20
20 / 5 = 4
但是在矩阵计算里,这个守则同样是不适用的。我们不能用向量去除一个矩阵,我们只能用向量去乘以一个矩阵的逆矩阵,来完成“撤销”的操作。
变化后的向量 = 原始向量 * 变化矩阵; 逆矩阵 = 变化矩阵.inverse(); 原始向量 = 变化后的向量 * 逆矩阵;
逆矩阵表示的是相反的变换。
Three.js里提供了一种计算逆矩阵的方法:
var matrix = new THREE.Matrix4();
var inverseMatrix = new THREE.Matrix4();
matrix.getInverse(inverseMatrix);
除此之外,逆矩阵还应用在3D场景中处理相机对象的时候。
最后
矩阵在3D世界里是一种十分强大的工具,它能够将任意变换都表示为一种相似的结构,并采用相同的计算过程。而实际上,矩阵的世界远远比这里介绍的内容更多,希望通过这些简要的介绍,可以让我们进入到一个更深的领域,并游刃有余的利用他处理图形开发中更复杂的场景。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对本站的支持。
为什么叫JavaScript:语言命名由来及与Java的历史渊源
2026-11-05 11:37:48
JS获取当前时间年月日时分秒并格式化为YYYY-MM-DD HH:mm:ss
2026-11-04 21:25:21
不同语言间JS与Java/Python加密结果不一致怎么办
2026-11-04 18:27:23
Mock.js 拦截 Axios 和 Fetch 请求的原理与配置方法
2026-11-04 08:13:59
JS怎么获取当前日期并格式化为YYYY-MM-DD
2026-11-03 18:47:03
