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標準化了。
視窗座標系(Window Coordinates)/螢幕座標系(Screen Coordinates)
將標準化裝置座標系(NDC)應用於視口轉換。NDC將縮小和平移以便適應螢幕的透視。視窗座標系最終傳遞給OpenGL的管道處理變成了fragment。glViewPort()函式用來定義最終圖片對映的投影區域。同樣,glDepthRange()用來決定視窗座標系的z座標。視窗座標系由下面兩個方法給出的引數計算出來
glViewPort(x,y,w,h);
glDepthRange(n,f);
//這裡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