OpenGL學習腳印: 投影矩陣的推導
寫在前面
本節(jié)內容翻譯和整理自http://www. songho的博客《OpenGL Projection Matrix》內容,以供自己和初學者熟悉投影矩陣推導過程。
通過本節(jié),你可以了解到:
本節(jié)的要點就在于: 閱讀時,自己拿筆推導一遍。
1.概覽
計算機屏幕是2維的,OpenGL渲染的3D場景必須以2D形式的圖像投影到屏幕上。GL_PROJECTION 矩陣就是用來設置投影變換的。首先,它將所有頂點從眼坐標(照相機坐標)轉換到裁剪坐標系下。然后,這些裁剪坐標通過透視除法,即除以裁剪坐標中w分量,轉換到歸一化設備坐標系(NDC)。
一個由視錐裁剪的三角形
因此,我們要記住,裁剪(視錐剔除frustum culling)和NDC轉換都集成到了GL_PROJECTION 矩陣。接下來的部分描述了怎么樣通過left, right, bottom, top, near and far 這6個界限參數(shù)來構造投影矩陣。
注意:
視錐剔除是在裁剪坐標系中進行的,并且恰好在透視除法之前進行。裁剪坐標xc, yc 和 zc 通過與wc比較來進行測試。 如果某個坐標值比Wc小或者比Wc大,那么這個頂點將被丟棄。然后,OpenGL會重新在裁剪進行的地方構造多邊形的邊緣。
補充內容:
實際上,眼坐標系下坐標在乘以投影矩陣后,裁剪測試和透視除法都是由GPU來執(zhí)行的。而后面這兩個過程處理的裁剪坐標系數(shù)據(jù)都是由投影矩陣變換的。
1. 裁剪測試也即視錐剔除
-Wc < Xc,Yc,Zc < Wc
2. NDC透視除法
Xn = Xc / Wc Yn = Yc / Wc Zn = Zc / Wc
這里需要注意的是,我們在構造16個參數(shù)的投影矩陣的同時,不僅要考慮到裁剪,還要考慮到透視除法的過程。這樣,最終的NDC坐標才會滿足:
-1 < Xn,Yn,Zn < 1
2.透視投影
在透視投影中,在眼坐標下截頭椎體(a truncated pyramid frustum)內的3D點被映射到NDC下一個立方體中;x坐標從[l,r]映射到[-1,1],y坐標從[b,t]映射到[-1,1],z坐標從[n,f]映射到[-1,1]。
注意:
眼坐標系使用右手坐標系,而NDC使用左手坐標系。這就是說,眼坐標系下,在原點處的照相機朝著-Z軸看去,但是在NDC中它朝著+Z軸看去。因為glFrustum() 僅接受正的near和far距離,我們在構造GL_PROJECTION 矩陣時,需要取其相反數(shù)。眼坐標系和NDC坐標系如下圖所示:
在OpenGL中,眼坐標下3D點被投影到近裁剪面(即投影平面)。下圖展示了眼坐標系下點(xe, ye, ze) 如何投影到近裁剪面上的 (xp, yp, zp) 的。(左側是視錐的俯視圖,右側是視錐的側視圖,拿出右手構成右手坐標系,然后比劃比劃就出來了)
根據(jù)三角形的相似性,由俯視圖可得出:
由側視圖可以得出:
補充: xp 和yp其實是一個中間值,我們要找的是(Xc, Yc, Zc)和 (Xn, Yn, Zn)之間的關系,但是可以利用:
這一關系做過渡,后面利用xp和yp,映射到NDC中xn和yn的線性關系就利用到了 xp 和yp。這一點很重要。
注意,這里 xp 和yp 都依賴于ze,他們與 -ze成反比。換句話說,他們都被 -ze相除。這個事構造GL_PROJECTION 矩陣最初的線索。在眼坐標通過乘以 GL_PROJECTION 來轉換時,裁剪坐標系仍然是一個齊次坐標系。通過對裁剪坐標進行透視除法得到最終的NDC坐標。下圖解釋了這個過程:
因此我們可以把裁剪坐標系下的w分量設為-ze,那么GL_PROJECTION矩陣第4行變?yōu)?0, 0, -1, 0),如下圖(求出了投影矩陣第4行):
下現(xiàn)在我們把xp和yp,映射到NDC中xn和yn,他們之間是線性關系: [l, r] ? [-1, 1]和[b, t] ? [-1, 1].
線性關系如下圖所示:
則可以推導出:
細節(jié)部分有刪節(jié),這個推導過程使用的就是簡單的y=kx+b線性關系推導,同理利用[b, t] ? [-1, 1]可推得:
將上面的 xp 和yp帶入求得:
注意這里Xn和Yn已經(jīng)是NDC中的坐標了,通過這兩個坐標可以求出GL_PROJECTION 的前兩行來,書寫如下(求出了投影矩陣第1,2,4行):
現(xiàn)在怎么求出第3行呢?
找出zn與找出xn和yn不同,因為 ze總是被投影近裁剪面-n上。但是我們需要唯一的Z值進行裁剪和深度測試。另外,我們還能夠unproject即反向變換(inverse transform)。因為Z值不依賴于x或者y,因此我們借用w分量來找出 zn 和 ze之間的關系。
因此我們可以這樣指定第3行:
在眼坐標下We等于1,因此上式變?yōu)?
我們使用(ze, zn)的關系(-n, -1)和 (-f, 1)來求解出系數(shù)A,B;
細節(jié)部分有刪節(jié),使用消元法即可求出:
我們求出了A和B,那么ze和zn關系如下式:
最終的投影矩陣如下式:
這個公式對應的是一般的視錐,如果視錐是對稱的,即r = -l ,t= -b,那么有:
z-fighting
在繼續(xù)之前,我們來看看表示ze和zn關系的式3.這是一個有理函數(shù),并且ze和zn之間不是線性關系。這意味著,在近裁剪面的精度很高,而遠裁剪面則很小。如果[-n,
-f]范圍變得大寫,就會引起深度精度問題,即z-fighting。在遠裁剪面附近,ze的小變化根本不影響zn值。近裁剪面和遠裁剪面之間的n和f舉例,應該盡可能小,來減少深度緩存的精度問題??蓞⒖枷聢D來幫助理解。
3.正交投影
構造正交投影的矩陣簡單很多。所有的是眼坐標下xe, ye 和ze,都被線性的映射到NDC中。我們需要做的就是講長方體視景體縮放為規(guī)范視見體,然后移動到原點。如下圖所示:
以xe和xn之間映射關系為例,[l,r]=>[-1,1],則可以推導如下:
y,z也有類似推導,這里省略,最后得出投影矩陣為:
如果視錐是對稱的話,即r = -l ,t= -b的話,則可以簡化為:
到這里透視投影和正交投影矩陣推導完畢。
|