开始的话:顶点坐标变换时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++):
D3DXMATRIX matWorld;
//
世界变换矩阵
D3DXMATRIX matTranlate,matRotation,matScale;
//
变换矩阵,旋转矩阵,缩放矩阵
D3DX
MatrixScaling(
&
matScale,
1.0f
,
1.0f
,
5.0
);
//
在Z轴上放大5倍
FLOAT fAngle
=
60
*
(
2.0f
*
D3DX_PI)
/
360.0f
;
//
计算需要旋转的角度
D3DXMatrixRotationY(
&
matRotation,fAngle);
//
绕Y轴旋转60度
D3DXMatrixMultiply(
&
matTranlate,
&
matScale,
&
matRotation);
//
组合两个矩阵
D3DXMatrixTranslation(
&
matTranlate,
30.0f
,
0.0f
,
0.0f
);
//
沿X平移30个单位
D3DXMatrixMultiply(
&
matWorld,
&
matWorld,
&
matTranlate);
//
组合得到世界变幻矩阵
g_pd3dDevice
->
SetTransform(D3DTS_WORLD,
&
matWorld);
//
为Direct设备设置世界变换矩阵
通过以上的代码,就可以使每个将要输出的物体通过相同的变换矩阵放到世界坐标系中(关于没种变换的详细计算方法以后有机会补上)
2,观察变换和观察坐标系(世界坐标变为以摄像机位置为原点的坐标):
观察坐标系是以摄像机(屏幕中显示的图形就是虚拟摄像机拍摄在胶片上的景物)为摄像机位置为原点,摄像机观察的方向向着Z轴而建立的坐标系,他由世界坐标变为观察坐标就是观察变换。
原理如下图所示(从左到右三幅图)
观察变换使用世界坐标点乘观察变换矩阵,观察变换矩阵根据摄像机在世界坐标系的所在位置及其观察方向在世界坐标系中的方向,将世界坐标系下的所有对象重新定位。以下代码是设置观察变换矩阵的方法:
D3DXVECTOR3 vEyePt(
0.0f
,
3.0f
,
-
5.0f
);
//
摄像机在世界坐标系中所在的位置
D3DXVECTOR3 vLookatPt(
0.0f
,
0.0f
,
0.0f
);
//
摄像机观察点的位置
D3DXVECTOR3 vUpVec(
0.0f
,
1.0f
,
0.0f
);
//
摄像机的向上量,通常为(0,1,0)指与Y轴
D3DXMATRIX matView;
D3DXMatrixLookAtLH(
&
matView,
&
vEyePt,
&
vLookatPt,
&
vUpVec);
//
生成观察变换矩阵,此为左手坐标系专用函数
g_od3dDevice
->
SetTransfrom(D3DTS_VIEW,
&
matView);
//
设置观察变换矩阵
3,光照:
因不涉及到坐标变换(暂略)
4,投影变换和投影坐标系(将去景截头体的点变成身前矩形中的点):
将观察坐标系上的三维物体投影到二维表面上,就是投影变换,其坐标系以胶片中心为参考原点(此坐标系的坐标为浮点坐标)。
投影变换在DirectX中有两种基本的方法:正交投影和透视投影。
(1) 正交投影(感觉这种比较少用到):
物体坐标沿观察坐标系的z轴平行投影到观察平面上,观察点和观察平面间的距离不会影响物体的大小。其取景范围是一个矩形
代码:
D3DXMATRIX matProject;
D3DXMatrixOrthoLH(
&
matProject,w,h,znear,zfar);
//
w,h为宽和高,znear和zfar为最近和最远距离,使用后得到变换矩阵
g_pd3dDevice
->
SetTransform(D3DTS_PROJECTION,
&
matProject);
(2)透视投影:
原理是将一个取景截头体转换成一个立方体,因为截头体的近端比远端小,所以变为立方体时,近端的物体就被放大。而对象的距离离摄像机越远则成像越小。
在这里提供一个函数:
D3DXMATRIX ProjectionMatrix(
const
float
near_plane,
//
到近裁面的距离
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,视区变换和屏幕坐标系(化为屏幕像素):
将投影坐标变换为基于像素的屏幕坐标,需要定义视区的大小。
关于这个先给出一个结构体:
typedef
struct
_ D3DVIEWPORT9
{
DWORD x;
//
视区左上角的X坐标
DWORD y;
//
视区左上角的Y坐标
DWORD Width;
//
视区宽度
DWORD Height;
//
视区高度
float
MinZ;
//
视区内景物的最小深度,0.0f~1.0f
float
MaxZ;
//
视区内景物的最大深度,0.0f~1.0f
}
以下是一个设置视区的代码:
RECT rect;
GetClientRect(hWnd,
&
rect);
//
hWnd为绘制窗口的句柄
D3DVIEWPORT9 pViewport
=
{
0
,
0
,rect.right,rect.bottom,
0.0f
,
1.0f
};//定义D3DVIEWPORT
if
(SUCCESSED(pd3dDevice
->
SetViewport(
&
pViewport)))//设置视区
{
.
//
处理代码