1. 程式人生 > >OpenGL中視口變換的矩陣求解

OpenGL中視口變換的矩陣求解

渲染過程中座標系的變換

裁剪在投影時進行,透視投影的裁剪區域為稜錐,正交投影的裁剪區域為長方體。

標準座標系可以直接進行視口變換,將x,y變換到0-w,0-h,z變換到0-1。

頂點幾何變換包含:模型檢視變換,投影變換,視口變換。

注意: 在程式中視口的位置是在初始化時指定的,一旦指定,如果還想要在相同的視窗中渲染圖形,就不能更改視口矩陣。


標準化裝置座標系(NDC)

將剪切面座標系除以w所得(關於w的討論可在此處:http://http://www.songho.ca/math/homogeneous/homogeneous.html),它被稱為視角除(perspective division)它更像是視窗座標系,只是還沒有轉換或者縮小到螢幕畫素。其中它取值範圍在3個軸向從-1到1標準化了。

OpenGL Normalized Device Coordinates

視窗座標系(Window Coordinates)/螢幕座標系(Screen Coordinates)

將標準化裝置座標系(NDC)應用於視口轉換。NDC將縮小和平移以便適應螢幕的透視。視窗座標系最終傳遞給OpenGL的管道處理變成了fragment。glViewPort()函式用來定義最終圖片對映的投影區域。同樣,glDepthRange()用來決定視窗座標系的z座標。視窗座標系由下面兩個方法給出的引數計算出來

glViewPort(x,y,w,h);

glDepthRange(n,f);

OpenGL Window Coordinates 

//這裡h/2 應該改為 -h/2

視口轉換公式很簡單,通過NDC和視窗座標系的線性關係得到:


視口變換在投影變換之後,投影變換是將viewing frustum(視景體)變換為一個cuboid(立方體),如下圖


關於透視投影的矩陣求解,請參考透視投影詳解

視口變換則是將這個cuboid中的物體變換到視口中,見下圖




其中cuboid的座標範圍是

-1 ≤x≤1

-1 ≤y≤1

0 ≤z≤1

而viewport的座標範圍是

X ≤x≤X + Width

Y ≤y≤Y + Height

MinZ ≤z≤MaxZ

注:由上圖知,視口的起點為(X,Y),寬高分別為Width和Height,x軸向右為正,y軸向下為正,y軸的方向與三維座標正好相反。視口是一個2D平面,但是在viewport變換中,Z座標也是跟著變換的,只是在這個圖中沒有體現。

先求變換矩陣的第一列

Cuboid中的左上角點(-1, 1, -1, 1)對映到viewport中的起點(X, Y, MinZ, 1),

Cuboid中的右上角點(1, 1, -1, 1)對映到viewport中的點(X+Width, Y, MinZ, 1),

Cuboid中的左下角點(-1, -1, -1, 1)對映到viewport中的點(X, Y+Heiht, MinZ, 1),

假設變換矩陣的第一列為[x’, y’, z’, w' ]T

根據矩陣乘法有

[-1, -1, 0, 1]* [x’, y’, z’, w' ]T = X

[-1, 1, 0, 1]* [x’, y’, z’, w' ]T = X

[1, 1, 0, 1]* [x’, y’, z’, w' ]T = X+Width

這裡都把Z' 前面的0改為-1;;

對應的兩個方程為

-1*x' - 1*y' +0*z' +1*w' = X

-1*x’ + 1*y’ + 0*z’ + 1*w’ = X

1*x’ + 1*y’ + 0*z’ + 1*w’ = X+Width

解之得

x’ = Width/2

y’ = 0

z’ = 0

w’ = X + Width/2

再求第二列

列方程(這裡省略了x’,z’,但結果不變,下同)

y’ + 1*w’=Y

-1*y’ + 1*w’=Y+Height


解之得

y’ = -Height/2

w’ = Y + Height/2

最後求第三列

Cuboid中的右上角點(1,1,1, 1)對映到viewport中的點(X+Width, Y, MaxZ, 1),

列方程

-1*z’ + 1*w’ = MinZ

1*z’ + 1*w’ = MaxZ

解之得

z’ = (MaxZ – MinZ)/2

w’ = (MaxZ + MinZ)/2

組合以上各列,得到視口變換矩陣

 

上面第三列的MaxZ-MinZ需要改成(MaxZ – MinZ)/2,MinZ需要改成(MaxZ+ MinZ)/2