今天郭先生说一说three.js中的Matrix4,相较于Matrix3来说,Matrix4和three.js联系的更紧密,因为在4x4矩阵最常用的用法是作为一个变换矩阵。这使得表示三维空间中的一个点的向量Vector3通过乘以矩阵来进行转换,如平移、旋转、剪切、缩放、反射、正交或透视投影等。这就是把矩阵应用到向量上。

1. Object3D矩阵

任何3D物体Object3D都有三个关联的矩阵:

  • Object3D.matrix: 存储物体的本地变换。 这是对象相对于其父对象的变换。
  • Object3D.matrixWorld: 对象的全局或世界变换。如果对象没有父对象,那么这与存储在矩阵matrix中的本地变换相同。
  • Object3D.modelViewMatrix: 表示对象相坐标相对于摄像机空间坐标转换, 一个对象的 modelViewMatrix 是物体世界变换矩阵乘以摄像机相对于世界空间变换矩阵的逆矩阵。

摄像机Cameras 有两个额外的四维矩阵:

  • Camera.matrixWorldInverse: 视图矩阵 - 摄像机世界坐标变换的逆矩阵。
  • Camera.projectionMatrix: 投影矩阵 - 表示将场景中的信息投影到裁剪空间。

注意:物体的正规矩阵 Object3D.normalMatrix 并不是一个4维矩阵,而是一个三维矩阵Matrix3。下面举例子说明一下这几个矩阵。

var geom = new THREE.BoxGeometry(); //创建一个几何体
var mate = new THREE.MeshNormalMaterial(); //创建一个材质
var mesh = new THREE.Mesh(geom, mate); //创建一个网格
var group = new THREE.Group(); //创建一个组
mesh.position.setX(9); //设置网格的位置
group.add(mesh); //将网格添加到组里
group.position.setX(8); //设置组的位置
scene.add(group); //将组添加到场景中
scene.position.setX(7); //设置场景的位置

下面我们来分析一下这几个矩阵,mesh、group和scene的Object3D.matrix矩阵分别是

  • mesh – elements: (16) [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 9, 0, 0, 1]
  • group – elements: (16) [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 8, 0, 0, 1]
  • scene – elements: (16) [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 7, 0, 0, 1]

可以看出他们本地变换矩阵的x方向位置分量分别是9、8和7,很明显,Object3D.matrix是相对于父元素的变换,我们再来看看mesh、group和scene的Object3D.matrixWorld矩阵

  • mesh – elements: (16) [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 24, 0, 0, 1]
  • group – elements: (16) [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 15, 0, 0, 1]
  • scene – elements: (16) [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 7, 0, 0, 1]

其中scene世界变换矩阵x方向的位置分量是7,group的是15(7+8),mesh的是24(7+8+9),也就是说世界变换矩阵是该元素相对于世界坐标系的变换,也是该元素的本地变换和该元素父级世界变换的乘积。
说Object3D.modelViewMatrix之前,先说一下Camera.matrixWorldInverse矩阵,这个矩阵有很重要的意义,

camera = new THREE.PerspectiveCamera(60,window.innerWidth / window.innerHeight,0.1,10000);
camera.position.set(0, 0, 60);
scene.add(camera);

由于相机是加到场景里面的所以相机的本地变换矩阵是elements: (16) [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 60, 1],相机的世界变换矩阵是elements: (16) [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 7, 0, 60, 1],相机的视图矩阵是elements: (16) [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -7, 0, -60, 1],Camera.projectionMatrix是投影矩阵,它和相机的方向,视角等等有关。

Object3D.modelViewMatrix是对象相坐标相对于摄像机空间坐标转换,如上mesh的世界变换是elements: (16) [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 24, 0, 0, 1],摄像机视图矩阵阵elements: (16) [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -7, 0, -60, 1],那么将二者相乘,既是mesh的Object3D.modelViewMatrix的值elements: (16) [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 17, 0, -60, 1]。
以上是我自己在矩阵上推到出的结论。

2. Matrix4的属性

1. elements

矩阵列优先column-major列表。

2. isMatrix4

用于判定此对象或者此类的派生对象是否是三维矩阵。默认值为 true。

3. Matrix4的方法

1. set(number, number, number …): Matrix4

设置set()方法参数采用行优先row-major, 而它们在内部是用列优先column-major顺序存储在数组当中。方法同三维矩阵

2. identity(): Matrix4

创建并初始化一个4X4的单位矩阵identity matrix。方法同三维矩阵。

3. clone(): this

创建一个新的矩阵,元素elements与该矩阵相同。方法同三维矩阵。

4. copy( m: Matrix4 ): this

将矩阵m的元素elements复制到当前矩阵中。方法同三维矩阵。

5. copyPosition( m: Matrix4 ): Matrix4

将给定矩阵m : Matrix4 的平移分量拷贝到当前矩阵中。

var matrix1 = new THREE.Matrix4().makeTranslation(3,3,3);
var matrix2 = new THREE.Matrix4().makeScale(2,2,2);
matrix2.copyPosition(matrix1);
console.log(matrix2); //返回elements: (16) [2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 3, 3, 3, 1]

6. extractBasis( xAxis: Vector3, yAxis: Vector3, zAxis: Vector3 ): Matrix4

将矩阵的基向量basis提取到指定的3个轴向量中。方法同三维矩阵。

7. makeBasis( xAxis: Vector3, yAxis: Vector3, zAxis: Vector3 ): Matrix4

通过给定的三个向量设置该矩阵为基矩阵basis:
xAxis.x, yAxis.x, zAxis.x, 0,
xAxis.y, yAxis.y, zAxis.y, 0,
xAxis.z, yAxis.z, zAxis.z, 0,
0, 0, 0, 1

8. extractRotation( m: Matrix4 ): Matrix4

将给定矩阵m的旋转分量提取到该矩阵的旋转分量中。

var matrix1 = new THREE.Matrix4().makeRotationZ(Math.PI/6);
var matrix2 = new THREE.Matrix4().makeScale(2,2,2);
matrix2.extractRotation(matrix1)
console.log(matrix2); //返回elements: (16) [0.8660254037844387, 0.49999999999999994, 0, 0, -0.49999999999999994, 0.8660254037844387, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]

9. makeRotationFromEuler( euler: Euler ): Matrix4

将传入的欧拉角转换为该矩阵的旋转分量(左上角的3x3矩阵)。 矩阵的其余部分被设为单位矩阵。根据欧拉角euler的旋转顺序order,总共有六种可能的结果。

var euler = new THREE.Euler(0,0,Math.PI/6, 'XYZ');
var matrix = new THREE.Matrix4().makeScale(2,2,2);
matrix.makeRotationFromEuler(euler)
console.log(matrix); //返回elements: (16) [0.8660254037844387, 0.49999999999999994, 0, 0, -0.49999999999999994, 0.8660254037844387, 0, 0, 0, -0, 1, 0, 0, 0, 0, 1]

因为都是绕z轴旋转30度,所以返回结果和上面一样

10. makeRotationFromQuaternion( q: Quaternion ): Matrix4

将这个矩阵的旋转分量设置为四元数q指定的旋转,使用方法同makeRotationFromEuler。

11. lookAt( eye: Vector3, target: Vector3, up: Vector3 ): Matrix4

构造一个旋转矩阵,从eye 指向 center,由向量 up : Vector3 定向。

var eye = new THREE.Vector3(0,0,0);
var target = new THREE.Vector3(1,0,0);
var up = new THREE.Vector3(0,0,1);
var matrix = new THREE.Matrix4();
matrix.lookAt(eye,target,up);
console.log(matrix); //返回elements: (16) [0, -0, 1, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 1]

12. multiply( m: Matrix4 ): Matrix4

将当前矩阵乘以矩阵m。方法同三维矩阵。

13. premultiply( m: Matrix4 ): Matrix4

将矩阵m乘以当前矩阵。方法同三维矩阵。

14. multiplyMatrices( a: Matrix4, b: Matrix4 ): Matrix4

设置当前矩阵为矩阵a x 矩阵b。方法同三维矩阵。

15. multiplyScalar( s: number ): Matrix4

当前矩阵所有的元素乘以该缩放值s。方法同三维矩阵。

16. determinant(): number;

计算并返回矩阵的行列式determinant。方法同三维矩阵。

17. transpose(): Matrix4

将该矩阵转置Transposes。方法同三维矩阵。

18. setPosition( v: Vector3 | number, y?: number, z?: number ): Matrix4

取传入参数v : Vector3中值设置该矩阵的位置分量,不影响该矩阵的其余部分——即。或者直接传递向量的分量值(v,y,z),下面是源码:

setPosition: function ( x, y, z ) {
var te = this.elements;
if ( x.isVector3 ) {
te[ 12 ] = x.x;
te[ 13 ] = x.y;
te[ 14 ] = x.z;
} else {
te[ 12 ] = x;
te[ 13 ] = y;
te[ 14 ] = z;
}
return this;
}

19. getInverse( m: Matrix4 ): Matrix4

使用逆矩阵计算方法analytic method, 将当前矩阵设置为给定矩阵的逆矩阵inverse,如果throwOnDegenerate 参数没有设置且给定矩阵不可逆,那么将当前矩阵设置为3X3单位矩阵。逆矩阵求法可以是用伴随矩阵的方法,同三维矩阵。

20. scale( v: Vector3 ): Matrix4

将该矩阵的列向量乘以对应向量v的分量。

var matrix = new THREE.Matrix4();
matrix.scale(new THREE.Vector3(1,2,3));
console.log('matrix', matrix); //返回elements: (16) [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1]

21. getMaxScaleOnAxis(): number

获取3个轴方向的最大缩放值。

var matrix = new THREE.Matrix4().makeScale(2,3,4);
matrix.getMaxScaleOnAxis(); //返回4

22. makeTranslation( x: number, y: number, z: number ): Matrix4

x - 在X轴上的平移量。y - 在Y轴上的平移量。z - 在Z轴上的平移量。不多说。

23. makeRotationX( theta: number ): Matrix4

把该矩阵设置为绕x轴旋转弧度theta (θ)大小的矩阵。很简单,不多说。

24. makeRotationY( theta: number ): Matrix4

把该矩阵设置为绕y轴旋转弧度theta (θ)大小的矩阵。同上。

25. makeRotationZ( theta: number ): Matrix4

把该矩阵设置为绕z轴旋转弧度theta (θ)大小的矩阵。同上。

26. makeRotationAxis( axis: Vector3, angle: number ): Matrix4

设置当前矩阵为围绕轴 axis 旋转量为 theta弧度。也不难。

var matrix = new THREE.Matrix4(); //一下两个旋转是相同的 matrix.makeRotationZ(Math.PI/4);
matrix.makeRotationAxis(new THREE.Vector3(0,0,1), Math.PI/4);

27. makeScale( x: number, y: number, z: number ): Matrix4

x - 在X轴方向的缩放比。y - 在Y轴方向的缩放比。z - 在Z轴方向的缩放比。方法不难,就不多说了。

28. compose( translation: Vector3, rotation: Quaternion, scale: Vector3 ): Matrix4

设置将该对象由位置position,四元数quaternion 和 缩放scale 组合变换的矩阵。内部先调用makeRotationFromQuaternion( quaternion ) 再调用缩放scale( scale )最后是平移setPosition( position )。
这个方法集成了旋转、缩放和平移。

//使用make系列的方法操作
Object3D.applyMatrix(new THREE.Matrix4().makeScale(2,1,1));
Object3D.applyMatrix(new THREE.Matrix4().makeTranslation(0,4,0));
Object3D.applyMatrix(new THREE.Matrix4().makeRotationZ(Math.PI/6));
//使用compose方法操作
var matrix = new THREE.Matrix4();
var trans = new THREE.Vector3(0,4,0);
var rotat = new THREE.Quaternion().setFromEuler(new THREE.Euler(0,0,Math.PI/6));
var scale = new THREE.Vector3(2,1,1);
Object3D.applyMatrix4(matrix.compose(trans, rotat, scale)); //效果同上

29. decompose(translation: Vector3, rotation: Quaternion, scale: Vector3): Matrix4;

将矩阵分解到给定的平移position ,旋转 quaternion,缩放scale分量中。就是compose的逆过程。随便举个例子。

var matrix = new THREE.Matrix4().set(1,2,3,4,2,3,4,5,3,4,5,6,4,5,6,7);
var trans = new THREE.Vector3();
var rotat = new THREE.Quaternion();
var scale = new THREE.Vector3();
matrix.decompose(trans, rotat, scale);
console.log(trans); //返回Vector3 {x: 4, y: 5, z: 6} 因为是随便写的,所以只有平移变量不需计算就可以看出来的
console.log(rotat); //返回Quaternion {_x: 0.05565363763555474, _y: -0.11863820054057297, _z: 0.051265314875937947, _w: 0.7955271896092125}
console.log(scale); //返回Vector3 {x: 3.7416573867739413, y: 5.385164807134504, z: 7.0710678118654755}

30. makePerspective(fov: number,aspect: number,near: number,far: number): Matrix4

创建一个透视投影矩阵perspective projection。 在引擎内部由PerspectiveCamera.updateProjectionMatrix()使用。

31. makeOrthographic(left: number,right: number,top: number,bottom: number,near: number,far: number): Matrix4;

创建一个正交投影矩阵orthographic projection。 在引擎内部由OrthographicCamera.updateProjectionMatrix()使用。

32. equals( matrix: Matrix4 ): boolean

如果矩阵m 与当前矩阵所有对应元素相同则返回true。

33. fromArray( array: number[], offset?: number ): Matrix4

使用基于列优先格式column-major的数组来设置该矩阵。方法同三维矩阵。

34. toArray( array?: number[], offset?: number ): number[]

使用列优先column-major格式将此矩阵的元素写入数组中。

四维矩阵就先说到这里,我们以后将会经常看到他,无论是在变换中,还是在着色器中。

转载请注明地址:郭先生的博客

three.js 数学方法之Matrix4的更多相关文章

  1. three.js 数学方法之Plane

    今天郭先生就来继续说一说three.js数学方法中的plane(平面).在三维空间中无限延伸的二维平面,平面方程用单位长度的法向量和常数表示.构造器为Plane( normal : Vector3, ...

  2. three.js 数学方法之Box3

    从今天开始郭先生就会说一下three.js 的一些数学方法了,像Box3.Plane.Vector3.Matrix3.Matrix4当然还有欧拉角和四元数.今天说一说three.js的Box3方法(B ...

  3. three.js 数学方法之Matrix3

    今天郭先生来说一说three.js的三维矩阵,这块知识需要结合线性代数的一些知识,毕业时间有点长,线性代数的知识大部分都还给了老师.于是一起简单的复习了一下.所有的计算都是使用列优先顺序进行的.然而, ...

  4. three.js 数学方法之Vector3

    今天郭先生来说一说three.js的Vector3,该类表示的是一个三维向量(3D vector). 一个三维向量表示的是一个有顺序的.三个为一组的数字组合(标记为x.y和z),可被用来表示很多事物, ...

  5. js数学方法应用

    找出数组中最大的数 var values = [1, 2, 3, 4, 5, 6, 7, 8]; alert(Math.min.apply(Math,values))//8 这个技巧的关键是把 Mat ...

  6. js数组及常用数学方法

    数组方法 清空数组   1: arr.length=0;   2: arr=[]; arr.push()          //往数组最后一个添加元素,会待会一个返回值,就是新的数组长度arr.uns ...

  7. js数组方法大全(下)

    # js数组方法大全(下) 记录一下整理的js数组方法,免得每次要找方法都找不到.图片有点多,注意流量,嘻嘻! 本期分享 forEach() map() filer() every() some() ...

  8. js数组方法

    数组方法清空数组1: arr.length=02: arr=[]arr.push()//往数组最后一个添加元素,会待会一个返回值,就是新的数组长度arr.unshift()//往数组的第一个添加元素, ...

  9. js调用php和php调用js的方法举例

    js调用php和php调用js的方法举例1 JS方式调用PHP文件并取得php中的值 举一个简单的例子来说明: 如在页面a.html中用下面这句调用: <script type="te ...

随机推荐

  1. Springboot--元注解及自定义注解(表单验证)

    本文简单说明一下元注解,然后对元注解中的@Retention做深入的讨论,在文章最后使用元注解写一个自定义注解来结尾. 一.结论: @Target:注解的作用目标 @Target(ElementTyp ...

  2. METS介绍

    METS介绍 首页  >  关于METS > METS介绍   医护英语水平考试(Medical English Test System, 以下简称:METS)是由教育部考试中心与中国国际 ...

  3. java List的初始化

    今天在处理生成excel的时候用到了java的list,但是需要直接赋值固定的几个变量,如果先初始化然后add的方法: List<String> name = new ArrayList( ...

  4. 数据库连接池 --Druid 连接工具类创建_JDBCUtils

    package com.itheima.jdbc_druid; import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.s ...

  5. 二.4vue展示用户数据及用户组操作以及给用户组添加额外字段

    一.用户列表 1.新建(1)views/users/index.vue: <template> <div class="user-list-container"& ...

  6. Spring Security(三) —— 核心配置解读

    摘要: 原创出处 https://www.cnkirito.moe/spring-security-3/ 「老徐」欢迎转载,保留摘要,谢谢! 3 核心配置解读 上一篇文章<Spring Secu ...

  7. Spring笔记(3) - debug源码AOP原理解析

    案例 @EnableAspectJAutoProxy//开启基于注解的aop模式 @Configuration public class AOPConfig { //业务逻辑类加入容器中 @Bean ...

  8. css实现div多边框_box-shadow模拟多边框、outline描边实现

    在css3中我们知道可以使用box-shadow属性轻松的为元素添加阴影效果,并且可以设置多组效果,每组参数值用逗号隔开.如果把box-shadow特性的两个偏移量 h-shadow .v-shado ...

  9. P1220 关路灯——区间dp

    P1220 关路灯 题目描述 某一村庄在一条路线上安装了 \(n\) 盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少).老张就住在这条路中间某一路灯旁,他有一项工作就是每天早上天亮时一 ...

  10. UVA1464 Traffic Real Time Query System

    传送门:https://www.luogu.com.cn/problem/UVA1464 看到这道题,求必经的点数,还是无向图.那么妥妥的圆方树.圆方树上的任意两圆点间的路径必定是圆点方点相交错的,对 ...