四、顶点坐标变换和设置光照
1,顶点坐标变换
我们在计算机图形学里已经学过要显示一个三维景物模型,应用程序必须经历从世界坐标系到屏幕坐标系的转换,这里主要有三个坐标系及相应的变换:世界坐标系和模型几何变换,观察坐标系和取景变换,以及投影坐标系与投影变换。相关概念在我们大学《计算机图形学》的课程里已经有很清楚的讲解,这里主要说明在D3D中如何实现。
我个人对图形学中各种坐标变换的理解就是矩阵运算,更直接地说,就是矩阵乘法。在D3D中有一个函数IDirect3DDevice9:: SetTransform()用以设定D3D顶点变换矩阵。这个函数的第一个参数用以标识当前设定的矩阵类型(D3DTS_WORLD,D3DTS_VIEW和D3DTS_PROJECTION ),第二个参数就是矩阵的指针。用法见后面的示例代码。
I.模型几何变换
这个变换的主要工作是通过矩阵乘法完成图形平移,旋转和放缩操作,在D3D功能扩展库上提供了相应的生成函数。
D3DXMATRIXA16是D3D提供的一个变换矩阵类型,生成相关变换的矩阵如下:
D3DXMatrixTransformation()//生成一个平移变换矩阵
D3DXMatrixRotationX()//生成一个绕X轴旋转的变换矩阵
D3DXMatrixRotationY()//生成一个绕Y轴旋转的变换矩阵
D3DXMatrixRotationZ()//生成一个绕Z轴旋转的变换矩阵
D3DXMatrixScaling()//生成一个缩放变换矩阵
下面的示例代码,完成了一个绕X轴旋转的变换矩阵的设定:
D3DXMATRIXA16 matWorld;
//建立一个绕X轴动态旋转的模型几何变换矩阵
UINT iTime = timeGetTime() % 1000;
FLOAT fAngle = iTime * (2.0f * D3DX_PI) / 1000.0f;
D3DXMatrixRotationX(&matWorld, fAngle);
g_pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld);
如果需要复合多个变换,只需将各个变换矩阵相乘,D3D也提供了一个完成这个乘法的函数D3DXMatrixMultiply,示例代码如下:
D3DXMATRIXA16 matTranslation;
D3DXMatrixTranslation(&matTranslation,fAngle,0.0f,0.0f);
D3DXMatrixMultiply(&matWorld,&matWorld,&matTranslation);
II.取景变换
取景变换就是设置一个虚拟的相机,使用函数D3DXMatrixLookAtLH就可以生成一个观察矩阵,使用D3D内置的D3DXVECTOR3类型进行设置,如下例
//建立一个观察矩阵
D3DXVECTOR3 vEyePt(0.0f, 3.0f, -5.0f);//观察点的位置坐标
D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f);//被观察点的坐标
D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f);//虚拟相机的向上向量
D3DXMATRIXA16 matView;
D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec);
g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView);
观察坐标系的几个坐标含义在图形学里有明确的定义,这里也不赘述了哦!
III.投影变换
投影的目的就是将三维景物投影到二维屏幕上显示,主要包含正交投影和透视投影两大类。
D3D提供函数D3DXMatrixOrthoLH()生成一个正交投影矩阵.需要设置的参数为存放地址,取景宽度,取景高度,取景到观察点的最短距离以及取景到观察点的最远距离。
函数D3DXMatrixPerspectiveFovLH()生成一个透视投影矩阵,用法如下例
// 建立一个透视投影矩阵
D3DXMATRIXA16 matProj;
//矩阵存放地址:matProj
//在竖直方向的成像角度(弧度表示)D3DX_PI/4
//取景范围的纵横比:1.0f
//取景到观察点的最短距离:1.0f
//观察点的最远距离:100.0f
D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f);
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);
2、视区变换
在完成了三维顶点坐标的转换后,需要通过定义屏幕显示区域属性完成顶点裁剪和将点的投影坐标转换为屏幕坐标,予以最终显示,此变换过程称为视区变换。
D3D中定义了D3DVIEWPORT9结构,用以设置进行视区变换的各项参数,原型如下:
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
} D3DVIEWPORT9;
函数SetViewport用来设置D3D的视区。示例代码如下:
D3DVIEWPORT9 vp;
vp.X = 0;
vp.Y = 0;
vp.Width = rect.right;
vp.Height = rect.bottom;
vp.MinZ = 0.0f;
vp.MaxZ = 1.0f;
g_pd3dDevice->SetViewport(&vp);
3、光照设置
要让需要显示的图形更符合现实场景,就必须模拟真实世界中的光照环境,在D3D中使用函数IDirect3DDevice9::SetRenderState来设置整体的光照模型,主要包括环境光,漫反射和镜面反射。
光照模型说明了图形工作系统以怎样的方式计算灯光照射在物体上的颜色值,而不是具体的灯光。因此,我们还需要定义光源,它包含了三维场景中具体灯光的位置、方向等信息。D3D中提供了3种光源类型:方向光、点光源和聚灯光,用枚举类型D3DLIGHTTYPE表示。而光源的属性由结构D3DLIGHT9来组织。各属性值可以参考示例代码和开发文档。使用的设置函数为函数IDirect3DDevice9::SetLight。需要注意的是,仅仅调用设置函数并没有激活D3D使用光照进行计算,还需要调用函数IDirect3DDevice9::SetLightEnable启用灯光。
除光源外,影响光照效果的另一大要素就是物体材质,它决定了物体反射什么颜色以及能反射多少光线。D3D中提供结构D3DMATERIAL9来定义材质的属性,并由IDirect3DDevice9::SetMaterial完成设置。
下面是设置光照示例代码:
//在激活了光照计算并使用了漫反射或镜面反射,在FVF中就需要设定顶点的法线向量
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL)
VOID Cdig_D3DDlg::SetupLights()
{
// Set up a material.
D3DMATERIAL9 mtrl;
ZeroMemory(&mtrl, sizeof(mtrl));
mtrl.Diffuse.r = mtrl.Ambient.r = 1.0f;
mtrl.Diffuse.g = mtrl.Ambient.g = 1.0f;
mtrl.Diffuse.b = mtrl.Ambient.b = 0.0f;
mtrl.Diffuse.a = mtrl.Ambient.a = 1.0f;
g_pd3dDevice->SetMaterial(&mtrl);
//set up lights
D3DXVECTOR3 vecDir;
D3DLIGHT9 light;
ZeroMemory(&light, sizeof(light));
//方向光
light.Type = D3DLIGHT_DIRECTIONAL;
light.Diffuse.r = 0.0f;
light.Diffuse.g = 1.0f;
light.Diffuse.b = 0.0f;
vecDir = D3DXVECTOR3(10, 10, -10);
//将向量单位化
D3DXVec3Normalize((D3DXVECTOR3*)&light.Direction, &vecDir);
light.Range = 1000.0f;
g_pd3dDevice->SetLight(0, &light);
g_pd3dDevice->LightEnable(0, TRUE);
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
D3DXVECTOR3 vecPos2;
D3DLIGHT9 light2;
ZeroMemory(&light2, sizeof(light2));
//点光源
light2.Type = D3DLIGHT_POINT;
light2.Diffuse.r = 0.9f;
light2.Diffuse.g = 0.0f;
light2.Diffuse.b = 0.0f;
light2.Position = D3DXVECTOR3(10 * sinf(timeGetTime()/350.0f), 10, 10 * cos(timeGetTime()/350.0f));
light2.Range = 100.0f;
light2.Attenuation0 = 1.0f;
g_pd3dDevice->SetLight(1, &light2);
g_pd3dDevice->LightEnable(1, TRUE);
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
//打开环境光,并设置颜色
g_pd3dDevice->SetRenderState(D3DRS_AMBIENT, 0x00202020);
}
转自:http://blog.csdn.net/shi06/archive/2010/07/29/5773777.aspx