在开始ARKit的内容之前,我们还是要补充一点关于线性代数的知识。别担心,即便你觉得自己已经忘光了,理解接下来我们要讨论的话题也完全没问题。毕竟,我们的出发点并不是严谨的讨论数学,而只是把它作为一种工具,理解一下我们要使用线性代数完成哪些工作。
向量是“方向”和“数量”的组合。在一个二维或三维空间里,一个向量,表示一个具体的位置。我们可以通过向量和整数的运算,组合而成新的位置。但是,当我们要对向量存在的空间本身进行变换的时候,该怎么办呢?
这时,我们就需要一种新的数学工具,叫做矩阵。
什么是矩阵
简单来说,矩阵看起来就是一组排列起来的向量,其中向量的维数是矩阵的行,向量的个数,叫做矩阵的列。在下面的注释中,就是一个3x3的矩阵:
///
/// | 5 6 7 |
/// | 2 4 3 |
/// | 1 9 8 |
///
但是,矩阵的行和列并不一定是相同的,并且,我们管这种行列个数相同的矩阵,叫做方阵。接下来,为了尽可能简化我们讨论的范围,我们将只基于2x2和3x3这两种方阵研究矩阵的运算。当你理解了它们的含义之后,也就不难扩展到更一般的情况了。
理解矩阵和向量的运算
首先,来看方阵M和向量v相乘的运算规则,简单来说,就是先把向量v放倒,然后依次和矩阵M的每一行中的对应元素相乘并相加,每向下处理过一层,就产生一个新的结果。因此,最终相乘的结果,还是一个向量,这个向量的维数和矩阵的行数是相等的。
LinearAlgebra
例如一个3x3的方阵乘以一个三维向量,得到的结果仍旧是一个三维向量:
///
/// | 5 6 7 | | 1 | | 1 * 5 + 2 * 6 + 3 * 7 |
/// | 2 4 3 | x | 2 | = | 1 * 2 + 2 * 4 + 3 * 3 |
/// | 1 9 8 | | 3 | | 1 * 1 + 2 * 9 + 3 * 8 |
///
要注意的是,相乘的顺序,一定是方阵在前,向量在后,我们才能能到一个新的向量。如果反过来,就变成了两个矩阵相乘,它们的含义是不同的,甚至,是不能相乘的。
理解了这个计算方式之后,你可能会想,为什么要把向量和矩阵相乘呢?直接把答案说出来,就是:
为了变换向量所在的空间
为了变换向量所在的空间
为了变换向量所在的空间
重要的事情要说三遍,那么空间变换具体的含义是什么呢?我们知道,在一个二维平面里,我们可以选取两个基向量e1和e2确定一个空间,在这个空间里,向量w就可以表示为5e1+2e2。
LinearAlgebra
现在,假设我们要换一组基向量e3 e4表达这个2维空间,在更换的过程里,我们要让一些条件保持不变:
- 原点位置不变;
- 平行线变换后仍旧是平行线;
- 直线变换后仍旧是直线;
LinearAlgebra
这样,同样是(5, 2)这个位置,在新的坐标系中,就变成了5e3+2e4,也就是说,同样的向量,在我们变换了空间之后,它就会呈现不同的样子。不难想象,利用这个特性,我们就能让图像呈现出远近、缩放等不同的视觉效果。
现在,为了量化这个变换的过程,我们把这些基向量放在一个笛卡尔坐标系中。假设,e3的坐标是(x11, y21),e4的坐标是(x12, y22),其实,我们不用管这个具体的数值是什么。这样,(5, 2)在e3 e4空间中的变换就可以写成这样:5(x11, y21) + 2(x12, y22),把这个式子做一些简单的变换就可以得到这样的结果:5x11 + 2x12 + 5y21 + 2y22,发现什么了么?如果我们把e3 e4的值写成一个2x2方阵,就更加明显了:
LinearAlgebra
没错,按照矩阵和向量乘积的运算法则,这个结果就是变换后的向量,理解了这个过程之后,我们就可以反过来理解刚才强调了三遍的结果:当我们用一个矩阵和向量相乘的时候,是为了变换向量所在的空间。
单位矩阵
接下来,我们来看一种特殊的方阵,叫做单位矩阵,这种方阵左上到右下对角线都是1,其它位置都是0。例如这样:
///
/// | 1 0 0 |
/// | 1 0 | | 0 1 0 |
/// | 0 1 | | 0 0 1 |
///
也就是说,在笛卡尔坐标系中,单位矩阵是由基底向量排列而成的矩阵。不难理解,这种矩阵和向量相乘的时候,不会带来任何变化。但通过它,我们可以方便的某个维度上对向量进行调整。
例如,要水平翻转平面的X轴,可以用:
///
/// | -1 0 0 |
/// | -1 0 | | 0 1 0 |
/// | 0 1 | | 0 0 1 |
///
要垂直翻转Y轴,可以用:
///
/// | 1 0 0 |
/// | 1 0 | | 0 -1 0 |
/// | 0 -1 | | 0 0 1 |
///
要让对象远离当前位置可以用:
///
/// | 0.5 0 0 |
/// | 0.5 0 | | 0 0.5 0 |
/// | 0 0.5 | | 0 0 0.5 |
///
如果你想不太清楚道理,试着用一个向量分别去观察下和这些方阵相乘的结果,一下子就会明白了。通过这些例子,我们不难发现,对于任意一个2x2的方阵来说,它的第一列,就是变换后(1, 0)所在的位置;而第二列,就是变换后(0, 1)所在的位置,对于3x3的方阵来说,道理也是如此。理解了这个规律,就不难想象一个矩阵对向量空间的变化了。
三维空间中的坐标轴旋转
理解了基于单位矩阵对空间进行的变换之后,我们来看个更一般情的情况,即在三维空间中按坐标轴旋转,稍后,在ARKit的例子中,我们将通过旋转坐标轴,在空间的不同位置里放置物品。例如,我们握住Z轴,让XY平面水平旋转:
LinearAlgebra
在这个过程里,由于空间的原点不变,空间的Z轴坐标也没变,因此,我们可以用一个XY平面来计算这个变换过程。假设已知点M,如何用M的坐标得到旋转β角之后N的坐标呢?我们先把它们各自的坐标写出来:
LinearAlgebra
然后,我们把cos(α+β)和rsin(α+β)展开,并做一些简单的代数运算,就可以得到用M坐标表示的N坐标:
LinearAlgebra
这样,再把XY平面放回立体空间,根据矩阵的计算规则,我们就可以得到按Z轴旋转的矩阵了:
LinearAlgebra
同理,我们也可以得到按X轴或Y轴旋转的变换矩阵。当然,我们并不需要记住这个矩阵具体的模样,稍后,我们会使用相关的函数来生成这个旋转矩阵,大家只要知道其中的道理就好了。