齊次坐標與投影幾何
本文翻譯自:Explaining Homogeneous Coordinates & Projective Geometry
這篇文章通俗易懂地解釋了齊次坐標和投影幾何的概念和他們在圖形學中的作用.
術語
大多數時候我們使用三維坐標系,從歐式幾何(Euclidean geometry)的角度來看待問題–即,三維空間中的坐標分為X,Y,Z三個維度.但是,在某些情況下從投影幾何(projective geometry)的角度來思考問題是更好的.投影幾何有一個額外的維度,稱之為W.在此基礎上的四維空間被稱為”投影空間“(projective space),並且坐標在投影空間中被稱為”齊次坐標“(homogeneous coordinates).
並非四元數
四元數看起來有點像齊次坐標.他們都是四維向量,並且通常都被描述為(X,Y,Z,W).然而,四元數和齊次坐標是不同的概念,有不同的作用(四元數主要用來描述旋轉矩陣,這裡不展開).
二維中的投影幾何
首先,在談論三維之前,我們來看一下投影幾何如何在二維中起作用.
想像一個正在投影二維影像到螢幕上的投影儀.很容易定義被投影影像的X和Y維度:
現在,如果你從二維影像退後一步,看看投影儀和螢幕,你同時也可以看到W維度.W維度是從投影儀到螢幕的距離.
那麼,W維度究竟是做什麼的呢?想像一下,如果你增加或減少W,即增加或減少投影儀與螢幕之間的距離,二維影像會發生什麼.如果移動投影儀靠近螢幕,整個二維影像會縮小.如果移動投影儀原理螢幕,二維影像會變大.如你所見,W的值會影響影像的大小(或者說比例).
將其應用至三維
(目前)還沒有三維投影儀這種東西,因此很難想像在三維空間中的投影幾何.但是,W值的作用與二維空間中的完全相同.當W增加時,坐標將擴展(放大).當W減小時,坐標將收縮(縮小).W基本上代表著三維坐標的縮放變換。
當W=1時
一般建議當將三維坐標轉換為四維坐標時,應始終把W設置為1.這樣做的原因是當你將坐標用1為係數來縮放時,它不會收縮或增長,只會保持相同的大小.因此,當W=1時,它對X,Y 和Z值沒有影響.
因此,當涉及到三維圖形學時,只有當W=1時坐標才被稱為「正確」.如果使用W>1的坐標渲染,所有內容最後看起來都會被縮小.而使用W<1的坐標渲染時,所有內容最後看起來都會被放大.如果嘗試使用W=0進行渲染,程式在嘗試除以零時會直接崩潰.當W<0,一切都會從前到後翻轉過來.
從數學上講,不存在”不正確”的齊次坐標.使用W=1的坐標只是三維電腦圖形學的一個有效的約定。
從數學上看
現在讓我們來看看一些實際的數據,看看它從數學角度是如何起作用的.
假設投影儀距離螢幕3米,並且在二維影像上的坐標(15,21)處有一個點.它的投影(齊次)坐標向量(X,Y,W)=(15,21,3).
現在,想像這個投影儀被推到離螢幕1米的地方.投影儀離螢幕越近,影像就變得越小.投影儀被移近了三倍,因此影像縮小了三倍,如果我們取原始坐標向量並將所有值除以三,則得到W=1的新向量:
\]
這個點現在在坐標(5,7).
這就是一個”錯誤”的齊次坐標轉換為”正確”的齊次坐標的過程:對所有的值除以W.這個過程對於二維和三維坐標是完全相同的.
這裡是一個四維(三維齊次)坐標的例子:
\]
這個例子使用C++和GLM編寫是這樣的:
glm::vec4 coordinate(10, 20, 30, 5);
glm::vec4 correctCoordinate = (1.0/coordinate.w) * coordinate;
//now, correctCoordinate == (2,4,6,1)
圖形學中齊次坐標的應用
三維坐標的平移矩陣
旋轉和縮放變換矩陣只需要三行三列(這是由於旋轉和縮放都是線性變換).但是,為了進行平移,矩陣至少需要有四列.這就是為什麼圖形學中的變換矩陣通常是4×4矩陣.但是,由於矩陣乘法的規則,一個有四列的矩陣不能與一個三維向量相乘.一個4×4矩陣只能與一個四維向量相乘,這就是為什麼我們在三維圖形學經常使用齊次坐標而不是三維空間坐標。
當在矩陣變換中使用齊次坐標時,第四維的W通常是不變的.當三維坐標轉換為四維(齊次)坐標時,W被設置為1,應用變換矩陣後通常仍然為1,此時可以通過忽略W將其轉換回三維坐標.對於所有平移,旋轉和縮放變換都是如此.投影矩陣是個例外,它會影響W維度.
透視(投影)變換
在三維世界中,”透視”是”近大遠小”的現象.如果這隻貓離相機足夠近,遠處的山可能比貓還小.
透視是在三維圖形中通過使用更改每個頂點的W元素的轉換矩陣來實現的.在相機(視口)變換之後,投影變換之前,每個頂點的Z值表示與相機的距離.因此,Z值越大,頂點坐標就被縮放得越小.W維度會影響縮放,因此投影矩陣只是根據Z值更改W值.下面是應用於齊次坐標的透視投影矩陣的示例:
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0 \\
0 & 0 & 1 & 0 \\
0 & 0 & 1 & 0
\end{bmatrix}
\begin{bmatrix} 2 \\ 3 \\ 4 \\ 1 \end{bmatrix} =
\begin{bmatrix} 2 \\ 3 \\ 4 \\ 4 \end{bmatrix}
\]
注意W值根據Z值變換成了4.
應用透視投影矩陣後,每個頂點都會經歷”透視除法”.如前所述,”透視除法”只是將齊次坐標轉換回W=1形式的特定術語.繼續上面的示例,透視除法步驟將如下所示:
\]
透視分割後,W值將被丟棄,並且我們只剩下一個已根據三維透視投影正確縮放的三維坐標.
在GLM中,這個透視投影矩陣可以用函數glm::perspective
或者glm::frustum
來創建.在老式的OpenGL中,同時使用gluPerspective
或者gluFrustum
來創建.在OpenGL中,透視除法在每個頂點的頂點著色器(vertex shader)運行後自動進行.這就是頂點著色器的主要輸出gl_Position
為什麼是一個四維向量而不是三維向量的原因.
注意,這裡作者舉的透視投影的例子非常簡單,實際上透視投影的矩陣需要根據參數經過一定的推導才能得出,參考OpenGL Projection Matrix
表示平行光
本段的翻譯略有調整,但是整體的意思應該沒有差別
在齊次坐標中W=0貌似是沒有意義的,如果嘗試將W=0的齊次坐標轉換為正確的齊次坐標(W=1),則會導致一些除零的運算:
\]
這意味著W=0的齊次坐標不可以轉換為三維坐標.
這有什麼用呢?
我們可以規定W=0的齊次坐標可以看作在無窮遠處的一個點,如果我們把他看作一個光源,則可以看作一個平行光(Directional Lights),並把它的X,Y,Z三個分量看作是平行光的方向.在傳統的三維圖形學中,平行光與點光源的區別在於光的位置向量的W值.如果 W=1,則為點光源.如果 W=0,則它是平行光.
平行光和點光源通常因為行為方式的不同,通常需要不同的程式碼來實現.典型的光照著色器可能如下所示:
if(lightPosition.w == 0.0){
//directional light code here
} else {
//point light code here
}
總結
齊次坐標具有一個用於縮放X,Y和Z維度的的額外維度W.
平移變換和透視投影變換的矩陣只能應用於齊次坐標,這就是為什麼齊次坐標在三維圖形學中如此普遍的原因.一般規定W=1的齊次坐標為”正確”的齊次坐標.任何齊次坐標可以通過除以W分量(透視除法)轉換為”正確”的齊次坐標,除了W=0的情況.當W=0時,我們一般用其來表示一束平行光的方向