MathmaticWebGLProgramming

WebGL 编程中的数学知识之矩阵

August 3, 20251 min

什么是矩阵

矩阵就是一个按照长方形排列的数表,本质上就是一个二维数组。

将m x n 个元素按 m 行,n 列的方式排列,可用下面的方式表示:

A=[a11a12a1na21a22a2nam1am2amn]\mathbf{A} = \begin{bmatrix} a_{11} & a_{12} & \cdots & a_{1n} \\ a_{21} & a_{22} & \cdots & a_{2n} \\ \vdots & \vdots & \ddots & \vdots \\ a_{m1} & a_{m2} & \cdots & a_{mn} \end{bmatrix}

常见的有:2x2,3x3,4x4等。

矩阵可用来表示线性变换,图形学中的平移、缩放、旋转、投影、相机视角等变换,全靠矩阵。

矩阵的加减法

矩阵加减法计算是在矩阵对应位置上元素的加减法计算,结果还是一个矩阵。

假设有两个3x3矩阵 A 和 B,它们的加法计算公式为:

A+B=[a11a12a13a21a22a23a31a32a33]+[b11b12b13b21b22b23b31b32b33]=[a11+b11a12+b12a13+b13a21+b21a22+b22a23+b23a31+b31a32+b32a33+b33]\mathbf{A} + \mathbf{B} = \begin{bmatrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \end{bmatrix} + \begin{bmatrix} b_{11} & b_{12} & b_{13} \\ b_{21} & b_{22} & b_{23} \\ b_{31} & b_{32} & b_{33} \end{bmatrix} = \begin{bmatrix} a_{11} + b_{11} & a_{12} + b_{12} & a_{13} + b_{13} \\ a_{21} + b_{21} & a_{22} + b_{22} & a_{23} + b_{23} \\ a_{31} + b_{31} & a_{32} + b_{32} & a_{33} + b_{33} \end{bmatrix}

它们的减法公式为:

AB=[a11a12a13a21a22a23a31a32a33]+[b11b12b13b21b22b23b31b32b33]=[a11b11a12b12a13b13a21b21a22b22a23b23a31b31a32b32a33b33]\mathbf{A} - \mathbf{B} = \begin{bmatrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \end{bmatrix} + \begin{bmatrix} b_{11} & b_{12} & b_{13} \\ b_{21} & b_{22} & b_{23} \\ b_{31} & b_{32} & b_{33} \end{bmatrix} = \begin{bmatrix} a_{11} - b_{11} & a_{12} - b_{12} & a_{13} - b_{13} \\ a_{21} - b_{21} & a_{22} - b_{22} & a_{23} - b_{23} \\ a_{31} - b_{31} & a_{32} - b_{32} & a_{33} - b_{33} \end{bmatrix}

注意:

  • 只有相同维度的矩阵才能进行加减法运算
  • 矩阵加法使用交换律和结合律:A + B = B + A;(A + B) + C = A + (B + C)

GLSL示例:

mat3 A = mat3(
    1.0, 2.0, 3.0,
    4.0, 5.0, 6.0,
    7.0, 8.0, 9.0
);

mat3 B = mat3(
    9.0, 8.0, 7.0,
    6.0, 5.0, 4.0,
    3.0, 2.0, 1.0
);

mat3 C = A + B; // mat(10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0)
mat3 D = A - B; // mat(-8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0, 8.0)

矩阵的数乘

矩阵的数乘就是用一个数(标量)去乘m x n矩阵的所有元素,结果还是一个m x n矩阵。

kA=k[a11a12a13a21a22a23a31a32a33]=[ka11ka12ka13ka21ka22ka23ka31ka32ka33]k \mathbf{A} = k \begin{bmatrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \end{bmatrix} = \begin{bmatrix} ka_{11} & ka_{12} & ka_{13} \\ ka_{21} & ka_{22} & ka_{23} \\ ka_{31} & ka_{32} & ka_{33} \end{bmatrix}

几何意义:矩阵本身可以看作是一种“空间变换规则”,例如矩阵S,对它数乘一个系数 k=2后:

2S=2[200030001]=[400060002]2 \mathbf{S} = 2 \begin{bmatrix} 2 & 0 & 0 \\ 0 & 3 & 0 \\ 0 & 0 & 1 \end{bmatrix} = \begin{bmatrix} 4 & 0 & 0 \\ 0 & 6 & 0 \\ 0 & 0 & 2 \end{bmatrix}

矩阵 S 是一个缩放矩阵,表示:在x方向缩放 2 倍,在y方向缩放 3 倍,在z方向不变。数乘 2 之后,表示x方向缩放4倍,y方向缩放6倍,z方向缩放2倍。这说明数乘改变了缩放比例,整体放大了原变换的强度。

注:

  • 矩阵数乘满足分配律:(k + v) A = kA + vA; k(A + B) = kA + kB
  • 矩阵数乘满足结合律:(kv)A = k(vA)

ThreeJS示例:

const m = new THREE.Matrix3().set(
  1, 2, 3,
  4, 5, 6,
  7, 8, 9
)

m.multiplyScalar(2)
console.log(m.elements) // [2, 4, 6, 8, 10, 12, 14, 16, 18]

GLSL示例:

const m = new THREE.Matrix3().set(
  1, 2, 3,
  4, 5, 6,
  7, 8, 9
)

m.multiplyScalar(2)
console.log(m.elements) // [2, 4, 6, 8, 10, 12, 14, 16, 18]

矩阵的转置

矩阵转置是将一个矩阵的行列互换的操作,一个mxn的矩阵转置后,得到的结果是一个nxm的矩阵。

(AT)ij=Aji(A^T)_{ij} = A_{ji} A=[a11a12a13a21a22a23a31a32a33],AT=[a11a21a31a12a22a32a13a23a33]A = \begin{bmatrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \end{bmatrix}, \quad A^T = \begin{bmatrix} a_{11} & a_{21} & a_{31} \\ a_{12} & a_{22} & a_{32} \\ a_{13} & a_{23} & a_{33} \end{bmatrix}

例如:

A=[123456]2×3,AT=[142536]3×3A = \begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix}_{2 \times 3}, A^T = \begin{bmatrix} 1 & 4 \\ 2 & 5 \\ 3 & 6 \end{bmatrix}_{3 \times 3}

注:

  • 转置的转置:(AT)T=A(A^T)^T = A
  • 和的转置:(A+B)T=AT+BT(A+B)^T = A^T + B^T
  • 数乘的转置:(kA)T=kAT(kA)^T = kA^T
  • 乘法的转置(注意顺序反转):(AB)T=BTAT(AB)^T = B^TA^T

Three.js 示例:

const m = new THREE.Matrix3();
m.set(1, 2, 3, 4, 5, 6, 7, 8, 9)
const n = m.clone().transpose()

console.log(m.elements) // [1, 4, 7, 2, 5, 8, 3, 6, 9]
console.log(n.elements) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

GLSL 示例:

mat3 m = mat3(
  1.0, 2.0, 3.0,
  4.0, 5.0, 6.0,
  7.0, 8.0, 9.0
);

mat3 t = transpose(m);

矩阵的乘法:矩阵乘矩阵

两个矩阵相乘必须满足一个条件:被乘矩阵的列数等于乘数矩阵的行数,如果矩阵A的大小是 m x n,矩阵B的大小是 n x p,那么它们的乘积 C = A x B 就是一个 m x p 大小的矩阵。即:前一个矩阵的列数 = 后一个矩阵的行数,才能相乘。

3 x 3矩阵的计算公式如下:

C=A×B=[a11a12a13a21a22a23a31a32a33]×[b11b12b13b21b22b23b31b32b33]=[a11b11+a12b21+a13b31a11b12+a12b22+a13b32a11b13+a12b23+a13b33a21b11+a22b21+a23b31a21b12+a22b22+a23b32a21b13+a22b23+a23b33a31b11+a32b21+a33b31a31b12+a32b22+a33b32a31b13+a32b23+a33b33]\mathbf{C} = \mathbf{A} \times \mathbf{B} = \begin{bmatrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \end{bmatrix} \times \begin{bmatrix} b_{11} & b_{12} & b_{13} \\ b_{21} & b_{22} & b_{23} \\ b_{31} & b_{32} & b_{33} \end{bmatrix} = \begin{bmatrix} a_{11}b_{11}+a_{12}b_{21}+a_{13}b_{31} & a_{11}b_{12}+a_{12}b_{22}+a_{13}b_{32} & a_{11}b_{13}+a_{12}b_{23}+a_{13}b_{33} \\ a_{21}b_{11}+a_{22}b_{21}+a_{23}b_{31} & a_{21}b_{12}+a_{22}b_{22}+a_{23}b_{32} & a_{21}b_{13}+a_{22}b_{23}+a_{23}b_{33} \\ a_{31}b_{11}+a_{32}b_{21}+a_{33}b_{31} & a_{31}b_{12}+a_{32}b_{22}+a_{33}b_{32} & a_{31}b_{13}+a_{32}b_{23}+a_{33}b_{33} \end{bmatrix}

C=[c11c12c13c21c22c23c31c32c33],cij=k=13aikbkjC = \begin{bmatrix} c_{11} & c_{12} & c_{13} \\ c_{21} & c_{22} & c_{23} \\ c_{31} & c_{32} & c_{33} \end{bmatrix}, \quad c_{ij} = \sum_{k=1}^{3} a_{ik} b_{kj}

C 也是一个 3x3 矩阵,每个元素的计算公式为:

cij=ai1b1j+ai2b2j+ai3b3j,i,j=1,2,3c_{ij} = a_{i1}b_{1j} + a_{i2}b_{2j} + a_{i3}b_{3j}, \quad i, j = 1, 2, 3

记忆技巧:结果矩阵的每个元素 Cij 等于 A 的第 i 行与 B 的第 j 列对应元素乘积之和。

一般形式的公式为:

设:

A=[aik]m×p,B=[bkj]p×n,C=AB=[cij]m×nA = [a_{ik}]_{m \times p}, \quad B = [b_{kj}]_{p \times n}, \quad C = AB = [c_{ij}]_{m \times n}

每个元素的公式是:

cij=k=1paikbkj,i=1,,m,  j=1,,nc_{ij} = \sum_{k=1}^{p} a_{ik} b_{kj}, \quad i=1,\dots,m, \; j=1,\dots,n

注:

  • 矩阵相乘满足分配律:A(B + C) = AB + AC, (A + B)C = AC + BC
  • 满足结合律:(AB)C = A(BC)
  • 满足标量结合律:k(AB) = (kA)B = A(kB)
  • 不满足交换律,顺序很重要:AB ≠ BA

Three.js 示例:

// A 矩阵
const A = new THREE.Matrix3().set(
  1, 2, 3,
  4, 5, 6,
  7, 8, 9
);

// B 矩阵
const B = new THREE.Matrix3().set(
  9, 8, 7,
  6, 5, 4,
  3, 2, 1
);

// C = A × B
const C = new THREE.Matrix3().copy(A).multiply(B);

console.log(C.elements);
// [1*9+2*6+3*3, 1*8+2*5+3*2, 1*7+2*4+3*1
// 4*9+5*6+6*3, 4*8+5*5+6*2, 4*7+5*4+6*1
// 7*9+8*6+9*3, 7*8+8*5+9*2, 7*7+8*4+9*1]

//[ 30, 24, 18,
//  84, 69, 54, 
// 138, 114, 90]

GLSL 示例:

mat3 A = mat3(
    1.0, 2.0, 3.0,
    4.0, 5.0, 6.0,
    7.0, 8.0, 9.0
);

mat3 B = mat3(
    9.0, 8.0, 7.0,
    6.0, 5.0, 4.0,
    3.0, 2.0, 1.0
);

// 矩阵相乘
mat3 C = A * B;

矩阵的乘法:矩阵和向量相乘

任何一个向量都可以用一维矩阵表示,行向量可以看成一个1xn的矩阵(一行n列),列向量可以看成是一个mx1的矩阵(m行一列)。

根据两个矩阵可以相乘的条件,矩阵和向量相乘有两种形式:

  • 列向量形式:如果 xx 是列向量 (n×1)(n \times 1),常见的乘法是:

    Am×nxn×1=ym×1A_{m \times n} \cdot x_{n \times 1} = y_{m \times 1}

    结果是一个mx1的列向量,这是最常用的「矩阵乘向量」。

  • 行向量形式:如果 xTx^T是行向量 (1×m)(1 \times m),那么它可以乘在矩阵的左边:

    x1×mTAm×n=y1×nT x^T_{1 \times m} \cdot A_{m \times n} = y^T_{1 \times n}

    结果是一个1xn的行向量。

计算过程可以参考矩阵相乘的过程,但只需更少的计算,比如:

y=Ax=[a11a12a13a21a22a23a31a32a33][x1x2x3]=[a11x1+a12x2+a13x3a21x1+a22x2+a23x3a31x1+a32x2+a33x3]y = Ax = \begin{bmatrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \end{bmatrix} \cdot \begin{bmatrix} x_{1} \\ x_{2} \\ x_{3} \end{bmatrix} = \begin{bmatrix} a_{11}x_{1} + a_{12}x_{2} + a_{13}x_{3} \\ a_{21}x_{1} + a_{22}x_{2} + a_{23}x_{3} \\ a_{31}x_{1} + a_{32}x_{2}+ a_{33}x_{3} \end{bmatrix}

几何意义:可以把矩阵看成一个线性变换,它把一个向量映射为一个新的向量。如在计算机图形学中,旋转或缩放矩阵作用在向量上得到旋转或缩放后的向量,投影矩阵作用在向量上得到投影坐标。

Three.js 示例:

const m = new THREE.Matrix3().set(
  1, 2, 3,
  4, 5, 6,
  7, 8, 9
)
const v = new THREE.Vector3(1, 2, 3)

// y = m * v
const result = v.clone().applyMatrix3(m)
console.log(result) // {x: 14, y: 32, z: 50}
// {x: 1*1+2*2+3*3, y: 4*1+5*2+6*3, z: 7*1+8*2+9*3}

GLSL 示例:

mat3 m = mat3(
  1.0, 2.0, 3.0,
  4.0, 5.0, 6.0,
  7.0, 8.0, 9.0
);

vec3 v = vec3(1.0, 2.0, 3.0);
vec3 y = m * v;

常用矩阵

  • 方阵(Square Matrix):行数与列数相同,常用于线性变换、特征值、逆矩阵等。

  • 零矩阵(Zero Matrix):所有元素都为 0。

  • 单位矩阵(Identity Matrix):对角线为 1,其余为 0。

    In=[100010001]I_n = \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix}
  • 对角矩阵(Diagonal Matrix):也是缩放矩阵,仅主对角线上有元素。运算简单、常用于缩放变换。

    D=[d1000d2000d3]D = \begin{bmatrix} d_1 & 0 & 0 \\ 0 & d_2 & 0 \\ 0 & 0 & d_3 \end{bmatrix}
<< BACK TO BLOG LIST