高级前端应该掌握的数学知识-矩阵

4,038 阅读7分钟

矩阵在图形学中非常重要,是必须要掌握的。将一些元素排列成若干行,每行放上相同数量的元素,就是一个矩阵。数学中矩阵长下面这样。

image.png

上图中是一个 m 行和 n 列(m * n)排列成的矩形阵列,在图形学中用的都是方块矩阵,也就是矩阵的行数和列数相等。在编程中我们通常将矩阵用一个普通数组或二维数组表示,就像下面这样。

const matrix1 = [
    1, 2, 3,
    1, 2, 3,
    1, 2, 3
]
const matrix2 = [
    [1, 2, 3],
    [1, 2, 3],
    [1, 2, 3]
]

本系列文章中将只使用第一种一个简单一维数组表示矩阵,因为它更加紧凑。

矩阵加法和减法

两个矩阵相加就是将两个矩阵上对应位置的数相加即可,所以必须保证两个矩阵的尺寸相等,也就是说行要一样大小,列也要一样大小。

image.png

矩阵减法和加法一样,可以看成加一个负数。

矩阵乘法

矩阵最重要就是它的乘法,首先常量和矩阵的乘法比较简单,就是用常量乘以矩阵中的每个数就行了,这里不做过多介绍。主要讲解矩阵乘以矩阵,另外矩阵是没有除法的。

矩阵和矩阵的乘法稍微复杂一点,需要用到上篇文章中介绍的点积,运算的时候需要将第一个矩阵的第一行点积第二矩阵的第一列,就像下面这样。

image.png

image.png

我们可以将第一行和第一列都看成一个上篇文章中介绍的矢量。第一个矩阵的第一行点积第二个矩阵的第一列得到的就是新矩阵的第一行和第一列的值,如果是第一个矩阵的第一行点积第二个矩阵的第二列,那么得到的就是新矩阵的第一行第二列的值。

这里的规律是新矩阵值的位置是第一个矩阵的行和第二个矩阵的列决定的。

另外由于矩阵乘法的这个特性,对两个相乘的矩阵的尺寸也是有要求的。需要两个矢量大小相等,也就是第一个矩阵的列数等于第二矩阵的行数,两个矩阵才能相乘。

上图中第一个矩阵是 2x3,第二个矩阵大小是 3x2,所以它们可以相乘,另外得到的新矩阵的行数等于第一个矩阵的行数,列数等于第二个矩阵的列数,2x33x2,我们只取两头得到的就是新矩阵的尺寸。

矩阵的乘法满足结合律和分配律,但不满足交换律,2x3 可以和 3x5 的矩阵相乘,但是 3x5 不能和 2x3 相乘因为 5 不等于 2

在图形学中我们会经常用到矩阵的结合律。

image.png

如果 y 和 z 矩阵是固定的,x 矩阵是变动的,利用结合律我们可以先将 y 和 z 矩阵相乘,这样就不用每次都重复计算了。

单位矩阵

矩阵中有一个特殊的矩阵,叫单位矩阵,它和数字中的 1 一样,任何矩阵乘上它都等于它自己。

const identity = [
    1, 0, 0,
    0, 1, 0,
    0, 0, 1
]

上面例子中是一个 3x3 的单位矩阵,我们可以发现单位矩阵从左上到右下这一条斜线上的数都是 1,其余都是 0

矩阵转置

矩阵转置是把矩阵的行和列颠倒一下,如下图所示。

image.png

原先的列变成行,原先的行变成列,转置的矩阵一般在上面加个 T。矩阵转置满足如下性质。

image.png

矩阵转置再转置等于原来的自己。

image.png

两个矩阵相加再转置,等于两个矩阵转置再相加。

image.png

两个矩阵相乘再转置,等于两个矩阵转置后再反向相乘。

矩阵的行列式

矩阵的行列式是一个可以从方形矩阵计算出来的特别的数。它可以帮助我们找到接下来要讲解的矩阵的逆矩阵。

要计算一个矩阵的行列式稍微有点复杂,

image.png

上图中 A 矩阵的行列式是 ad-bc,它是十字交叉相乘,然后将结果相加。左上到右下是正号,右上到左下是负号。

image.png

3x3 矩阵的行列式也和 2x2 差不多。

image.png

首先把第一行提出来,下面的作上面介绍到的十字交叉相乘,也就是把 3x3 分解成了 2x2,然后第一行的数乘上得到的结果,最后相加,需要注意它们的符号是正负交替的。

image.png

对于 4x4 矩阵,它和 3x3 一样,被分解成了找 3x3 矩阵的行列式。

image.png

逆矩阵

一个矩阵的逆,我们可以在矩阵上加一个 -1 来表示。

image.png

它和数字中的倒数很相似,例如 8 乘上它的倒数等于 1,矩阵乘上它的逆等于单位矩阵,如下图所示。

image.png

要计算矩阵的逆,我们首先要计算出矩阵的行列式,然后用它的倒数乘上它的伴随矩阵。

image.png

并不是所有矩阵都有逆,首先矩阵一定是方形 (行和列数目相同)才能有逆矩阵(图形学中用到都满足这点),另外还要保证矩阵的行列式不能等于 0,因为 0 不能作分母。

在图形学中逆矩阵最常用的是抵消变换,比如我左转 x 度,如果我要转回去就直接计算旋转矩阵的逆矩阵即可。

WebGL 中的矩阵

WebGL 中的矩阵和数学中的矩阵有一点不同,WebGL 中的矩阵是列主序的,也就是数学中的矩阵写成 WebGL 中的矩阵要转置一下,行和列倒一下。

另外矩阵之间的运算已经有库帮我们实现了,例如比较出名的 gl-matrix,我们只需要调用 API 即可。

不过作为教学文章,不会使用这些库,代码全部手写,下面是本篇文章中讲解的 4x4 矩阵相乘的 JS 实现。

class Mat4 {
  static multiply(a, b, out = []) {
    const a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
    const a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
    const a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
    const a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];

    let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
    out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
    out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
    out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
    out[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;

    b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7];
    out[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
    out[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
    out[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
    out[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;

    b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11];
    out[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
    out[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
    out[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
    out[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;

    b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15];
    out[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
    out[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
    out[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
    out[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
    return out;
  }
}

WebGL 中用的最多的就是 4x43x3 尺寸的矩阵,后面代码实现也只实现这两个尺寸的矩阵运算。

总结

矩阵在图形学中非常重要,本篇文章讲解了矩阵的基础知识,和图形学中是如何使用矩阵的,对矩阵有初步的了解。这篇文章讲解的矩阵知识不算多,也偏向理论比较枯燥。下篇文章我们将把这篇文章学的矩阵用起来,讲解图形学中如何实际的使用矩阵做出一些炫酷的效果。

如果觉得文章还不错欢迎点赞关注来支持鼓励作者,我会尽快更新系列教程的下一篇文章。

零基础玩转 WebGL 系列文章目录请查看:零基础玩转 WebGL - 目录