【Unity Shader入门】Shader基础概念:渲染流水线
【Unity Shader入门】Shader编程基础:ShaderLab语法
【Unity Shader入门】Shader数学基础:向量(矢量)
【Unity Shader入门】Shader数学基础:矩阵
【Unity Shader入门】Shader数学基础:矩阵变换
【Unity Shader入门】Shader编程初级:Shader结构
一个m×n的矩阵是一个由m行n列元素排列成的矩形阵列
在三维数学中,我们通常会使用矩阵来进行变换。一个矩阵可以把一个矢量从一个坐标空间转换到另一个坐标空间。
我们可以用矩阵来表示矢量。实际上,矢量可以看成是n×1的列矩阵或1×n的行矩阵,其中n对应了矢量的维度。
把矢量和矩阵联系在一起的原因是为了让矢量可以像一个矩阵一样参与矩阵的运算。
矩阵和标量相乘,结果仍然是一个相同维度的矩阵
它们之间的乘法非常简单,就是矩阵的每个元素和该标量相乘。
公式
k M = M k = k [ m 11 m 12 m 13 m 21 m 22 m 23 m 31 m 32 m 33 ] = [ k m 11 k m 12 k m 13 k m 21 k m 22 k m 23 k m 31 k m 32 k m 33 ] kM=Mk=k \begin{bmatrix} m_{11} & m_{12} & m_{13} \\ m_{21} & m_{22} & m_{23} \\ m_{31} & m_{32} & m_{33} \\ \end{bmatrix}= \begin{bmatrix} km_{11} & km_{12} & km_{13} \\ km_{21} & km_{22} & km_{23} \\ km_{31} & km_{32} & km_{33} \\ \end{bmatrix} kM=Mk=k⎣⎡m11m21m31m12m22m32m13m23m33⎦⎤=⎣⎡km11km21km31km12km22km32km13km23km33⎦⎤
一个r×n的矩阵A和一个n×c的矩阵B相乘,它们的结果AB将会是一个r×c大小的矩阵。这意味着,矩阵相乘,第一个矩阵的列数必须和第二个矩阵的行数相同。
公式
设两个矩阵A和B相乘的结果是矩阵C,那么,C中的每一个元素Cij等于A的第i行所对应矢量和B的第j列所对应矢量的点积
c i j = a i 1 b 1 j + a i 2 b 2 j + ⋯ + a i n b n j = ∑ k = 1 n a i k b k j c_{ij}=a_{i1}b_{1j} +a_{i2}b_{2j} + \cdots + a_{in}b_{nj} = \displaystyle\sum_{k=1}^n a_{ik}b_{kj} cij=ai1b1j+ai2b2j+⋯+ainbnj=k=1∑naikbkj
以一个简单的方式解释:对于每个元素Cij,我们找到A中的第i行和B中的第j列,然后把他们的对应元素相乘后再加起来,这个和就是Cij
性质
1、矩阵乘法不满足交换律
2、矩阵乘法满足结合律
A B C D = ( ( A ( B C ) ) D ) E = ( A B ) ( C D ) E ABCD=((A(BC))D)E = (AB)(CD)E ABCD=((A(BC))D)E=(AB)(CD)E
方块矩阵,简称方阵,是指那些行和列数目相等的矩阵。在三维渲染里,最常使用的就是3×3和4×4的方阵
方阵的对角元素指的是行号和列号相等的元素。
如果一个矩阵除了对角元素外的所有元素都为0,那么这个矩阵就叫做对角矩阵
一个4×4的对角矩阵:
[ 3 0 0 0 0 − 2 0 0 0 0 1 0 0 0 0 7 ] \begin{bmatrix} 3 & 0 & 0 & 0 \\ 0 & -2 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 7 \\ \end{bmatrix} ⎣⎢⎢⎡30000−20000100007⎦⎥⎥⎤
如果一个对角矩阵的对角元素都为1,那么这个矩阵被称为单位矩阵。一个3×3的单位矩阵如下所示:
I 3 = [ 1 0 0 0 1 0 0 0 1 ] I_3= \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \\ \end{bmatrix} I3=⎣⎡100010001⎦⎤
任何矩阵和单位矩阵相乘的结果都还是原来的矩阵。这就跟标量中的1一样。
M I = I M = M MI=IM=M MI=IM=M
转置矩阵实际上是通过对原矩阵进行转置运算得到的。给定一个r×c的矩阵M,它的转置矩阵可以表示成MT,这是一个c×r的矩阵。转置运算就是把原矩阵翻转一下,即原矩阵的第i行变成了第j列,而第j列变成了第i行。
如果一个矩阵的转置矩阵是其本身,则我们称其为对称矩阵
公式
M i j T = M j i M_{ij}^T=M_{ji} MijT=Mji
对于行矩阵和列矩阵来说,我们可以使用转置运算来相互转换:
[ x y z ] T = [ x y z ] \begin{bmatrix} x & y & z \\ \end{bmatrix}^T= \begin{bmatrix} x \\ y \\ z \\ \end{bmatrix} [xyz]T=⎣⎡xyz⎦⎤
[ x y z ] T = [ x y z ] \begin{bmatrix} x \\ y \\ z \\ \end{bmatrix}^T= \begin{bmatrix} x & y & z \\ \end{bmatrix} ⎣⎡xyz⎦⎤T=[xyz]
性质
矩阵转置的转置等于原矩阵
( M T ) T = M (M^T)^T=M (MT)T=M
矩阵的串接的转置,等于反向串接各个矩阵的转置。这个性质可以扩展到更多矩阵相乘的情况
( A B ) T = B T A T (AB)^T = B^TA^T (AB)T=BTAT
矩阵M的行列式用|M|表示
公式
对于2×2矩阵:
D = [ a 11 a 12 a 21 a 22 ] = a 11 a 22 − a 12 a 21 D=\begin{bmatrix} a11 & a12 \\ a21 & a22 \\ \end{bmatrix}= a11 a22 - a12 a21 D=[a11a21a12a22]=a11a22−a12a21
对于3×3矩阵,可以展开为2×2矩阵:
A = [ a b c d e f g h i ] A=\begin{bmatrix} a & b & c \\ d & e & f \\ g & h & i \\ \end{bmatrix} A=⎣⎡adgbehcfi⎦⎤
∣ A ∣ = a ⋅ [ e f h i ] − b ⋅ [ d f g i ] + c ⋅ [ d e g h ] |A|=a\sdot \begin{bmatrix} e & f \\ h & i \\ \end{bmatrix}- b \sdot\begin{bmatrix} d & f \\ g & i \\ \end{bmatrix}+ c \sdot\begin{bmatrix} d & e \\ g & h \\ \end{bmatrix} ∣A∣=a⋅[ehfi]−b⋅[dgfi]+c⋅[dgeh]
同理,对于4×4矩阵,可以展开为3×3矩阵:
A = [ a b c d e f g h i j k l m n o p ] A=\begin{bmatrix} a & b & c & d \\ e & f & g & h \\ i & j & k & l \\ m & n & o & p \\ \end{bmatrix} A=⎣⎢⎢⎡aeimbfjncgkodhlp⎦⎥⎥⎤
∣ A ∣ = a ⋅ [ f g h j i l n o p ] − b ⋅ [ e g h i k l m o p ] + c ⋅ [ e f h i j l m n p ] − d ⋅ [ e f g i j k m n o ] |A|=a\sdot \begin{bmatrix} f & g & h \\ j & i & l \\ n & o & p \\ \end{bmatrix}- b \sdot\begin{bmatrix} e & g & h \\ i & k & l \\ m & o & p \\ \end{bmatrix}+ c \sdot\begin{bmatrix} e & f & h \\ i & j & l \\ m & n & p \\ \end{bmatrix} - d \sdot\begin{bmatrix} e & f & g \\ i & j & k \\ m & n & o \\ \end{bmatrix} ∣A∣=a⋅⎣⎡fjngiohlp⎦⎤−b⋅⎣⎡eimgkohlp⎦⎤+c⋅⎣⎡eimfjnhlp⎦⎤−d⋅⎣⎡eimfjngko⎦⎤
可以理解为对于矩阵第一行的每个元素,都乘以去除该元素所在行和列后剩下矩阵的行列式,然后把结果按照+ -+-的规律加/减起来。
给定一个方阵M,它的逆矩阵用M-1来表示。逆矩阵最重要的性质就是把M和M-1相乘,那么它们的结果将会是一个单位矩阵
只有方阵才有逆矩阵,且并不是所有的方阵都有逆矩阵。
如何判断一个矩阵是否可逆:如果一个矩阵的行列式不为0,那么它就是可逆的。
例如所有元素都为0的方阵就没有逆矩阵
如果一个矩阵有对应的逆矩阵,我们就说这个矩阵是可逆的或者说是非奇异的。否则这个矩阵就是不可逆的或者说是奇异的
公式
M M − 1 = M − 1 M = I MM^{-1} = M^{-1} M=I MM−1=M−1M=I
性质
逆矩阵的逆矩阵是原矩阵本身
( M − 1 ) − 1 = M (M^{-1})^{-1}=M (M−1)−1=M
单位矩阵的逆矩阵是它本身
I − 1 = I I^{-1}=I I−1=I
转置矩阵的逆矩阵是逆矩阵的转置
( M T ) − 1 = ( M − 1 ) T (M^T)^{-1}=(M^{-1})^T (MT)−1=(M−1)T
矩阵串接相乘后的逆矩阵等于反向串接各个矩阵的逆矩阵。这个性质可以扩展到更多矩阵相乘的情况
( A B C D ) − 1 = D − 1 C − 1 B − 1 A − 1 (ABCD)^{-1} = D^{-1} C^{-1} B^{-1} A^{-1} (ABCD)−1=D−1C−1B−1A−1
逆矩阵是有几何意义的,一个矩阵可以表示一个变换,而逆矩阵允许我们还原这个变换。假设,我们使用变换矩阵M对矢量进行了一次变换,然后再使用M的逆矩阵M-1进行另一次变换,那么我们会得到原来的矢量。
如果一个方阵M和它的转置矩阵的乘积是单位矩阵的话,我们就称这个矩阵是正交的
再结合逆矩阵的公式,我们可以知道,如果一个矩阵是正交的,那么它的转置矩阵和逆矩阵是一样的
公式
M M T = M T M = I MM^T = M^T M=I MMT=MTM=I
M T = M − 1 M^T = M^{-1} MT=M−1
在三维变换中,我们经常会使用逆矩阵来求解反向的变换。但逆矩阵的求解往往计算量很大,而如果我们可以确定这个矩阵是正交矩阵的话,就可以直接通过转置矩阵得到逆矩阵。
那么如何判断的一个矩阵是否是正交矩阵呢,当然可以通过公式计算判断,但这仍然需要一定的计算量,有时候我们更希望不通过计算,而根据一个矩阵的构造过程来判断这个矩阵是否是正交矩阵
根据正交矩阵的定义可以得到:
M T M = [ − c 1 − − c 2 − − c 3 − ] [ ∣ ∣ ∣ c 1 c 2 c 3 ∣ ∣ ∣ ] M^TM= \begin{bmatrix} -& c1 & - \\ -& c2 & - \\ -& c3 & - \\ \end{bmatrix} \begin{bmatrix} |& | & | \\ c1& c2& c3 \\ |& | & | \\ \end{bmatrix} MTM=⎣⎡−−−c1c2c3−−−⎦⎤⎣⎡∣c1∣∣c2∣∣c3∣⎦⎤
= [ c 1 ⋅ c 1 c 1 ⋅ c 2 c 1 ⋅ c 3 c 2 ⋅ c 1 c 2 ⋅ c 2 c 2 ⋅ c 3 c 3 ⋅ c 1 c 3 ⋅ c 2 c 3 ⋅ c 3 ] = [ 1 0 0 0 1 0 0 0 1 ] = I =\begin{bmatrix} c1\sdot c1 & c1\sdot c2 & c1\sdot c3 \\ c2\sdot c1 & c2\sdot c2 & c2\sdot c3 \\ c3\sdot c1 & c3\sdot c2 & c3\sdot c3\\ \end{bmatrix}= \begin{bmatrix} 1& 0 &0 \\ 0&1& 0 \\ 0& 0& 1\\ \end{bmatrix} =I =⎣⎡c1⋅c1c2⋅c1c3⋅c1c1⋅c2c2⋅c2c3⋅c2c1⋅c3c2⋅c3c3⋅c3⎦⎤=⎣⎡100010001⎦⎤=I
这样,我们就有个9个等式:
c 1 ⋅ c 1 = 1 c 1 ⋅ c 2 = 0 c 1 ⋅ c 3 = 0 c 2 ⋅ c 1 = 0 c 2 ⋅ c 2 = 1 c 2 ⋅ c 3 = 0 c 2 ⋅ c 1 = 0 c 2 ⋅ c 2 = 1 c 2 ⋅ c 3 = 0 \begin{matrix} c1\sdot c1 =1 & c1\sdot c2 =0 & c1\sdot c3 =0 \\ c2\sdot c1 =0 &c2\sdot c2 =1 &c2\sdot c3 =0 \\ c2\sdot c1 =0 & c2\sdot c2 =1 &c2\sdot c3 =0\\ \end{matrix} c1⋅c1=1c2⋅c1=0c2⋅c1=0c1⋅c2=0c2⋅c2=1c2⋅c2=1c1⋅c3=0c2⋅c3=0c2⋅c3=0
可以得到如下结论:
1、矩阵的每一行(即c1,c2,c3)都是单位矢量,因为它们与自己的点积为1
2、矩阵的每一行(即c1,c2,c3)都互相垂直,因为它们互相的点积为0(参考点积的公式|a||b|cosθ)
3、上述的两条结论对每一列也同样适用。因为M是正交矩阵的话,MT也是正交矩阵
也就说如果一个矩阵满足上面的条件,那么它就是一个正交矩阵。
由于一个矢量既可以转换成一个行矩阵也可以转换成列矩阵,虽然它们本身是没有区别的,但当需要把它和另一个矩阵相乘时,就会出现差异,因为矩阵的乘法是不满足交换律的。
假设有一个矢量v=(x,y,z),转换成行列矩阵:
行 矩 阵 : v = [ v x v y v z ] 列 矩 阵 : v = [ v x v y v z ] 行矩阵:v=\begin{bmatrix} v_x & v_y & v_z \\ \end{bmatrix} 列矩阵:v=\begin{bmatrix} v_x \\ v_y \\ v_z \\ \end{bmatrix} 行矩阵:v=[vxvyvz]列矩阵:v=⎣⎡vxvyvz⎦⎤
现在,有另一个矩阵M:
M = [ m 11 m 12 m 13 m 21 m 22 m 23 m 31 m 32 m 33 ] M=\begin{bmatrix} m_{11} & m_{12} & m_{13} \\ m_{21} & m_{22} & m_{23} \\ m_{31} & m_{32} & m_{33} \\ \end{bmatrix} M=⎣⎡m11m21m31m12m22m32m13m23m33⎦⎤
行矩阵和M矩阵相乘:
v M = [ x m 11 + y m 21 + z m 31 x m 12 + y m 22 + z m 32 x m 13 + y m 23 + z m 33 ] vM=\begin{bmatrix} xm_{11} + ym_{21} + zm_{31} &xm_{12} + ym_{22} + zm_{32} & xm_{13} + ym_{23} + zm_{33} \\ \end{bmatrix} vM=[xm11+ym21+zm31xm12+ym22+zm32xm13+ym23+zm33]
列矩阵和M矩阵相乘:
M v = [ x m 11 + y m 12 + z m 13 x m 21 + y m 22 + z m 23 x m 31 + y m 32 + z m 33 ] Mv=\begin{bmatrix} xm_{11} + ym_{12} + zm_{13} \\ xm_{21} + ym_{22} + zm_{23} \\ xm_{31} + ym_{32} + zm_{33} \\ \end{bmatrix} Mv=⎣⎡xm11+ym12+zm13xm21+ym22+zm23xm31+ym32+zm33⎦⎤
对比就会发现,结果矩阵除了行列矩阵的区别外,里面的元素也是不一样的。这就意味着, 在和矩阵相乘时选择行矩阵还是列矩阵来表示矢量是非常重要的,因为这决定了矩阵乘法的书写次序和结果值。
在Unity中,常规做法是把矢量放在矩阵的右侧,即把矢量转换成列矩阵来进行计算。此时我们的阅读顺序是从右到左的。即对矢量v先使用A进行变换,再使用B进行变换,最后使用C进行变换。
C B A v = ( C ( B ( A v ) ) ) CBA_v=(C(B(A_v))) CBAv=(C(B(Av)))
v A T B T C T = ( ( ( v A T ) B T ) C T ) _vA^TB^TC^T=(((_vA^T)B^T)C^T) vATBTCT=(((vAT)BT)CT)