開始的話:頂點座標變換時Direct3D學習中的入門基礎,在這裏將詳述其原理:
Direct3D中渲染三維對象的過程分爲兩個階段:《1》T&L(Transforming and Lighting),即座標變換和光照;
《2》光柵化處理階段。
一,T&L流水線:
如下圖:
1,世界變換和世界座標系(局部座標變爲世界座標):
物體在三維空間中的變形和運動的過程稱爲世界變換(平移,旋轉,縮放),這個三維空間就是世界空間,其座標系就是三維座標系
世界變換事實上就是將物體頂點從模型空間轉換到世界空間中,模型空間其實就是在三維設計軟件(如3DSMAX)中爲物體設定的座標系,也稱局部座標系。而世界座標則是所有物體都是用同一個世界座標原點的座標系,變換就是指對模型進行平移,旋轉,縮放及它們的任意組合變換。
使用以下公式對點P1進行世界變換。
P2(X,Y,Z,1)=dot(P1(X,Y,Z,1),M)
M爲世界變幻矩陣,它實現物體的平移,旋轉,縮放,dot爲點乘,P2爲變換後的座標,p1爲變換前的座標。
以下爲實現世界座標矩陣變換的DirectX代碼(C++):
通過以上的代碼,就可以使每個將要輸出的物體通過相同的變換矩陣放到世界座標系中(關於沒種變換的詳細計算方法以後有機會補上)
2,觀察變換和觀察座標系(世界座標變爲以攝像機位置爲原點的座標):
觀察座標系是以攝像機(屏幕中顯示的圖形就是虛擬攝像機拍攝在膠片上的景物)爲攝像機位置爲原點,攝像機觀察的方向向着Z軸而建立的座標系,他由世界座標變爲觀察座標就是觀察變換。
原理如下圖所示(從左到右三幅圖)
觀察變換使用世界座標點乘觀察變換矩陣,觀察變換矩陣根據攝像機在世界座標系的所在位置及其觀察方向在世界座標系中的方向,將世界座標系下的所有對象重新定位。以下代碼是設置觀察變換矩陣的方法:
3,光照:
因不涉及到座標變換(暫略)
4,投影變換和投影座標系(將去景截頭體的點變成身前矩形中的點):
將觀察座標系上的三維物體投影到二維表面上,就是投影變換,其座標系以膠片中心爲參考原點(此座標系的座標爲浮點座標)。
投影變換在DirectX中有兩種基本的方法:正交投影和透視投影。
(1) 正交投影(感覺這種比較少用到):
物體座標沿觀察座標系的z軸平行投影到觀察平面上,觀察點和觀察平面間的距離不會影響物體的大小。其取景範圍是一個矩形
代碼:
(2)透視投影:
原理是將一個取景截頭體轉換成一個立方體,因爲截頭體的近端比遠端小,所以變爲立方體時,近端的物體就被放大。而對象的距離離攝像機越遠則成像越小。
在這裏提供一個函數:
const float far_plane,//到遠裁面的距離
const float fov_horiz,//視角的水平範圍(弧度)
const float fov_vert)//視角的垂直範圍(弧度)
{
float h,w,Q;
w = (float)1/tan(fov_horis*0.5);//cot(fov_horis/2)
h = (float)1/tan(fov_vert*0.5);//cot(fov_horis/2)
Q = far_plane/(far_plane-near_plane);
D3DXMATRIX matProject;
ZeroMemory(&matProject,sizeof(matProject));
matProject(0,0) = w;
matProject(1,1) = h;
matProject(2,2) = Q;
matProject(3,2) = -Q*near_plane;
matProject(2,3) = 1;
return matProject;
}
得到矩陣(這個矩陣的參數由上面函數得到,因爲要畫圖才能說明白,所以就算了,有興趣的可以自己去看看相關資料)
w 0 0 0
0 h 0 0
0 0 Q 1
0 0 -Qz 0
此矩陣即爲投影變換矩陣。可以使用DirectXapi
D3DXMatrixPerspectiveFovLH()來構造一個投影矩陣。
5,視區變換和屏幕座標系(化爲屏幕像素):
將投影座標變換爲基於像素的屏幕座標,需要定義視區的大小。
關於這個先給出一個結構體:
{
DWORD x;//視區左上角的X座標
DWORD y;//視區左上角的Y座標
DWORD Width;//視區寬度
DWORD Height;//視區高度
float MinZ;//視區內景物的最小深度,0.0f~1.0f
float MaxZ;//視區內景物的最大深度,0.0f~1.0f
}
以下是一個設置視區的代碼:
GetClientRect(hWnd,&rect);//hWnd爲繪製窗口的句柄
D3DVIEWPORT9 pViewport = {0,0,rect.right,rect.bottom,0.0f,1.0f};//定義D3DVIEWPORT
if(SUCCESSED(pd3dDevice->SetViewport(&pViewport)))//設置視區
{