6.1 2D 线性变换
缩放变换 是最基本的变换,可以改变向量的长度和方向。
错切 变换,在一个轴向上根据另一个轴上的值以一定比例移动,看图很容易明白。沿 轴和沿 轴上的错切矩阵分别如下:
沿 轴的错切也可以理解是沿 轴顺时针旋转 (与 轴的夹角):
沿 轴的错切也可以理解是沿 轴逆时针旋转 (与 轴的夹角):
旋转变换,证明过程 略,逆时针旋转 的矩阵为:
顺时针旋转 的矩阵为(其实就是把所有的 换成 ):
可以看到旋转矩阵是 正交矩阵,并且有两对正交向量,一对是第一行和第二行向量;一对是第一列和第二列向量。每一行会被带给标准基向量(),标准基向量会被带给每一列。简单来说就是,对标准基向量进行一个 旋转变换,就会变成 的列向量,看下图的 点,变换后为 ,就是 的第二列(因为相当于是 ,所以是第二列);对 的每一行做 的旋转变换,结果就是标准基向量,看下面的,变换后为。
反射变换 是一种使用负数的缩放变换,下面分别是关于 轴的反射变换矩阵,和关于 轴的反射变换矩阵:
有人会认为矩阵的左上角和右下角都为 时,也是一种反射变换(关于原点的反射),但实际上那只是 的旋转变换而已。
我们经常需要对一个图案进行多种变换,这些变换可以通过将他们的变换矩阵连乘进行 合并,比如需要先对 进行 变换得到 ,然后再对 进行 变换得到 ,那么有以下转换:
也就是说我们可以把所有需要用到的变换,从右往左 依次乘起来,在读变换的时候也要注意是 从右往左,比如 是先应用 变换,再应用 变换。
有时候我们也需要将一个组合的变换进行 分解,所有的 2D 矩阵都可以通过 奇异值分解(SVD) 成为旋转、缩放、旋转的形式。下图是把一个错切变换进行 SVD 的过程:
我们先从 对称矩阵 开始,对称矩阵可以通过特征值分解成 的形式。
- 是正交矩阵,我们称他的列为
- 是对角矩阵,从左上到右下依次为
(这一块不明白的可以看 这里)
我们可以把 当做是一次旋转,把 当成是一次缩放,那么其实就是一个组合变换,请结合下面的文字看图:
- 将 旋转到跟标准基坐标对齐 ()
- 在标准基坐标上进行缩放 ()
- 旋转回原来的角度 ()
我们可以发现对于 对称矩阵 来说,变换的表现就是沿着一对基向量进行非均匀缩放,这一对基向量依然是正交的,跟标准基向量没什么不同,只是整体方向不一样。而这一对基向量正是 对称矩阵 的特征向量。
看一个例子:
对应的结果如下图,实际上是在以 特征向量为轴,并进行了大小为 特征值 的 缩放 的过程。可以看出对称矩阵有 三个自由度,一个旋转,两个缩放。
对于非对称矩阵,我们可以使用 奇异值分解(SVD),对此有疑问的可以看 这里。他与特征值分解基本一样,不同的是左右旋转矩阵不再是同一个,而是分为两个,特征值分解的结果是,而奇异值分解 写成 ,这里的 依然是一个正交矩阵,列是 左奇异向量,也是一个正交向量,列是 右奇异向量,S依然是对角矩阵,对角线上是奇异值。
可以看到,奇异值分解的几何意义跟特征值分解相似,但变换的角度不再是一样的。
前面提过对于非对称矩阵 奇异值是对称矩阵 的平方根,但是我们默认是取正的,这是因为取负的是没有必要的,因为我们可以 随时反转某个奇异值的符合和他对应的奇异向量的方向,结果是一样的,从图形中来看就是带着坐标轴一起反转了,所以是一样的。
对于 的矩阵,有四个自由度,两个缩放,两个旋转,还有一个反射,但这个包含在旋转里面了。因为旋转矩阵里面也包含了反射,当行列式结果为 时为反射,当结果为 时为旋转。当实际是反射,但期望得到旋转的时候,可以将其中一个奇异值取负来把它变成旋转矩阵,这样的话,反射就不是跟随这旋转,而是跟随着缩放了。结果依然是 旋转——缩放——旋转 的形式。
还有一种旋转的分解叫做 Paeth 分解,这是将旋转分解成 错切 的方式,最大的好处就是不会在图像中出现间隙,下面是分解公式:
举个例子,如果要旋转 45°,那么写作:
分解的过程如下:
6.3 3D 线性变换
顺着笛卡尔坐标系的 缩放 矩阵为:
绕着 z 轴,x轴,y轴 逆时针 旋转 的矩阵分别为:
注意围绕 y 轴的时候有点不一样,这个我的理解是左上角的 4 个逐渐向右下角移动,当超出边界的时候,右边超出的列往最左边补,下面超出的行往最上面补。因为 x-y-z-x-y-z-x-... 其实是循环的。
也可以在特定轴上进行 错切 变换,下面是在 x 轴上进行错切,沿着哪一个轴,就是在哪个轴上值不变。
在 3D 中,跟 2D 一样,可以将对称矩阵进行特征值分解,对非对称矩阵进行奇异值分解。
如何将一个点绕着 3D 中任意轴进行旋转呢?假设该点为 ,旋转轴为 (这里的 是起点为原点的向量,所以不涉及平移),那么如果能构建一组以 为 轴的基向量,并把 转换到该空间,进行旋转,再转回来就可以了。
第一步:构建以 为 的一组正交基
首先我们将 进行归一化得到 :
再找一个跟 不同,并且也不是很接近的向量 ,比较简单的一个办法就是直接将 的最小值改成 1。然后使用叉乘找到第二个正交基 :
最后再次使用叉乘得到第三个正交基:
我们只关心 ,剩下两个并不重要,只需要他们存在就够了。
第二步:构建空间转换矩阵
假设我们有一组基向量,,还有一个点 ,将 按行来摆好,写成 我们可以提前知道 在上述基向量中的坐标应该为 ,正是 的结果。下面是书上的证明过程,有兴趣的可以看一下,没兴趣就记着我们可以用任意的一组正交基,把 三个分量摆第一行,把 三个分量摆第二行,把 三个分量摆第三行,总而构建出转换到该空间下的转换矩阵。并且再次乘其转置,就可以再次变回来。
第三步:实现
也就是按上面所说的,先转换空间(从右往左看),再绕着转换后空间内的 轴,实际上(也就是在一开始的标准基向量空间)是 向量的旋转我们期望的 度,最后再转换回一开始的空间。注意转换空间的意思只是坐标改变了,但世界上位置不变,就像步骤一里面的 一样,他一直都在 的位置,只不过在另一个空间的坐标为 ,我们只是让它在这两个坐标之间做转换。
上面所说的变换都是方向或者点,还有一种特殊的向量叫做 法线,如果图形应用的变换矩阵我们称为 ,那么 经过 变换之后不再垂于表面,但切线 依然与表面相切。期望求出一个矩阵 ,使得 ,其中 。
下面是证明过程,有兴趣的可以看一下,没兴趣的可以直接跳过图片看结论。
我们并不关心法线的长度,只需要知道它的方向,所以在知道了 之后,按照求逆的公式 ,我们可以把 去掉,所以:
进一步就是:
6.3 仿射变换和齐次坐标
以上所有的变换都是 线性变换(原点不变,且直线变换后依然是直线),而 平移 变换并不满足。我们把一个线性变换 + 一次平移的操作叫做 仿射变换,通过增加一个维度的方式来实现,叫做 齐次坐标。
我们把一个 2D 中的点 写成 ,把 的矩阵写成:
这样应用变换就将写成:
也就巧妙在不改变线性变换的计算时,加入了平移变换。当我们 不想进行平移操作,只想进行线性变换的时候,对于不是位置的向量,比如说偏移,或者方向,确实也不应该在改变物体的时候进行平移。那可以 中的 变成 。并且在变换之后, 依然是 ,也就是方向向量依然是方向向量。(女少)
我们可以把任意多个线性变换和平移变换组合成一个矩阵,在 3D 中也一样,增加一个维度:
看一个例子,下图和式子是表示的同一个过程:
另一种类似的、在 3D 中的变换为,将一个盒子 映射到 。矩阵为:
注意,我们 先 应用一个线性变换,然后再 进行平移,得到的矩阵还是可以很清楚的把线性和平移分离开来,如下面的式子所示。
变换中一个比较重要的类叫做 刚体,他们只有旋转和平移组成,没有拉伸或者缩放。
6.4 变换的逆
我们知道了矩阵的几何意义之后,可以通过几何意义来进行逆操作,比如 的逆就是 ;比如旋转矩阵的逆就是角度变成相反的符号;平移矩阵的逆就是相反的方向。如果我们有一系列的变换 ,那么逆操作就是 。
不过,有些矩阵在代数上也是很好求的,比如说对于缩放矩阵,他是对角矩阵;第二重要的是旋转矩阵,他是正交矩阵,逆就是它的转置。所以使得求旋转和缸体的逆操作都变得简单。当然我们也需要知道,求完逆之后,原矩阵最下面一行不要动,比如是 的话,那逆的最下面一行也是 。
有趣的是,我们也可以使用奇异值分解来求逆,我们知道可以将一个矩阵分解成旋转——缩放——旋转的方式:
那么按照上面说的,这个矩阵的逆就是:
6.5 坐标转换
通常我们会在场景中有一个全局坐标系(世界坐标系),图中的 ,图中有另外一个坐标系 ,还有一个点 。
在 坐标系中, ,这是简写,完整的写法是:
在 坐标系中, ,这是简写,完整的写法是:
我们在存储的时候也只需要存简写的两个数字而已, 这些都存在心里,不会被写出来。如果我们将这种坐标系转换用矩阵表示出来,就是:
我们只是将 按顺序放在列中,并在最底下写上 。也就是以下形式:
反过来的矩阵为:
也就是:
注意 并没有什么特别的,只是我们习惯性将它看成是标准的,所以你完全可以把 到 的矩阵写成:
在 3D 中,也是类似的情况: