回顧Games101圖形學(一)幾何變換中一些公式的推導
- 2021 年 6 月 24 日
- 筆記
- Games101 圖形學基礎, 電腦圖形學
回顧Games101 chatper1 – 6
前言
本文只寫回顧後重新加深認識的知識
透視除法的意義
經過MVP矩陣之後,將模型空間下某點的坐標,轉換成了裁剪空間下的坐標,此時因為裁剪空間的範圍是x∈[-W/2,W/2]和y∈[-H/2,H/2],所以經過以下兩個變換,其中除以pz就是透視除法
一:
\\
-1≤2·\frac{\left( \frac{p_y}{p_z}·near \right)}{h}≤1
\]
二:
x& y& z& w\\
\end{matrix} \right] \left[ \begin{matrix}
1& 0& 0& 0\\
0& 1& 0& 0\\
0& 0& 1& 0\\
\varDelta x& \varDelta y& \varDelta z& 1\\
\end{matrix} \right] =\left[ \begin{matrix}
x+\varDelta x*w& y+\varDelta y*w& z+\varDelta z*w& w\\
\end{matrix} \right]
\]
只有當W=1,這個三維坐標轉換是等價的,才能保證位移的量是正確的,W=0時,則沒有位移
只有當W=1時,三維坐標點轉換成四維齊次坐標點才是等價的
坐標系變換和矩陣推導
坐標系變換理解不直觀,傾向於101中閆老師所說的理解坐標系的轉換通過矩陣進行的線性變換,將A坐標系下的點P,乘上矩陣得出B坐標系下的點P’,以下是拋開常見的變換(如透視投影變換、正交投影變換等)如何得出變換矩陣M,通過矩陣變換(下文著重說明)
已知坐標系A和坐標系B
\]
\]
則將坐標系B中一點P從坐標系B變換到坐標系A的變換矩陣為:(注意此處的例子是將源坐標系A變換到目標坐標系B下)
u_{\mathrm{x}}& u_{\mathrm{y}}& u_{\mathrm{z}}& 0\\
v_{\mathrm{x}}& v_{\mathrm{y}}& v_{\mathrm{z}}& 0\\
w_{\mathrm{x}}& w_{\mathrm{y}}& w_{\mathrm{z}}& 0\\
Q_{\mathrm{x}}& Q_{\mathrm{y}}& Q_{\mathrm{z}}& 1\\
\end{matrix} \right]
\]
如之前所說,變換過程中點p在空間中的絕對位置沒有發生改變,只是參考坐標系發生了改變,從B坐標系變到A坐標系。(縮放,旋轉,平移變換隻有在同一坐標系下才有意義)
矩陣變換是基於基向量組的結果
- 矩陣變換之於同一個坐標系,可以理解為坐標系不變,點的位置改變
- 矩陣變換之於不同坐標系,可以理解為點的絕對位置不變,坐標系改變
x^{‘}\\
y^{‘}\\
\end{array} \right] =B\left[ \begin{array}{c}
x\\
y\\
\end{array} \right] \Rightarrow \left[ \begin{array}{c}
x\\
y\\
\end{array} \right] =B^{-1}\left[ \begin{array}{c}
x^{‘}\\
y^{‘}\\
\end{array} \right] \text{,}B=\left[ \begin{matrix}
\overrightarrow{b_1}& \overrightarrow{b_2}\\
\end{matrix} \right] \text{,且}\overrightarrow{b_1}\text{,}\overrightarrow{b_2}\text{是坐標系}B\text{的基向量}
\]
其中,矩陣B的各個列向量分別對應B坐標系的各個基向量,\(
\left[ \begin{array}{c}
x\\
y\\
\end{array} \right]
\)是向量\(
\overrightarrow{OP}
\)或者說點P在B坐標系的表示,\(
\left[ \begin{array}{c}
x^{‘}\\
y^{‘}\\
\end{array} \right]
\)則是向量\(
\overrightarrow{OP}
\)或者點P在A坐標系中的表示
以圖中的兩個向量\(
\overrightarrow{b_1}
\),\(
\overrightarrow{b_2}
\)為基確定一個坐標系B,顯然在B坐標系中\(
\overrightarrow{b_{1B}}=\left[ \begin{array}{c}
1\\
0\\
\end{array} \right]
\),\(
\overrightarrow{b_{2B}}=\left[ \begin{array}{c}
0\\
1\\
\end{array} \right]
\),接下來,將\(
\overrightarrow{b_1}
\),\(
\overrightarrow{b_2}
\)定位到A坐標系中,得到\(
\overrightarrow{b_{1A}}=\left[ \begin{array}{c}
2\\
1\\
\end{array} \right]
\),\(
\overrightarrow{b_{2A}}=\left[ \begin{array}{c}
-1\\
1\\
\end{array} \right]
\)
\(
\because \overrightarrow{OP}=2\overrightarrow{b_1}+2\overrightarrow{b_2}
\)
\(
\therefore \overrightarrow{OP}\)在B坐標系中的表示為\(
\left[ \begin{array}{c}
2\\
2\\
\end{array} \right]
\),現在,將\(
\overrightarrow{OP}
\)用A坐標系描敘:
\(
\overrightarrow{OP}=2\overrightarrow{b_1}+2\overrightarrow{b_2}=2\overrightarrow{b_{1A}}+2\overrightarrow{b_{2A}}=\left[ \begin{matrix}
\overrightarrow{b_{1A}}& \overrightarrow{b_{2A}}\\
\end{matrix} \right] \left[ \begin{array}{c}
2\\
2\\
\end{array} \right] =\left[ \begin{array}{c}
2\\
4\\
\end{array} \right]
\\
\)
現在,令矩陣B=\(
\left[ \begin{matrix}
\overrightarrow{b_{1A}}& \overrightarrow{b_{2A}}\\
\end{matrix} \right]
\),P點是用B坐標系表示的任意一點\((x,y)\)。
於是\(
\overrightarrow{OP}
\)在A坐標系中的表示\(
\left[ \begin{array}{c}
^{x^{‘}}\\
y^{‘}\\
\end{array} \right] =B\left[ \begin{array}{c}
x\\
y\\
\end{array} \right]
\),顯然,B是可逆的,於是就有了之前的結論
那麼在這個例子當中,當我們需要知道某點在轉換坐標系後的新坐標時,通過該例子也可以加深印象,比如在B坐標系下有點\(Q(3,4)\),即\(\overrightarrow{OQ}=(3,4)\),跟據剛才的例子可以看出它轉換在A坐標系下的點
\overrightarrow{b_{1A}}& \overrightarrow{b_{2A}}\\
\end{matrix} \right] \left[ \begin{array}{c}
3\\
4\\
\end{array} \right] =\left[ \begin{matrix}
2& -1\\
1& 1\\
\end{matrix} \right] \left[ \begin{array}{c}
5\\
4\\
\end{array} \right] =\left[ \begin{array}{c}
6\\
9\\
\end{array} \right]
\]
即轉換到A坐標系下的點\(Q^{‘}\)的坐標為\(Q^{‘}(6,9)\)
雖然這裡的討論是基於二維的,但是,結論可以擴展到任意維度
闡述結論:
將B坐標系的基向量定位到A坐標系,然後將定位之後的基向量作為矩陣B的列向量,用矩陣B對B坐標系中的點P的坐標進行矩陣變換,將得到點P在A坐標系中的坐標。這個過程,就是從坐標系B到坐標系A的一個追溯過程
View/Camera Transformation
先將相機移到原點,然後進行分別對坐標軸進行旋轉,用矩陣表示則是\(M_{view}=R_{view}T_{view}\)
- 將相機移回原點
1& 0& 0& -x_e\\
0& 1& 0& -y_e\\
0& 0& 1& -z_e\\
0& 0& 0& 1\\
\end{matrix} \right]
\]
- \(Rotate\,\,g\,\,to\,\,-Z, t\,\,to\,\,Y, \left( g×t \right) \,\,To\,\,X\)
g是相機看的方向(lookAt),t是相機向上的方向(Up),也就是相機的-Z軸和Y軸,兩個向量叉積就是另一個坐標軸
x_{\widehat{g}×\widehat{t}}& x_t& x_{-g}& 0\\
y_{\widehat{g}×\widehat{t}}& y_t& y_{-g}& 0\\
z_{\widehat{g}×\widehat{t}}& z_t& z_{-g}& 0\\
0& 0& 0& 1\\
\end{matrix} \right]
\]
旋轉矩陣是正交矩陣,所以旋轉矩陣的逆就是旋轉矩陣的轉置
x_{\widehat{g}×\widehat{t}}& y_{\widehat{g}×\widehat{t}}& z_{\widehat{g}×\widehat{t}}& 0\\
x_t& y_t& y_t& 0\\
x_{-g}& y_{-g}& z_{-g}& 0\\
0& 0& 0& 1\\
\end{matrix} \right]
\]
正交投影矩陣
無論是正交投影還是透視投影,都是要將x、y、z移到-1到1的範圍內,先將中心點移到原點,然後縮放
\frac{2}{r-l}& 0& 0& 0\\
0& \frac{2}{t-b}& 0& 0\\
0& 0& \frac{2}{n-f}& 0\\
0& 0& 0& 1\\
\end{matrix} \right) \left( \begin{matrix}
1& 0& 0& -\frac{r+l}{2}\\
0& 1& 0& -\frac{t+b}{2}\\
0& 0& 1& -\frac{n+f}{2}\\
0& 0& 0& 1\\
\end{matrix} \right)
\]
透視投影矩陣推導
首先先將frustum 轉變為cuboid(n -> n,f -> f)(\(
M_{persp->ortho}
\)
)
然後再做正交投影
整個投影變換包括兩部分
- v = P(矩陣)*p
- \(v=\frac{v}{v_w}=\frac{v}{pz}\)透視除法
以上大概推出等式這一步,接下來用公式展示更為直觀
m00& m01& m02& m03\\
m10& m11& m12& m13\\
m20& m21& m22& m23\\
m30& m31& m32& m33\\
\end{matrix} \right) \left( \begin{array}{c}
x\\
y\\
z\\
1\\
\end{array} \right) =\left( \begin{array}{c}
\frac{x}{z*aspect*\tan \left( \frac{fov}{2} \right)}\\
\frac{y}{z*tan\left( \frac{fov}{2} \right)}\\
z^{『』}\\
1\\
\end{array} \right)
\]
\]
將右邊的四維列向量表示的坐標每一項乘以z,所以有
m00& m01& m02& m03\\
m10& m11& m12& m13\\
m20& m21& m22& m23\\
m30& m31& m32& m33\\
\end{matrix} \right) *\left( \begin{array}{c}
x\\
y\\
z\\
1\\
\end{array} \right) =\left( \begin{array}{c}
\frac{x}{aspect*\tan \left( \frac{fov}{2} \right)}\\
\frac{y}{\tan \left( \frac{fov}{2} \right)}\\
z*z^{{‘}{‘}}\\
z\\
\end{array} \right)
\]
所以求得矩陣為
\frac{1}{aspect*\tan \left( \frac{fov}{2} \right)}& 0& 0& 0\\
0& \frac{1}{\tan \left( \frac{fov}{2} \right)}& 0& 0\\
0& 0& m22& m23\\
0& 0& 1& 0\\
\end{matrix} \right)
\]
\\
\Rightarrow m22+\frac{m23}{z}=z^{{‘}{‘}}
\]
因為z=zNear時,z”=-1;z=zFar時,z”=1所以有以下等式
\\
m22+\frac{m23}{zFar}=1
\]
聯立求得:
\\
m23=\frac{2*zFar*zNear}{zNear-zFar}
\]
最後求得投影矩陣為
\frac{1}{aspect*\tan \left( \frac{fov}{2} \right)}& 0& 0& 0\\
0& \frac{1}{\tan \left( \frac{fov}{2} \right)}& 0& 0\\
0& 0& \frac{-zFar-zNear}{zNear-zFar}& \frac{2*zNear*zFar}{zNear-zFar}\\
0& 0& 1& 0\\
\end{matrix} \right)
\]
將這樣得矩陣乘以視錐體內的一個頂點坐標,得到一個新的向量,再將這個向量的每個分量除以第四個分量(此步驟也被稱為透視除法)(w),這樣就可以得到頂點映射到規則立方觀察體後的新的坐標
注意:z坐標的映射方式的獲得,最後我們是為了方便矩陣乘法的操作方向求得了z坐標與cvv中的z坐標的映射方式:
\]
此時的映射並不是線性的,當z越大時,z的變化對z”的擾動越小
Canonical Cube to Screen
- Irrelevant to z
- Transform in xy plane : [-1, 1] to [0, width] × [0, height]
- Viewport transform matrix:
視口矩陣
\frac{width}{2}& 0& 0& \frac{width}{2}\\
0& \frac{height}{2}& 0& \frac{height}{2}\\
0& 0& 1& 0\\
0& 0& 0& 1\\
\end{matrix} \right]
\]
深度z的計算
前言
3D光柵化發生在圖元被變換到Screen space之後,因為這裡的Screen space與2D的Screen Space完全一致,所以2D的光柵化演算法在這裡依然適用。
然而由於圖元經過了投影變換,且投影變換為非線性變換,所以不能用簡單的線性插值獲取fragment的屬性
如上圖所示,view space中的線段v0v1上兩點$
p0\left( p0_x,p0_y,p0_z,1 \right)
$,$
p1\left( p1_x,p1_y,p1_z,1 \right)
$在near plane上的投影為點$
s0\left( s0_x,s0_y \right)
$,$
s1\left( s1_x,s1_y \right)
$。$
p0
$,$p1$中間一點$v(v_x,v_y,v_z,1)$在near plane上的投影為點$q(q_x,q_y)$。從圖中可以看出點v到p0,p1的距離比值與點q到s0,s1的距離比值完全不同,投影變換不保持距離不變。
為了執行z-buffer演算法,需要通過點q獲取到v的深度值(z)
點\(v\)的深度值可以通過如下方法插值得到:
\]
以下是推導的過程:
手寫版:
文字版:
由於點\(q\)為點\(v\)在near plane上的投影,因此點\(q\)與點\(v\)的關係為:
- \(q_x=\frac{v_x·near}{v_z}\)
且\(v\)位於\(p0p1\)之間,則 - \(v_z=p0_{z}+t·(p1_z-p0_z)=\frac{v_x·near}{q_x}\)
由點\(v\)在\(p0\),\(p1\)之間,點\(q\)在\(s0\),\(s1\)之間則有 - \(v_x=p0_{x}·(1-t)+p1_{x}·t=p0_{x}+t·(p1_{x}-p0_{x})\)
- \(q_x=s0_{x}·(1-c)+s1_{x}·c=s0_{x}+c·(s1_{x}-s0_{x})\)
代入式(1)可得
\(v_z=\frac{v_x·near}{q_x}=\frac{(p0_x+t·(p1_x-p0_x))·near}{s0_x+c·(s1_x-s0_x)}\)式(2)
又s0和s1分別為p0和p1在near plane上的投影,則: - \(s0_x=\frac{p0_x·near}{p1_z}\)
- \(s1_x=\frac{p1_x·near}{p1_z}\)
代入式(2)可得:
\]
\\
v_z=\frac{\left( p0_x·s0_x+t·\left( p1_x·s1_x-p0_x·s0_x \right) \right)}{s0_x+c·\left( s1_x-s0_x \right)}
\\
p0_z+t·\left( p1_z-p0_z \right) =\frac{\left( p0_x·s0_x+t·\left( p1_x·s1_x-p0_x·s0_x \right) \right)}{s0_x+c·\left( s1_x-s0_x \right)}
\\
\left( p0_z+t·\left( p1_z-p0_z \right) \right) ·\left( s0_x+c·\left( s1_x-s0_x \right) \right) =p0_x·s0_x+t·\left( p1_x·s1_x-p0_x·s0_x \right)
\\
p0_z·s0_x+p0_z·c·\left( s1_x-s0_x \right) +t·\left( p1_z-p0_z \right) ·s0_x+t·c·\left( p1_z-p0_z \right) ·\left( s1_x-s0_x \right) =p0_x·s0_x+t·\left( p1_x·s1_x-p0_x·s0_x \right)
\]
化簡得:
\]
則:
\]
代入式(1)可得
\]
\]
\]
若View Space中三角形\(v0v1v2\),變換到Screen Space後為三角形\(s0s1s2\),\(v0v1v2\)內一點v在Screen Space的投影點\(s0s1s2\)內的點\(q\),對三角形\(s0s1s2\)內的點(fragment)\(q\),可以通過如下方法取得fragment\(q\)在View Space中對應的深度值:
\]
\(\lambda0,\lambda1,\lambda2\)為點p在三角形\(s0s1s2\)內的重心坐標
引入結論:
對Screen Space三角形\(s0,s1,s2\)內一點p的任意屬性插值的公式為:
\]
\(\lambda0,\lambda1,\lambda2\)為點\(p\)的重心坐標,\(z0,z1,z2,z\)分別為\(s0,s1,s2,p\)在view space中對應點的深度值,可以用這個方法插值得到\(p\)在NDC Space內對應點的深度值
羅德里格斯旋轉公式
字寫得不好,在爬了…
手寫版:
文字版:
首先先將\(\overrightarrow{k}\)處理成單位向量,這點很重要,關乎著下一步等式是否成立,有些博文寫這裡不需要處理單位向量,這是錯的
\]
可得
\\
\overrightarrow{v}=\overrightarrow{v_{\bot}}+\overrightarrow{v_{||}}
\\
\overrightarrow{v_{\bot}}=\overrightarrow{v}-\overrightarrow{v_{||}}=\overrightarrow{v}-\left( \overrightarrow{v}·\overrightarrow{k} \right) \overrightarrow{k}
\]
繞\(\overrightarrow{k}\)做旋轉時,向下做垂線,可看作底部經過了類似半圓的旋轉
要求得\(\overrightarrow{v_{rot}}=\overrightarrow{v_{||}}+\overrightarrow{v_{rot\bot}}\),將\(\overrightarrow{v_{rot\bot}}\)作正交分解有\(
\overrightarrow{v_{rot\bot}}=\overrightarrow{a}+\overrightarrow{b}
\),易得\(
|\overrightarrow{w}|=|\overrightarrow{v_{\bot}}|
\),則有\(
\overrightarrow{w}=\overrightarrow{k}×\overrightarrow{v_{\bot}}=\overrightarrow{k}×\left[ \overrightarrow{v}-\overrightarrow{v_{||}} \right] =\overrightarrow{k}×\overrightarrow{v}-\overrightarrow{k}×\overrightarrow{v_{||}}=\overrightarrow{k}×\overrightarrow{v}-0=\overrightarrow{k}×\overrightarrow{v}
\)
接下來求\(
\overrightarrow{a}
\)和\(
\overrightarrow{b}
\)
\\
\overrightarrow{a}=\frac{\overrightarrow{w}}{|\overrightarrow{w}|}·|\overrightarrow{a}|=\frac{\overrightarrow{w}}{|\overrightarrow{v_{rot\bot}}|}·|\overrightarrow{v_{rot\bot}}|·\sin \left( \theta \right) =\overrightarrow{w}·\sin \left( \theta \right)
\]
\\
\overrightarrow{b}=\frac{\overrightarrow{v_{\bot}}}{|\overrightarrow{v_{\bot}}|}·|\overrightarrow{b}|=\frac{\overrightarrow{v_{\bot}}}{|\overrightarrow{v_{\bot}}|}·|\overrightarrow{v_{rot\bot}}|·\cos \left( \theta \right) =\overrightarrow{v_{\bot}}·\cos \left( \theta \right) \,\, \text{注意}|\overrightarrow{v_{\bot}}|=|\overrightarrow{v_{rot\bot}}|
\]
\\
\overrightarrow{v_{rot}}=\overrightarrow{v_{||}}+\overrightarrow{v_{rot\bot}}=\left( \overrightarrow{v}·\overrightarrow{k} \right) \overrightarrow{k}+\sin \left( \theta \right) ·\left( \overrightarrow{k}×\overrightarrow{v} \right) +\cos \left( \theta \right) \left( \overrightarrow{v}-\left( \overrightarrow{v}·\overrightarrow{k} \right) \overrightarrow{k} \right)
\\
=\cos \left( \theta \right) \overrightarrow{v}+\left( 1-\cos \left( \theta \right) \left( \overrightarrow{v}·\overrightarrow{k} \right) \overrightarrow{k} \right) +\sin \left( \theta \right) ·\left( \overrightarrow{k}×\overrightarrow{v} \right)
\]
把\(
\overrightarrow{k}
\)和\(
\overrightarrow{v}
\)分別寫為列向量
k_x\\
k_y\\
k_z\\
\end{array} \right)
\]
v_x\\
v_y\\
v_z\\
\end{array} \right)
\]
令\(
\overrightarrow{v_{rot}}=R·\overrightarrow{v}
\)
兩個式子
\]
k_yv_z-k_zv_y\\
k_zv_x-k_xv_z\\
k_xv_y-k_yv_x\\
\end{array} \right] =\left[ \begin{matrix}
0& -k_z& k_y\\
k_z& 0& -k_x\\
-k_y& k_x& 0\\
\end{matrix} \right] \left[ \begin{array}{c}
v_x\\
v_y\\
v_z\\
\end{array} \right]
\]
結合以上兩個式子可得,其中\(I\)為3×3的單位矩陣
k_x\\
k_y\\
k_z\\
\end{array} \right) \left( \begin{matrix}
k_x& k_y& k_z\\
\end{matrix} \right) +\sin \left( \theta \right) \left( \begin{matrix}
0& -k_z& k_y\\
k_z& 0& -k_x\\
-k_y& k_x& 0\\
\end{matrix} \right) \,\,
\]
以下是比較通用的表示方式
0& -n_z& n_y\\
n_z& 0& -n_x\\
-n_y& n_x& 0\\
\end{matrix} \right)
\]
部分引用的博文
//blog.csdn.net/unclerunning/article/details/70948696#齊次坐標系與平移
//zhuanlan.zhihu.com/p/45757899