Three.js中矩阵和向量的使用教程


Posted in Javascript onMarch 19, 2019

前言

提起矩阵,很容易让人想起我们曾经学不会的线性代数和离散数学,但是作为图形开发中的核心部分,它代表着每一次的运动和变换,就像鱼不能脱离水一样,矩阵并不是一个可以避之不谈的话题。

好消息是,Three.js帮助我们把许多矩阵运算封装成了一些顶层的方法,并提供了一个优秀的数学库,我们不太需要知道HowToCalc,只需要知道HowToUse,就可以得到绝大部分我们想要的东西。

这篇文章将要介绍的就是,如何在不了解内部结构的情况下在Three.js中使用矩阵和向量。

从一个例子开始

在讲解一些枯燥的概念前先举一个小例子,来简要说明一下为什么我们要使用矩阵方法。

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);

这段代码将生成如下场景:

Three.js中矩阵和向量的使用教程

虽然不那么美观,但作为示例已经足够了,现在我希望这堆物体尺寸减半。通常我会把物体的scale属性减半,像这样:

box.scale.multiplyScalar(0.5); 
sphere.scale.multiplyScalar(0.5);
cylinder.scale.multiplyScalar(0.5);

Three.js中矩阵和向量的使用教程

和想象中的有些偏差。我的本意是让这一组物体进行一个整体的缩放,并不想让它们彼此偏离,为了修正这件事,我需要根据其他对象的缩放重新计算每个对象的位置。但这并不是一件很难解决的问题,three.js提供了一种优雅的方式,来处理这个问题。我们可以定义一个空对象,然后将三个对象放在其中,然后将比例应用于父对象。

var pile = new THREE.Object3D();
pile.scale.multiplyScalar(0.5);
pile.add(box);
pile.add(sphere);
pile.add(cylinder);
scene.add(pile);

Three.js中矩阵和向量的使用教程

接下来我们做一点更有趣的事。

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

Three.js中矩阵和向量的使用教程

它变成了这样,很明显,这不是我想要的东西。我们在这里有两个做法可供选择:第一,通过数学计算算出圆柱相对于球体的正确位置;第二,创建另一个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。

Three.js中矩阵和向量的使用教程

理解了上面几步,你就会知道最后一步该做什么了。

只需要一行代码,作用在球上:

sphere_matrix.multiply(new THREE.Matrix4().makeRotationZ(-Math.PI * 0.25));

Three.js中矩阵和向量的使用教程

达成了想要的效果,很酷。

示例中用到的方法

在上面的示例中,我将球和圆柱体分别沿y轴移动了一定的距离,并使用了makeTranslation这个方法。这个方法的作用是创建了一个平移矩阵。紧接着,我又使用到了applyMatrix的方法。这个方法的作用是把平移矩阵作用在球和圆柱体上。

那么什么是平移矩阵?它又是如何完成一次平移呢?

Three.js中最常见的一种4x4的矩阵,被称为变换矩阵,它所表示的变换类型包括平移、旋转和缩放。

用一个简单的数学题来说明变换矩阵:

Three.js中矩阵和向量的使用教程

有一个起始点,用向量来表示即为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);

矩阵相乘

矩阵乘法的意义在于叠加。

Three.js中矩阵和向量的使用教程

上图表示了三个变化:旋转、缩放和移动。

通过按次序相乘,三个变化矩阵可以得出一个最终的变化矩阵:

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 相关文章推荐
jQuery 页面 Mask实现代码
Jan 09 Javascript
javascript 匿名函数的理解(透彻版)
Jan 28 Javascript
25个非常棒的jQuery滑块插件和教程小结
Sep 02 Javascript
javascript window.open打开新窗口后无法再次打开该窗口问题的解决方法
Apr 12 Javascript
使用pjax实现无刷新更改页面url
Feb 05 Javascript
JavaScript学习笔记之ES6数组方法
Mar 25 Javascript
jquery实现自定义图片裁剪功能【推荐】
Mar 08 Javascript
微信小程序 循环及嵌套循环的使用总结
Sep 26 Javascript
基于Vue实现图书管理功能
Oct 17 Javascript
layui的layedit富文本赋值方法
Sep 18 Javascript
es6函数之箭头函数用法实例详解
Apr 25 Javascript
Jquery+javascript实现支付网页数字键盘
Dec 21 jQuery
vue+iview动态渲染表格详解
Mar 19 #Javascript
浅谈vue加载优化策略
Mar 19 #Javascript
Vue中Table组件Select的勾选和取消勾选事件详解
Mar 19 #Javascript
详解js加减乘除精确计算
Mar 19 #Javascript
jQuery添加新内容的四个常用方法分析【append,prepend,after,before】
Mar 19 #jQuery
浅谈JavaScript_DOM学习篇_图片切换小案例
Mar 19 #Javascript
vue多层嵌套路由实例分析
Mar 19 #Javascript
You might like
使用XDebug调试及单元测试覆盖率分析
2011/01/27 PHP
php实现将上传word文件转为html的方法
2015/06/03 PHP
PHP超全局数组(Superglobals)介绍
2015/07/01 PHP
php实现session共享的实例方法
2019/09/19 PHP
Javascript 验证上传图片大小[客户端]
2009/08/01 Javascript
网站导致浏览器崩溃的原因总结(多款浏览器) 推荐
2010/04/15 Javascript
javascript定义函数的方法
2010/12/06 Javascript
基于jQuery的动态增删改查表格信息,可左键/右键提示(原创自Zjmainstay)
2012/07/31 Javascript
JQuery页面的表格数据的增加与分页的实现
2013/12/10 Javascript
JavaScript函数作用域链分析
2015/02/13 Javascript
js中for in语句的用法讲解
2015/04/24 Javascript
JQuery 进入页面默认给已赋值的复选框打钩
2017/03/23 jQuery
jQuery导航条固定定位效果实例代码
2017/05/26 jQuery
在ABP框架中使用BootstrapTable组件的方法
2017/07/31 Javascript
vue异步axios获取的数据渲染到页面的方法
2018/08/09 Javascript
Vue 嵌套路由使用总结(推荐)
2020/01/13 Javascript
研究Python的ORM框架中的SQLAlchemy库的映射关系
2015/04/25 Python
举例讲解Python中装饰器的用法
2015/04/27 Python
python实用代码片段收集贴
2015/06/03 Python
在Python的Django框架中创建语言文件
2015/07/27 Python
使用Python实现BT种子和磁力链接的相互转换
2015/11/09 Python
python实现复制大量文件功能
2019/08/31 Python
Python多进程multiprocessing、进程池用法实例分析
2020/03/24 Python
您的时尚,您的生活方式:DTLR Villa
2019/12/25 全球购物
swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上
2013/07/06 面试题
创业计划书中要认真思考的问题
2013/12/28 职场文书
便利店投资的创业计划书
2014/01/12 职场文书
企业管理培训感言
2014/01/27 职场文书
幼儿园教育教学反思
2014/01/31 职场文书
销售内勤岗位职责
2014/04/15 职场文书
《第一朵杏花》教学反思
2014/04/16 职场文书
创业女性典型材料
2014/05/02 职场文书
副职竞争上岗演讲稿
2014/05/12 职场文书
群众路线领导对照材料
2014/08/23 职场文书
先进事迹材料怎么写
2014/12/30 职场文书
装修公司工程部经理岗位职责
2015/04/09 职场文书