投影變換完成的是如何將三維模型顯示到二維視口上,這是一個三維到二維的過程。你可以將投影變換看作是調整照相機的焦距,它模擬了为照相機選擇鏡頭的過程。投影變換是所有變換中最复雜的一個。
視錐體是一個三維體,他的位置和攝像機相關,視錐體的形狀决定了模型如何從camera space投影到屏幕上。最常見的投影類型-透視投影,使得離攝像機近的物體投影後較大,而離攝像機較遠的物體投影後較小,透視投影使用棱錐作为視錐體,攝像機位於棱錐的椎頂。該棱錐被前後兩個平面截斷,形成一個棱台,叫做View Frustum,只有位於Frustum內部的模型才是可見的。
透視投影的目的就是將上面的棱台轉換为一個立方體(cuboid),轉換後,棱台的前剪裁平面的右上角點變为立方體的前平面的中心(下圖中弧線所示)。由圖可知,這個變換的過程是將棱台較小的部分放大,較大的部分縮小,以形成最終的立方體。這就是为什麼投影變換會產生近大遠小的效果的原因。變換後的x坐標範圍是[-1, 1],y坐標範圍是[-1, 1],z坐標範圍是[0, 1](OpenGL略有不同,z值範圍是[-1, 1])。
下面來推導一下透視投影矩陣,這样我們就可以自己設置投影矩陣了,就可以模擬神奇的D3DXMatrixPerspectiveLH函數的功能了。那麼透視投影到底做了什麼工作呢?這一部分算是個難點,無論是DX SDK的幫助文檔,還是大多數圖形學書籍,對此都是一帶而過,很少有詳細討論的,早期的DX SDK文檔還討論的稍微多一些,而新近的文檔則完全取消了投影矩陣的推導過程。
我們可以將整個投影過程分为兩個部分,第一部分是從Frustum內一點投影到近剪裁平面的過程,第二部分是由近剪裁平面縮放的過程。假設Frustum內一點P(x,y,z)在近剪裁平面上的投影是P'(x',y',z'),而P'經過縮放後的最終坐標設为P''(x",y",z")。假設所求的投影矩陣为M,那麼根據矩陣乘法可知,如下等式成立。
PM=P'',即
先看第一部分,为了簡化問題,我們考慮YOZ平面上的投影情況,見下圖。設P(x, y, z)是Frustum內一點,它在近剪裁平面上的投影是P'(x', y', z')。(注意:D3D以近剪裁平面作为投影平面),設視錐體在Y方向的夾角为Θ。
由上圖可知,三角形OP'Q'與三角形OPQ相似,於是有如下等式成立。
在看第二部分,將P'縮放的過程,假設投影平面的高度为H,由於轉換後cuboid的高度为2。所以有
又因为投影平面的縱橫比为Aspect,所以
最後看z'',因为在光柵化之前,我們需要對z坐標的倒數進行插值,所以可以將z''寫成z的一次表達式形式,如下
在映射前,z的範圍是[n,f],這裏n和f分別是近遠兩個剪裁平面到原點的距離,在映射後,z''的範圍是[0,1],將數據代入上面的一次式,可得下面的方程組
解這個方程組得到
所以
整理一下得
將X'',y'',z''代入最開始的矩陣乘法等式中得
由上式可見,x'',y'',z''都除以了Pz,於是我們將他們再乘以Pz(這並不該變齊次坐標的大小),得到如下等式。
注意這裏,x即Px,y即Py,z即Pz,解矩陣的每一列得到
於是所求矩陣为
代碼
一般來說,在程序中我們通常给定四個参數來求透視投影矩陣,分別是y方向的視角,縱橫比,近剪裁平面到原點的距離及遠剪裁平面到原點的距離,通過這四個参數即可求出上面的矩陣,代碼如下。
D3DXMATRIX BuildProjectionMatrix(float fov, float aspect, float zn, float zf){ D3DXMATRIX proj; ZeroMemory(&proj, sizeof(proj)); proj.m[0][0] = 1 / (tan(fov * 0.5f) *aspect) ; proj.m[1][1] = 1 / tan(fov * 0.5f) ; proj.m[2][2] = zf / (zf - zn) ; proj.m[2][3] = 1.0f; proj.m[3][2] = (zn * zf) / (zn - zf); return proj ;}
矩陣求解完畢,現在可以用如下代碼試試效果,這和使用D3D函數D3DXMatrixPerspectiveFovLH所得效果是一致的。
D3DXMATRIX proj = BuildProjectionMatrix(D3DX_PI / 4, 1.0f, 1.0f, 1000);g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &proj) ;
Happy Coding!!!
联系客服