学习Shader所需的数学基础(矩阵)
在三维数学中,我们通常会使用矩阵来进行变换。一个矩阵可以把一个矢量从一个坐标空间转换到另一个坐标空间。本篇文章就对矩阵相关的数学知识做了记录总结。
数学基础之坐标系,点和矢量的相关内容可以看这篇文章
矢量和矩阵
我们可以用矩阵来表示矢量。实际上,矢量可以看成是n×1
的列矩阵或1×n
的行矩阵,其中n对应了矢量的维度。
把矢量和矩阵联系在一起的原因是为了让矢量可以像一个矩阵一样参与矩阵的运算。
矩阵的运算
矩阵和标量的乘法
矩阵和标量相乘,结果仍然是一个相同维度的矩阵
它们之间的乘法非常简单,就是矩阵的每个元素和该标量相乘。
公式
矩阵和矩阵的乘法
一个r×n
的矩阵A和一个n×c
的矩阵B相乘,它们的结果AB将会是一个r×c
大小的矩阵。这意味着,矩阵相乘,第一个矩阵的列数必须和第二个矩阵的行数相同。
公式
设两个矩阵A和B相乘的结果是矩阵C,那么,C中的每一个元素Cij等于A的第i行所对应矢量和B的第j列所对应矢量的点积
以一个简单的方式解释:对于每个元素Cij,我们找到A中的第i行和B中的第j列,然后把他们的对应元素相乘后再加起来,这个和就是Cij
性质
- 矩阵乘法不满足交换律
- 矩阵乘法满足结合律
方块矩阵
方块矩阵,简称方阵,是指那些行和列数目相等的矩阵。在三维渲染里,最常使用的就是3×3和4×4的方阵
方阵的对角元素指的是行号和列号相等的元素。
如果一个矩阵除了对角元素外的所有元素都为0,那么这个矩阵就叫做对角矩阵
一个4×4的对角矩阵:
单位矩阵
如果一个对角矩阵的对角元素都为1,那么这个矩阵被称为单位矩阵。一个3×3的单位矩阵如下所示:
任何矩阵和单位矩阵相乘的结果都还是原来的矩阵。这就跟标量中的1一样。
转置矩阵
转置矩阵实际上是通过对原矩阵进行转置运算得到的。给定一个r×c
的矩阵M,它的转置矩阵可以表示成MT,这是一个c×r
的矩阵。转置运算就是把原矩阵翻转一下,即原矩阵的第i行变成了第j列,而第j列变成了第i行。
如果一个矩阵的转置矩阵是其本身,则我们称其为对称矩阵
公式
对于行矩阵和列矩阵来说,我们可以使用转置运算来相互转换:
性质
-
矩阵转置的转置等于原矩阵
-
矩阵的串接的转置,等于反向串接各个矩阵的转置。这个性质可以扩展到更多矩阵相乘的情况
矩阵的行列式
矩阵M的行列式用|M|表示
公式
对于2×2矩阵:
对于3×3矩阵,可以展开为2×2矩阵:
同理,对于4×4矩阵,可以展开为3×3矩阵:
可以理解为对于矩阵第一行的每个元素,都乘以去除该元素所在行和列后剩下矩阵的行列式,然后把结果按照+ -+-的规律加/减起来。
逆矩阵
给定一个方阵M,它的逆矩阵用M-1来表示。逆矩阵最重要的性质就是把M和M-1相乘,那么它们的结果将会是一个单位矩阵
只有方阵才有逆矩阵,且并不是所有的方阵都有逆矩阵。
如何判断一个矩阵是否可逆:如果一个矩阵的行列式不为0,那么它就是可逆的。
例如所有元素都为0的方阵就没有逆矩阵
如果一个矩阵有对应的逆矩阵,我们就说这个矩阵是可逆的或者说是非奇异的。否则这个矩阵就是不可逆的或者说是奇异的
公式
性质
-
逆矩阵的逆矩阵是原矩阵本身
-
单位矩阵的逆矩阵是它本身
-
转置矩阵的逆矩阵是逆矩阵的转置
-
矩阵串接相乘后的逆矩阵等于反向串接各个矩阵的逆矩阵。这个性质可以扩展到更多矩阵相乘的情况
逆矩阵是有几何意义的,一个矩阵可以表示一个变换,而逆矩阵允许我们还原这个变换。假设,我们使用变换矩阵M对矢量进行了一次变换,然后再使用M的逆矩阵M-1进行另一次变换,那么我们会得到原来的矢量。
正交矩阵
如果一个方阵M和它的转置矩阵的乘积是单位矩阵的话,我们就称这个矩阵是正交的
再结合逆矩阵的公式,我们可以知道,如果一个矩阵是正交的,那么它的转置矩阵和逆矩阵是一样的
公式
在三维变换中,我们经常会使用逆矩阵来求解反向的变换。但逆矩阵的求解往往计算量很大,而如果我们可以确定这个矩阵是正交矩阵的话,就可以直接通过转置矩阵得到逆矩阵。
那么如何判断的一个矩阵是否是正交矩阵呢,当然可以通过公式计算判断,但这仍然需要一定的计算量,有时候我们更希望不通过计算,而根据一个矩阵的构造过程来判断这个矩阵是否是正交矩阵
根据正交矩阵的定义可以得到:
这样,我们就有个9个等式:
可以得到如下结论:
- 矩阵的每一行(即c1,c2,c3)都是单位矢量,因为它们与自己的点积为1
- 矩阵的每一行(即c1,c2,c3)都互相垂直,因为它们互相的点积为0(参考点积的公式|a||b|cosθ)
- 上述的两条结论对每一列也同样适用。因为M是正交矩阵的话,MT也是正交矩阵
也就说如果一个矩阵满足上面的条件,那么它就是一个正交矩阵。
行矩阵还是列矩阵
由于一个矢量既可以转换成一个行矩阵也可以转换成列矩阵,虽然它们本身是没有区别的,但当我们需要把它和另一个矩阵相乘时,就会出现差异,因为矩阵的乘法是不满足交换律的。
在Unity中,常规做法是把矢量放在矩阵的右侧,即把矢量转换成列矩阵来进行计算。此时我们的阅读顺序是从右到左的。即对矢量v先使用A进行变换,再使用B进行变换,最后使用C进行变换。
矩阵的几何意义:变换
在游戏的世界中,变换一般包含了旋转,缩放和平移。游戏开发人员希望给定一个点或矢量,再给定一个变换,就可以通过某个数学运算来求得新的点和矢量。而使用矩阵可以完美地解决这个问题。
什么是变换
变换指的是我们把一些数据,如点,方向矢量,甚至是颜色等,通过某种方式进行转换的过程。
线性变换指的是那些可以保留矢量加和标量乘的变换。用数学公式来表示这两个条件就是:
缩放就是一种线性变换,例如f(x) = 2x
,可以表示一个大小为2的统一缩放
旋转也是一种线性变换
如果我们要对一个三维的矢量进行线性变换,那么仅仅使用3×3的矩阵就可以表示所有的线性变换。
线性变换除了包括旋转和缩放外,还包括错切,镜像(反射),正交投影等。
仿射变换就是合并线性变换和平移变换的变换类型。仿射变换可以使用一个4×4的矩阵来表示。
齐次坐标
由于3×3矩阵不能表示一个平移操作,我们就将其扩展到了4×4的矩阵。为此,我们还需要把原来的三维矢量转换成四维坐标,也就是齐次坐标(齐次坐标的维度可以超过四维,但本文泛指四维齐次坐标)。
如何把一个三维矢量转换成四维矢量呢:
- 对于一个点,从三维坐标转换成齐次坐标就是把w分量设置为1
- 对于方向矢量,需要把w分量设置为0。这样当用4×4矩阵进行变换时,平移的效果会被忽略(因为方向矢量没有位置)。
基础变换矩阵
我们把表示纯平移,纯旋转和纯缩放的变换矩阵叫做基础变换矩阵
我们可以把一个基础变换矩阵分解成4个组成部分:
其中,左上角的矩阵M3×3表示旋转和缩放,T3×1表示平移,01×3是零矩阵,右下角的元素是标量1(第四行是(0 0 0 1)的原因是保证w分量保持不变)。
平移矩阵
我们可以使用矩阵乘法来表示对一个点进行平移变换,如下所示,从结果很容易看出这个矩阵为什么有平移效果:点的x, y, z分量分别增加了一个位置偏移,即把点(x, y, z)在空间中平移了(tx, ty, tz)个单位。
对一个方向矢量进行平移变换,不会对其产生任何影响:
平移矩阵的逆矩阵就是反向平移得到的矩阵。可以看出平移矩阵并不是一个正交矩阵。
缩放矩阵
我们可以使用矩阵乘法来表示一个缩放变换:
对方向矢量同样可以进行缩放:
如果缩放系数Kx = Ky = Kz,我们把这样的缩放称为统一缩放,否则称为非统一缩放。从外观上看,统一缩放是扩大整个模型,而非统一缩放会挤压或拉伸模型,改变与模型相关的角度和比例。
缩放矩阵的逆矩阵是使用原缩放矩阵系数的倒数来进行缩放,缩放矩阵一般也不是正交矩阵。
旋转矩阵
在学习三维空间下的旋转矩阵前,我们先来看一下二维空间下的旋转,向量v旋转θ度,得到v':
假设v的模为r,则旋转前v的x = r * cosΦ
,y = r * sinΦ
旋转后v'的x' = r * cos( Φ + θ )
,y' = r * sin( Φ + θ )
根据三角函数的展开公式:
可得v'的x' = x*cosθ - y*sinθ
,y' = x*sinθ + y*cosθ
,即
让我们再回到三维空间,上面的二维旋转,可以理解为在三维中绕z轴的旋转正方向进行旋转(读者可以想象在左手坐标系下,对于上图的二维旋转,z轴正方向是指向屏幕内部的,根据左手法则,绕z轴旋转的正方向就是由x到y)
因此,三维空间下的绕z轴的旋转矩阵如下所示,因为绕z轴旋转,所以z坐标应该是保持不变的。
同理,绕x轴的旋转如下所示:
绕y轴的旋转如下所示:
旋转矩阵是正交矩阵,因此旋转矩阵的逆矩阵等于其转置矩阵。而且多个旋转矩阵之间的串联同样是正交的
复合变换
我们可以把平移,旋转和缩放组合起来,形成一个复杂的变换过程。复合变换可以通过矩阵的串联来实现。例如:
由于矩阵乘法不满足交换律,因此矩阵乘法的顺序很重要。在大多数情况下,我们约定变换的顺序是先缩放,再旋转,最后平移。
除了需要注意不同类型的变换顺序外,还需要小心旋转的变换顺序。如果我们需要同时绕着3个轴进行旋转,那么应该按什么样的旋转顺序呢?
在Unity中,这个旋转顺序是zxy
旋转时使用的坐标系也有以下两种选择:
- 绕坐标系E下的z轴旋转θz,绕坐标系E下的x轴旋转θx,绕坐标系E下的y轴旋转θy
- 绕坐标系E下的z轴旋转θz后,坐标系也绕z轴旋转θz,新的坐标系记做E'。再在E'下绕x轴旋转θx,坐标系也做同样的旋转,记做E''。再在E''下,绕y轴旋转θy。即旋转的时候,坐标系也一起转动。
上述两种情况的结果是不一样的,但如果把它们的顺序颠倒一下,得到的结果就会是一样的。
对于第二种情况,yxz旋转顺序的变换矩阵是M = Mrotate_z Mrotate_x Mrotate_y,则对于第一种情况,zxy的旋转矩阵也是M = Mrotate_z Mrotate_x Mrotate_y。Untiy的zxy旋转顺序指的就是在第一种情况下旋转的顺序。