Object3D 对象矩阵详解

预计阅读时间: 3 分钟

1. 前言

概述

在 Three.js 中,Object3D 及其子类有几个与矩阵相关的属性与方法,文档上对其描述或简略或抽象,常常会让初学者不能清楚地区分它们,从而不能很好的理解和运用矩阵。本文将从源码角度详细解析这些属性和方法。

主要涉及以下内容:

  • 属性:matrixmatrixWorldmatrixAutoUpdatematrixWorldAutoUpdatematrixWorldNeedsUpdate
  • 方法:updateMatrixupdateMatrixWorldupdateWorldMatrixapplyMatrix4

2. Object3D 的矩阵系统

2.1 Matrix(局部矩阵)

matrix 是记录 group 本身相对于 parent 的变换矩阵,是对 group 进行一些变换操作后更新到 matrix 中的,类似于相对路径的概念。

2.2 MatrixWorld(世界矩阵)

matrixWorld 是 group 相对于 world space 的变换矩阵,类似于绝对路径的概念。

2.3 矩阵更新机制

更新原理
  • 使用 parent 的 matrixWorld 与自身的 matrix 乘积得到自身的 matrixWorld
  • parent 的 matrixWorld 是通过不断累积祖先的变换得到的结果
  • 最外层的 matrixmatrixWorld 相同
深入理解
  1. group 实际是出于组织和管理 3D 物体的需求而人为创造的一个分组(不包含顶点),主要作用是对其子元素批量进行相同的矩阵变换
  2. 不管套了多少个 group,物体的 matrixWorld 始终表示其在场景世界中的绝对变换
  3. 对外层 group 应用变换操作时,最终都会自动执行更新操作,从而实现模型位置的变换

3. 核心方法解析

3.1 updateMatrix()

1updateMatrix() {
2    this.matrix.compose(this.position, this.quaternion, this.scale);
3    this.matrixWorldNeedsUpdate = true;
4}

主要功能:

  1. 将当前的局部矩阵 matrix 分解到 position/scale/quaternion 三个分量
  2. matrixWorldNeedsUpdate 标记为 true

3.2 updateMatrixWorld(force)

主要功能:

  1. matrixAutoUpdate 为 true,则调用 updateMatrix 更新本地 matrix
  2. matrixWorldNeedsUpdate 或参数 force 为 true 时,需要更新自身的 matrixWorld
  3. 如果更新了自身的 matrixWorld,则必须更新所有后代节点
  4. 对所有 children 递归调用 updateMatrixWorld(force)
1updateMatrixWorld(force) {
2    if (this.matrixAutoUpdate) this.updateMatrix();
3
4    if (this.matrixWorldNeedsUpdate || force) {
5        if (this.parent === null) {
6            this.matrixWorld.copy(this.matrix);
7        } else {
8            this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix);
9        }
10        this.matrixWorldNeedsUpdate = false;
11        force = true;
12    }
13
14    const children = this.children;
15    for (let i = 0, l = children.length; i < l; i++) {
16        const child = children[i];
17        if (child.matrixWorldAutoUpdate === true || force === true) {
18            child.updateMatrixWorld(force);
19        }
20    }
21}

3.3 updateWorldMatrix(updateParents, updateChildren)

主要功能:

  1. 一定会更新自己的 matrixWorld
  2. 如果设置了 matrixAutoUpdate,则也会更新自己的 matrix
  3. 符合条件且 matrixWorldAutoUpdate 为 true 时,会递归更新父元素和后代的 matrixWorld
1updateWorldMatrix(updateParents, updateChildren) {
2    const parent = this.parent;
3
4    if (updateParents === true && parent !== null && parent.matrixWorldAutoUpdate === true) {
5        parent.updateWorldMatrix(true, false);
6    }
7
8    if (this.matrixAutoUpdate) this.updateMatrix();
9
10    if (this.parent === null) {
11        this.matrixWorld.copy(this.matrix);
12    } else {
13        this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix);
14    }
15
16    if (updateChildren === true) {
17        const children = this.children;
18        for (let i = 0, l = children.length; i < l; i++) {
19            const child = children[i];
20            if (child.matrixWorldAutoUpdate === true) {
21                child.updateWorldMatrix(false, true);
22            }
23        }
24    }
25}

3.4 applyMatrix4(matrix)

1applyMatrix4(matrix) {
2    if (this.matrixAutoUpdate) this.updateMatrix();
3    this.matrix.premultiply(matrix);
4    this.matrix.decompose(this.position, this.quaternion, this.scale);
5}
矩阵乘法说明

矩阵的左乘与右乘是指矩阵在表达式的位置:

  • A × B:矩阵 A 左乘矩阵 B,或者矩阵 B 右乘矩阵 A
  • Matrix4 中的相关方法:
    1// 矩阵左乘矩阵 m
    2multiply(m) { 
    3    return this.multiplyMatrices(this, m);
    4}
    5// 矩阵右乘矩阵 m
    6premultiply(m) {
    7    return this.multiplyMatrices(m, this);
    8}
    9// 矩阵 a × b,a 左乘 b
    10multiplyMatrices(a, b) {}