【Unity Shader入門】Shader數學基礎:矩陣變換

【Unity Shader入門】Shader基礎概念:渲染流水線
【Unity Shader入門】Shader編程基礎:ShaderLab語法
【Unity Shader入門】Shader數學基礎:向量(矢量)
【Unity Shader入門】Shader數學基礎:矩陣
【Unity Shader入門】Shader數學基礎:矩陣變換
【Unity Shader入門】Shader編程初級:Shader結構

矩陣變換

變換:是把一些數據(點、矢量、顏色)通過某種方式進行轉換的過程。
線性變換:是指可以保留矢量加和標量乘的變換。其數學公式是:
f(x)+f(y)=f(x+y) f(x) + f(y) = f(x+y)
kf(x)=f(kx) kf(x) = f(kx)

線性變換都包括:旋轉、縮放、錯切(Shear)、鏡像(Mirroring)、正交投影(orthographic projection)。
仿射變換(affine transform)可以合併線性變換平移變換,將仿射變換擴展到4維空間下用4x4的矩陣來表示,它就齊次座標空間

齊次座標空間

由於3×3矩陣不能表示一個平移操作,就將其擴展到了4×4的矩陣。爲此,還需要把原來的三維矢量轉換成四維座標,也就是齊次座標(齊次座標的維度可以超過四維,但本文泛指四維齊次座標)。
如何把一個三維矢量轉換成四維矢量呢:
1、對於一個點,從三維座標轉換成齊次座標就是把w分量設置爲1
2、對於方向矢量,需要把w分量設置爲0。這樣當用4×4矩陣進行變換時,平移的效果會被忽略(因爲方向矢量沒有位置)。

基礎變換矩陣

可以使用一個4x4的矩陣來表示平移、旋轉和縮放。把表示純平移、純旋轉和純縮放的變換矩陣叫做基礎變換矩陣。這些矩陣具有一些共同點,可以把一個基礎變換矩陣分解成4 個組成部分:
[M3×3t3×101×31] \begin{bmatrix} M_{3×3} & t_{3×1} \\ 0_{1×3} & 1 \\ \end{bmatrix}

平移矩陣

我們可以使用矩陣乘法來表示對一個點進行平移變換,如下所示,從結果很容易看出這個矩陣爲什麼有平移效果:點的x, y, z分量分別增加了一個位置偏移,即把點(x, y, z)在空間中平移了(tx, ty, tz)個單位。
[100tx010ty001tz0001][xyz1]=[x+txy+tyz+tz1] \begin{bmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 & 1 & t_z \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \\ \end{bmatrix} = \begin{bmatrix} x + t_x \\ y + t_y \\ z + t_z \\ 1 \\ \end{bmatrix}

對一個方向矢量進行平移變換,不會對其產生任何影響:
[100tx010ty001tz0001][xyz0]=[xyz0] \begin{bmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 & 1 & t_z \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 0 \\ \end{bmatrix} = \begin{bmatrix} x \\ y \\ z \\ 0 \\ \end{bmatrix}

平移矩陣的逆矩陣就是反向平移得到的矩陣。可以看出平移矩陣並不是一個正交矩陣。
[100tx010ty001tz0001] \begin{bmatrix} 1 & 0 & 0 & -t_x \\ 0 & 1 & 0 & -t_y \\ 0 & 0 & 1 & -t_z \\ 0 & 0 & 0 & 1 \\ \end{bmatrix}

縮放矩陣

我們可以使用矩陣乘法來表示一個縮放變換:
[kx0000ky0000kz00001][xyz1]=[kxxkyykzz1] \begin{bmatrix} k_x & 0 & 0 & 0 \\ 0 & k_y & 0 & 0 \\ 0 & 0 & k_z & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \\ \end{bmatrix} = \begin{bmatrix} k_xx \\ k_yy \\ k_zz \\ 1 \\ \end{bmatrix}
對方向矢量同樣可以進行縮放:
[kx0000ky0000kz00001][xyz0]=[kxxkyykzz0] \begin{bmatrix} k_x & 0 & 0 & 0 \\ 0 & k_y & 0 & 0 \\ 0 & 0 & k_z & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 0 \\ \end{bmatrix} = \begin{bmatrix} k_xx \\ k_yy \\ k_zz \\ 0 \\ \end{bmatrix}
如果縮放係數Kx = Ky = Kz,我們把這樣的縮放稱爲統一縮放,否則稱爲非統一縮放。從外觀上看,統一縮放是擴大整個模型,而非統一縮放會拉伸或擠壓模型。更重要的是,統一縮放不會改變角度和比例信息,而非統一縮放會改變與模型相關的角度和比例。例如在對法線進行變換時,如果存在非統一縮放,直接使用用於變換頂點的變換矩陣的話,就會得到錯誤的結果。

縮放矩陣的逆矩陣是使用原縮放矩陣係數的倒數來進行縮放,縮放矩陣一般也不是正交矩陣。

[1kx00001ky00001kz00001] \begin{bmatrix} \frac 1 { k_x} & 0 & 0 & 0 \\ 0 & \frac 1 { k_y} & 0 & 0 \\ 0 & 0 & \frac 1 { k_z} & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix}
上面的矩陣只適用於沿座標軸方向進行縮放。如果我們希望在任意方向上進行縮放,就需要使用一個複合變換。其中一種方法的主要思想就是,先將縮放軸變換成標準座標軸,然後進行沿座標軸的縮放,再使用逆變換得到原來的縮放軸朝向。

旋轉矩陣

旋轉是三種常見的變換矩陣中最複雜的一種。我們知道,旋轉操作需要指定一個旋轉軸,這個旋轉軸不一定是空間中的座標軸,但這裏旋轉就是指繞着空間中的x 軸, y 軸或z 軸進行旋轉。
如果我們需要把點繞着x 軸旋轉θ度,可以使用下面的矩陣:
Rx(θ)=[10000cosθsinθ00sinθcosθ00001] R_{x}(\theta)= \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos \theta & -\sin \theta & 0 \\ 0 & \sin \theta & \cos \theta & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix}
同理,繞y 軸的旋轉如下所示:
Ry(θ)=[cosθ0sinθ00100sinθ0cosθ00001] R_{y}(\theta)= \begin{bmatrix} \cos \theta & 0 & \sin \theta & 0 \\ 0 & 1 & 0 & 0 \\ -\sin \theta & 0 & \cos \theta & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix}
繞z 軸的旋轉如下所示:
Rz(θ)=[cosθsinθ00sinθcosθ0000100001] R_{z}(\theta)= \begin{bmatrix} \cos \theta & \sin \theta & 0 & 0 \\ \sin \theta & \cos \theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix}

旋轉矩陣的逆矩陣是旋轉相反角度得到的變換矩陣。旋轉矩陣是正交矩陣,而且多個旋轉矩陣之間的串聯同樣是正交的。

複合變換

我們可以把平移、旋轉和縮放組合起來,來形成一個複雜的變換過程。
Pnew=MtranslationMrotationMscalθPold 複合變換公式:P_{new} = M_{translation}M_{rotation}M_{scal\theta}P_{old}
由於上面我們使用的是列矩陣,因此閱讀順序是從右到左,即先進行縮放變換,再進行旋轉變換,最後進行平移變換。需要注意的是,變換的結果是依賴於變換順序的,由於矩陣乘法不滿足交換律,因此矩陣的乘法順序很重要。想象一下,如果讓讀者向前一步然後左轉,記住此時的位置。然後回到原位,這次先左轉再向前走一步,得到的位置和上一次是不一樣的。究其本質,是因爲矩陣的乘法不滿足交換律,因此不同的乘法順序得到的結果是不一樣的
在絕大多數情況下,我們約定變換的順序就是先縮放,再旋轉,最後平移。

對比不同變換順序產生的變換矩陣的表達式。如果只考慮對y 軸的旋轉的話,按先縮放、再旋轉、最後平移這樣的順序組合3 種變換得到的變換矩陣是:
MrotationMscalθMtranslation=[cosθ0sinθ00100sinθ0cosθ00001][kx0000ky0000kz00001][100tx010ty001tz0001] M_{rotation}M_{scal\theta}M_{translation}= \begin{bmatrix} \cos \theta & 0 & \sin \theta & 0 \\ 0 & 1 & 0 & 0 \\ -\sin \theta & 0 & \cos \theta & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} k_x & 0 & 0 & 0 \\ 0 & k_y & 0 & 0 \\ 0 & 0 & k_z & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 & 1 & t_z \\ 0 & 0 & 0 & 1 \\ \end{bmatrix}
=[kxcosθ0kxsinθtxkxcosθ+tzkzsinθ0ky0txkxkxsinθ0kxcosθtxkxsinθ+tzkzcosθ0001] =\begin{bmatrix} k_x\cos \theta & 0 & k_x\sin \theta & t_xk_x\cos \theta + t_zk_z\sin \theta \\ 0 & k_y & 0 & t_xk_x \\ -k_x\sin \theta & 0 & k_x\cos \theta & -t_xk_x\sin \theta + t_zk_z\cos \theta \\ 0 & 0 & 0 & 1 \\ \end{bmatrix}
而如果使用了其他變換順序,例如先平移,再縮放,最後旋轉,那麼得到的變換矩陣是:
MtranslationMrotationMscalθ=[100tx010ty001tz0001][cosθ0sinθ00100sinθ0cosθ00001][kx0000ky0000kz00001] M_{translation}M_{rotation}M_{scal\theta}= \begin{bmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 & 1 & t_z \\ 0 & 0 & 0 & 1 \\ \end{bmatrix}\begin{bmatrix} \cos \theta & 0 & \sin \theta & 0 \\ 0 & 1 & 0 & 0 \\ -\sin \theta & 0 & \cos \theta & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} k_x & 0 & 0 & 0 \\ 0 & k_y & 0 & 0 \\ 0 & 0 & k_z & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix}
=[kxcosθ0kxsinθtx0ky0tykxsinθ0kzcosθtz0001] =\begin{bmatrix} k_x\cos \theta & 0 & k_x\sin \theta & t_x \\ 0 & k_y & 0 & t_y \\ -k_x\sin \theta & 0 & k_z\cos \theta & -t_z \\ 0 & 0 & 0 & 1 \\ \end{bmatrix}
從兩個結果可以看出,得到的變換矩陣是不一樣的。
除了需要注意不同類型的變換順序外,有時還需要小心旋轉的變換順序。如果我們需要同時繞着3個軸進行旋轉,那麼應該按什麼樣的旋轉順序呢?
當直接給出( θx, θy, θz)這樣的旋轉角度時,需要定義一個旋轉順序。在Unity 中,這個旋轉順序是zxy,這在旋轉相關的API 文檔中都有說明。這意味着,當給定(θx, θy, θz)這樣的旋轉角度時,得到的組合旋轉變換矩陣是:
MrotatθzMrotatθxMrotatθy=[cosθsinθ00sinθcosθ0000100001][10000cosθsinθ00sinθcosθ00001][cosθ0sinθ00100sinθ0cosθ00001] M_{rotat\theta z}M_{rotat\theta x}M_{rotat\theta y}= \begin{bmatrix} \cos \theta & \sin \theta & 0 & 0 \\ \sin \theta & \cos \theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos \theta & -\sin \theta & 0 \\ 0 & \sin \theta & \cos \theta & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} \cos \theta & 0 & \sin \theta & 0 \\ 0 & 1 & 0 & 0 \\ -\sin \theta & 0 & \cos \theta & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix}
旋轉時使用的座標系也有以下兩種選擇:
1、繞座標系E下的z 軸旋轉θz,繞座標系E 下的y 軸旋轉θy,繞座標系E 下的x 軸旋轉θx,即進行一次旋轉時不一起旋轉當前座標系。
2、繞座標系E下的z 軸旋轉θz,在座標系E 下在繞z 軸旋轉θz 後的新座標系E’下的y 軸旋轉θy , 在座標系E’下再繞y 軸旋轉θy 後的新座標系E”下的x 軸旋轉θx,即在旋轉時,把座標系一起轉動。

很容易知道,這兩種選擇的結果是不一樣的。但如果把它們的旋轉順序顛倒一下,它們得到的結果就會是一樣的!說得明白點,在第一種情況下,按zxy 順序旋轉和在第二種情況下,按yxz順序旋轉是一樣的。而Unity 文檔中說明的旋轉順序指的是在第一種情況下的順序。
和上面不同類型的變換順序導致的問題類似,不同的旋轉順序得到的結果也可能是不一樣的。同樣可以通過對比不同旋轉順序得到的變換矩陣來理解爲什麼會出現這樣的不同。

錯切矩陣

切變是座標系的變換,非均勻的拉伸。切變時候,角度變化,但是面積或體積不變。也可以理解爲座標軸間的角度變化,造成的扭曲。

錯切矩陣的逆矩陣可以通過取負來取得
(Hij)1(s)=Hij(s) (H_{ij})-1(s)= Hij(-s)

如下圖,這是x座標根據y座標的切變,機器人的y座標沒有變化,只有x座標變化了,變化後的座標x1理解爲將y座標乘以切變因子s與原座標x的和:x1 = x + sy。如果是3D則增加z座標的切變因子t: x1 = x + sy,y1 = y + tz

在這裏插入圖片描述

2D中切變矩陣爲:
Hx(s)=[10s1] H_x(s)= \begin{bmatrix} 1 & 0\\ s & 1\\ \end{bmatrix}
Hy(s)=[1s01] H_y(s)= \begin{bmatrix} 1 & s\\ 0 & 1\\ \end{bmatrix}
在3D中,同樣的道理,如下三個矩陣,分別是隨着z增大,x和y發生切變。隨着y增大,x和z發生切變。隨着z增大,x和y發生切變。
Hxy(s,t)=[100010st01] H_{xy}(s,t)= \begin{bmatrix} 1 & 0 &0 \\ 0 & 1 &0 \\ s & t &01 \\ \end{bmatrix}
Hxz(s,t)=[100s1t0001] H_{xz}(s,t)= \begin{bmatrix} 1 & 0 &0 \\ s & 1 &t\\ 0 & 0&01 \\ \end{bmatrix}
Hyz(s,t)=[1st0100001] H_{yz}(s,t)= \begin{bmatrix} 1 & s &t \\ 0 & 1 &0\\ 0 & 0&01 \\ \end{bmatrix}

鏡像矩陣

鏡像矩陣也叫做反射,與正交投影相似,正交投影將縮放值設爲0,而鏡像則設爲-1.

在這裏插入圖片描述

R是reflect(反射)的縮寫。2D:
R(n^)=S(n^,1)=[1+(11)nx2(11)nxny(11)nxny1+(11)ny2]=[1nx22nxny2nxny12ny2] R(\hat n)= S(\hat n,-1)= \begin{bmatrix} 1+(-1-1)n_x^2&(-1-1)n_xn_y \\ (-1-1)n_xn_y&1+(-1-1)n_y^2 \\ \end{bmatrix}= \begin{bmatrix} 1-n_x^2&-2n_xn_y \\ -2n_xn_y&1-2n_y^2 \\ \end{bmatrix}

3D:
R(n^)=S(n^,1)=[1+(11)nx2(11)nxny(11)nxnz(11)nxny1+(11)ny2(11)nynz(11)nxnz(11)nynz1+(11)nz2]=[12nx22nxny2nxnz2nxny12ny22nynz2nxnz2nynz12nz2] R(\hat n)= S(\hat n,-1)= \begin{bmatrix} 1+(-1-1)n_x^2&(-1-1)n_xn_y &(-1-1)n_xn_z \\ (-1-1)n_xn_y&1+(-1-1)n_y^2 &(-1-1)n_yn_z\\ (-1-1)n_xn_z& (-1-1)n_yn_z &1+(-1-1)n_z^2\\ \end{bmatrix}\\= \begin{bmatrix} 1-2n_x^2& -2n_xn_y & -2n_xn_z \\ -2n_xn_y&1-2n_y^2 & -2n_yn_z\\ -2n_xn_z& -2n_yn_z &1-2n_z^2\\ \end{bmatrix}

正交投影矩陣

投影意味和降維操作,將所有的點拉平到要投影的直線或平面上,從原來的點到投影點的直線相互平行,這就是正交投影。透視投影是另一種投影。

在這裏插入圖片描述

向座標軸或平面上投影

通過將垂直方向上縮放因子設爲0來實現,如將3D點投影到xy平面,則拋棄z分量,通過將z方向上的縮放因子設爲0實現。

P是projection(投影)的縮寫,2D中,Px表示向x軸投影,Py同理:
Px=S([01],0)=[1000] P_x= S (\begin{bmatrix} 0&1 \end{bmatrix},0)= \begin{bmatrix} 1&0 \\ 0&0 \\ \end{bmatrix}
Py=S([10],0)=[0001] P_y= S (\begin{bmatrix} 1&0 \end{bmatrix},0)= \begin{bmatrix} 0&0 \\ 0&1 \\ \end{bmatrix}\\

3D中,Pxy表示向xy平面投影,其餘同理:
Pxy=S([001],0)=[100010000] P_{xy}= S (\begin{bmatrix} 0&0&1 \end{bmatrix},0)= \begin{bmatrix} 1&0&0 \\ 0&1&0 \\ 0&0&0 \\ \end{bmatrix}
Pxz=S([010],0)=[100000001] P_{xz}= S (\begin{bmatrix} 0&1&0 \end{bmatrix},0)= \begin{bmatrix} 1&0&0 \\ 0&0&0 \\ 0&0&1 \\ \end{bmatrix}
Pyz=S([100],0)=[000010001] P_{yz}= S (\begin{bmatrix} 1&0&0 \end{bmatrix},0)= \begin{bmatrix} 0&0&0 \\ 0&1&0 \\ 0&0&1 \\ \end{bmatrix}

向任意指向或平面投影

投影有垂直於直線或平面的向量n定義,通過使n方向上的縮放因爲0就能導出任意方向的投影矩陣。P(n)表示向垂直於向量n的軸或平面投影矩陣,S(n,0)表示在n方向上的縮放因子爲0的縮放矩陣.

2D:
P(n^)=[1+(01)nx2(01)nxny(01)nxny1+(01)ny2]=[1nx2nxnynxny1ny2] P(\hat n)=\begin{bmatrix} 1+(0-1)n_x^2 & (0-1) n_xn_y \\ (0-1) n_xn_y & 1+(0-1)n_y^2 \\ \end{bmatrix}= \begin{bmatrix} 1-n_x^2 & -n_xn_y \\ -n_xn_y & 1-n_y^2 \\ \end{bmatrix}
3D:

P(n^)=[1+(01)nx2(01)nxny(01)nxnz(01)nxny1+(01)ny2(01)nynz(01)nxnz(01)nynz1+(01)nz2]=[1nx2nxnynxnznxny1ny2nynznxnznynz1nz2] P(\hat n)=\begin{bmatrix} 1+(0-1)n_x^2 & (0-1) n_xn_y & (0-1) n_xn_z \\ (0-1) n_xn_y & 1+(0-1)n_y^2 & (0-1) n_yn_z \\ (0-1) n_xn_z & (0-1) n_yn_z & 1+(0-1)n_z^2 \\ \end{bmatrix}= \begin{bmatrix} 1-n_x^2 & -n_xn_y & -n_xn_z \\ -n_xn_y & 1-n_y^2 & -n_yn_z \\ -n_xn_z & -n_yn_z & 1-n_z^2 \\ \end{bmatrix}

透視投影矩陣

Unity中投射投影矩陣與正交投影

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章