提起矩阵,很容易让人想起咱们曾经学不会的线性代数和离散数学,可是做为图形开发中的核心部分,它表明着每一次的运动和变换,就像鱼不能脱离水同样,矩阵并非一个能够避之不谈的话题。javascript
好消息是,Three.js帮助咱们把许多矩阵运算封装成了一些顶层的方法,并提供了一个优秀的数学库,咱们不太须要知道如何计算,只须要知道如何用,就能够获得绝大部分咱们想要的东西。java
这篇文章将要介绍的就是,如何在不了解内部结构的状况下在Three.js中使用矩阵和向量。api
在讲解一些枯燥的概念前先举一个小例子,来简要说明一下为何咱们要使用矩阵方法。bash
这是咱们最终要完成的效果。markdown
首先,咱们要建立三个几何体:app
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)}) 复制代码
这三个几何体分别是盒子、球和圆柱体。工具
而后去建立三个网格,并将它们置入场景。spa
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); 复制代码
这段代码将生成以下场景:code
虽然不那么美观,但做为示例已经足够了,如今我但愿这堆物体尺寸减半。一般我会把物体的scale属性减半,像这样:orm
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里提供了两种矩阵相乘的方法:
第一种方法表示将矩阵乘以另外一个矩阵;而第二种方法表明的是将矩阵设置为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世界里是一种十分强大的工具,它可以将任意变换都表示为一种类似的结构,并采用相同的计算过程。而实际上,矩阵的世界远远比这里介绍的内容更多,但愿经过这些简要的介绍,可让咱们进入到一个更深的领域,并游刃有余的利用他处理图形开发中更复杂的场景。