3D 圖形光柵化的透視校正問題?

最近在做本科畢業設計,想動手寫一個 3D 圖形軟體光柵化器。目前在做紋理貼圖的時候遇到了麻煩,關於透視校正有幾個不太明白的地方:

1. 透視校正發生在哪一個階段,是在攝影變換與透視變換之間,還是在視口變換之後進行插值之前?

2. 透視校正的具體做法是什麼?是包括 z 點在內的坐標點,紋理坐標,顏色,法線等等都在插值前除以 z,插值後再乘以 z 嗎?

3. w 的存在作用僅僅只是為了齊次坐標嗎?為何一些說法是除以 w 而不是 z,我只能找到的兩者之間的關係是 w 值就是攝影空間中的 z 值。


透視紋理繪製發生在最後階段,坐標已經完成projection,剔除,裁剪了,然後頂點/w,開始批量繪製掃描線之前,這時候開始計算紋理的位置。

使用w還是用z,關係不大,早年的3d引擎,直接/z的,只是後面標準化了以後,發現w更好用,可以同時表示透視投影和正交投影。同時頂點經過標準的mvp矩陣運算後,w和z是承線性關係的,方便對z/w做 [0,1] 的cvv裁剪。你可以理解成w就是另外一個z。以前屏幕坐標:

x" = x / z * d + A
y" = y / z * d + A

現在是

x" = x / w * d + A
y" = y / w * d + A

然後繪製紋理前,你需要先證明屏幕上兩個點之間,1/w 承線性關係,即屏幕上兩個點X1", X2"之間任意取一點X3",他們的(1/w)值的變化比例相同,即在 t 取任意值有:

x3" = x1" + (x2" - x1") * t
(1 / w3) = (1 / w1) + ((1 / w2) - (1 / w1)) * t

再根據他們在同一個平面上,證明屏幕上兩個點之間,u/w, v/w 承線性關係,即 t 取任意值有:

x3" = x1" + (x2" - x1") * t
(u3 / w3) = (u1 / w1) + ((u2 / w2) - (u1 / w1)) * t
(v3 / w3) = (v1 / w1) + ((v2 / w2) - (v1 / w1)) * t

具體到代碼裡面的做法就是三角形的三個頂點/w以後,u和v也同時/w,然後把w換成自己的倒數:w = 1 / w,及把頂點數據:

(x, y, z, w) + (u, v)

變換成:

(x / w, y / w, z / w, 1 / w) + (u / w, v / w)

然後用 1/w, u/w, v/w進行屏幕空間插值,具體繪製某個點的時候,先從1/w求倒得到w,然後乘以 u/w, v/w得到 u, v,就可以了。

更進一步,可以證明,所有在三維空間里同x,y,z成線性關係的變數,不管是紋理坐標,頂點色或者法向還是其他,他們在屏幕空間里的插值規則都可以通過:插值前先/w,插值後要用時再 * w得到具體值,然後我們把這類三維空間里同x,y,z成線性關係的變數統進行統一的批量處理,和OpenGL的 attribute,varying處理方法相同。

--


1、透視校正可以在triangle setup時插值三角形內部uv的時候做。

2、透視校正不是先除以在乘以,透視校正的原因是要求插值的時候相同空間的量插值相同空間的量,不要使用perspective space的量去插值view space的量。主要是uv:

直觀的說,如果不使用1/z插值投影的話,紋理會是這樣的 :

原因,看下圖:

從Eye往3D Line劃線,初中學習的三角學幾何知識就可以告訴我們,Near面上的3D line投影的變化比例跟3D空間裡面變化比例是不一樣的。所以如果直接在屏幕空間線性插值屏幕空間量(如uv),會導致扭曲。

這一切都是透視惹的禍。

拿near panel上的量去插值uv是不對的,uv在3d空間中是按照空間坐標(xyz)線性插值計算的,透視後投影到投影平面,空間量變成了屏幕空間量,所以在屏幕空間插值要按照1/z來插值才成。因為恰好1/z在屏幕空間是線性的。

推導1/z的過程,可以參考:【GPU技術】透視校正插值

3、其中透視矩陣,保留了z值在w處,前面的xyz都是乘以了z值的。所以在傳入管線之後,要把每一個xyz值都除以一個w。這樣這些量就可以按照屏幕空間直接線性插值。而uv沒有經過任何變換,因此不在屏幕空間,前面也說了其實他們是在view裡面的。

關於1/w、1/z,樓上前輩已經說了。


推薦閱讀:

有哪些辦法繪製分形?
工作6年的程序員,還在原地踏步怎麼辦?
透視裁剪到底發生在哪個階段?
海島奇兵中海邊的浪花是如何實現的,使用shader還是序列幀動畫?

TAG:遊戲開發 | 編程 | 遊戲引擎 | 計算機圖形學 | Direct3D |