OpenGL投影矩陣

概述

電腦顯示器是2D平面,一個3D物體通過OpenGL渲染投影到2D顯示器平面形成圖像,GL_PROJECTION是OpenGL用于投影轉換的矩陣。首先,轉換3D物體的頂點數(shù)據從相機空間到裁剪空間,然后,通過除以W(齊次坐標)從裁剪空間再轉換到標準設備空間(NDC)。
因此,我們需要意識到裁剪計算和NDC轉換都是整合到了GL_PROJECTION矩陣里了。接下來的內容將探討怎么構建投影矩陣,使用6個代表邊界值的參數(shù)分別是:left,right,bottom,top,near , far(下面簡寫為l、r、b、t、n、f)。有一點注意一下,裁剪計算是在轉換成NDC坐標之前發(fā)生的,即在除以w_c(齊次坐標)之前。通過裁剪坐標x_c,y_c,z_cw_c(這里的c下標表示裁剪空間)做對比,如果裁剪坐標小于-w_c或大于w_c將被裁減掉(-w_c < x_c,y_c,z_c < w_c),然后OpenGL會重新構建網格邊緣。

一個被平頭視錐裁剪的三角形

透視投影

在透視投影里,一個3D點在一個平頭視錐體里面(相機空間)是要被映射到一個正方體(NDC空間)里的,x坐標的區(qū)間從[l,r]映射到[-1,1],y坐標的區(qū)間從[b,t]映射到[-1,1],z坐標的區(qū)間從[-n,-f]映射到[-1,1]。

投影視錐和標準設備空間(NDC)

注意一點,相機空間是使用右手坐標系統(tǒng),但是NDC坐標空間使用左手坐標系統(tǒng),這就是相機在原點沿著-Z方向看,但是在NDC空間是反過來的。因為glFrustum()僅僅接收正的near和far值,在構造GL_PROJECTION矩陣時我們需要給他們變成負值。
在OpenGL里,一個3D點在相機空間是被投影到near平面(projection plane 即下圖的-n平面)。下面的圖片展示一個點(x_e,y_e,z_e)在相機空間投影到near平面上的點(x_p,y_p,z_p)。
從上往下看視錐體

從側面看視錐體

從上往下看視錐體,相機空間的x坐標點x_e投影到x_p,通過相似三角形原理得: x_p \over x_e = -n \over z_e => x_p = -n * x_e \over z_e = n*x_e \over -z_e
同理,從側邊看視錐體可得:y_p \over y_e = -n \over z_e => y_p = -n * y_e \over z_e = n * y_e \over -z_e
注意到,x_py_p都依賴z_e,他們和-z_e成相反的比例,也就是說他們都需要除以-z_e。這是非常有用的線索,用于構建GL_PROJECTION矩陣上,通過乘以GL_PROJECTION矩陣從相機空間轉換到裁剪空間,坐標仍然是齊次坐標,除以裁剪空間的w最終變成規(guī)范化設備坐標空間(NDC)(更多資料請查閱OpenGL Transformation
投影矩陣乘以相機空間坐標得到裁剪空間坐標
裁剪空間坐標除以W得到NDC空間坐標

因此,我們可以設置裁剪空間的w坐標等于-z_e,然后投影矩陣GL_PROJECTION的第四行就變成(0,0,-1,0)。
根據矩陣乘法公式計算得,裁剪空間的w值等于相機空間的-z值

下一步,我們通過線性關系映射x_p(p下標表示投影空間未有裁剪的)和y_p(p下標表示投影空間未有裁剪的)到NDC坐標空間的x_n(n下標表示NDC空間)和y_n(n下標表示NDC空間),即:[l,r] => [-1,1] [b,t] => [-1,1]

為了直觀點,可以把映射值連成的直線平移經過原點再平移回來
映射的最大點和最小點是已知的,代入上式可以求出平移值推導出映射公式

為了直觀點,可以把映射值連成的直線平移經過原點再平移回來
映射的最大點和最小點是已知的,代入上式可以求出平移值推導出映射公式

然后使用上面的x_py_p兩個等式替換到x_ny_n等式里:
求出NDC空間坐標,構造成除以w的形式
求出NDC空間坐標,構造成除以w的形式

注意到,我們故意把等式構造成除以-z_e的形式,即與透視除法(x_c/w_c,y_c/w_c)等同,只要把w_c看成-z_e上面等式括號的值就是裁剪空間的x_cy_c。
從上面的等式總結,我們可以找到GL_PROJECTION矩陣第一行和第二行的構造方法
裁剪空間坐標等于投影矩陣乘以相機空間坐標

現(xiàn)在GL_PROJECTION矩陣只剩下第三行沒構造好,構造z_n稍微不同于x_n、y_n,因為在相機空間ze總是投影在near平面的-n點上,由于我們需要z值方便做深度測試和裁剪,另外也需要逆轉換投影,我們知道z是不依賴于x或y的值,我們可以借用w表示z_nz_e之間的關系,因此我們可以構造GL_PROJECTION矩陣的第三行如下圖:

z是不依賴于x和y的,因此第三行矩陣x和y的值是0

在相機空間,w_e等于1,因此等式變成:
在相機空間的坐標w為1,因此可以略去

為了求出系數(shù)A和B,根據上面等式得出z_ez_n的關系,假設z_e等于-n(即近平面)那么z_n等于-1(即映射到NDC空間的坐標應為-1),z_e等于-f(即遠平面)那么z_n等于1(映射到NDC空間的坐標應為1),帶入上面等式:

由等式(1)推出:

把B等式代入等式(2):

把A等式代入等式(1):


得到A和B的等式可導出z_ez_n的關系等式:
NDC空間坐標的z值和相機空間坐標的z值的關系等式

最終,可得到整個GL_PROJECTION矩陣:
完整的投影矩陣公式

當視錐體是個對稱視錐體時,r = -l , t = -b 所以可以簡化矩陣公式:


最后,再審視一下上面等式(3)表達的z_ez_n的關系。注意到這個等式函數(shù)是非線性關系的,意思是在近平面時會有非常高的精度,反過來在遠平面會有非常低的精度。如果區(qū)間[-n,-f]是個很大的范圍會導致出現(xiàn)深度(z值)精度的問題(z-fighting),z_e改變很小的值不會計算得到對應的z_n的值。所以,n和f之間的區(qū)間應該小點以減少z深度的精度問題。
z-fighting,可以看到右圖跨度大,打圈處的f值由于精度問題都會對應1

正交投影

構造正交投影矩陣 GL_PROJECT比構造透視投影矩陣簡單多了,只需要在相機空間里線性的映射x_e,y_e,z_e到NDC空間。我們只需要縮放長方體變成正方體,然后移到原點位置,讓我們看看GL_PROJECTION的各元素之間的線性關系吧。


相機空間的x映射到NDC空間的x,同透視投用一樣,可以先平移到原點再平移回來

注意,沒有投影,NDC空間的x直接和相機空間的x成線性關系
相機空間的y映射到NDC空間的y,同透視投用一樣,可以先平移到原點再平移回來
注意,沒有投影,NDC空間的y直接和相機空間的y成線性關系
相機空間的z映射到NDC空間的z,可以先平移到原點再平移回來

注意,沒有投影,NDC空間的z直接和相機空間的z成線性關系

因為正交投影矩陣是不需要用w計算z值的(z值也是線性關系直接求出來,透視投影需要用w來間接計算z_e和z_n的關系),矩陣GL_PROJECTION的第四行保留為(0,0,0,1),因此完整的正交投影矩陣為:
正交投影矩陣公式

當視錐體是對稱長方體,即r = -l、t = -b,有:

坐標轉換:模型坐標 -> 世界坐標 -> 相機坐標 -> 裁剪坐標 -> NDC坐標
原文鏈接

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容