原文在MS DirectX的帮助文档中:
DirectX Graphics ->Direct3D 9 ->Tutorials and Samples->Tutorials->Direct3D Tutorials
->Tutorial 4: Creating and Using Lights
翻译本文主要为了记录自己的学习过程, 并希望能结交一些学习DirectX的朋友. 由于该指南是在前三个指南的基础上编写的, 之前阅读后没有及时把内容翻译下来, 以后陆续补上.
由于英文水平很菜有些语句翻译不准确, 各位看了见谅, 如果有错误和不足之处欢迎指出. (翻译中有些语句我已经省去, 并加入了一些注释, 为了养成一些习惯我对代码进行了一点点地修改.)
正文:
Direct3D光照使3D对象的真实感大大增加。当使用光照,场景中每一个几何对象将根据其位置和使用的光照类型被照亮。本例子介绍光照和材质两个话题。
代码路径:
(
SDK root)/Samples/C++/Direct3D/Tutorials/Tut04_Lights
步骤1 -- 初始化场景中的几何体
使用光照其中一个要求是每一个平面必须具有法线。因此,光照程序中使用与以往不同的顶点结构类型。新定义的顶点结构具有3D坐标和平面法线。平面法线用于Direct3D内部的光照计算。
struct
CUSTOMVERTEX
{
D3DXVECTOR3 position; //3D顶点坐标向量
D3DXVECTOR3 normal; //顶点所在平面的法线向量
};
//自定义自由顶点格式(FVF)
#define
D3DFVF_CUSTOMVERTEX
(D3DFVF_XYZ | D3DFVF_NORMAL)
定义了正确的顶点格式后,光照例子程序调用应用程序定义用于创建圆筒的函数InitGeometry。首先需要创建顶点缓冲存储存储圆筒的顶点。
//创建顶点缓冲, g_pVB为顶点缓冲的接口指针 IDirect3DVertexBuffer9*
If(FAILED(g_pd3dDevice->CreateVertexBuffer(50*2*sizeof(
CUSTOMVERTEX),
0,
D3DFVF_CUSTOMVERTEX),
D3DPOOL_DEFAULT, &g_pVB, NULL) )
return E_FAIL;
下一步需要用圆筒的顶点数据填充顶点缓冲,注意以下代码,对每一个顶点计算其坐标与法线向量。
CUSTOMVERTEX* pVertices;
if( FAILED(g_pVB->Lock(0, 0, (void**)&pVertices, 0) ) )
return E_FAIL;
//sin(x+y) = sin(x)*cos(y) + cos(x)*sin(y);
//cos(x+y) = cos(x)*cos(y) - sin(x)*sin(y);
FLOAT fDelta = (FLOAT)(2*D3DX_PI/(50-1));
FLOAT fDeltaSin = sinf(fDelta);
FLOAT fDeltaCos = cosf(fDelta);
FLOAT fSin = 0.0f;
FLOAT fCos = 1.0f;
FLOAT fTmpSin = 0.0f;
for(DWORD i=0; i<49; i++)
{
pVertices[i<<1].position = D3DXVECTOR3(fSin, -1.0f, fCos);
pVertices[i<<1].normal = D3DXVECTOR3(fSin, 0.0f, fCos);
pVertices[(i<<1)+1].position = D3DXVECTOR3(fSin, 1.0f, fCos);
pVertices[(i<<1)+1].normal = D3DXVECTOR3(fSin, 0.0f, fCos);
fTmpSin = fSin;
fSin = fSin * fDeltaCos + fCos * fDeltaSin;
fCos = fCos * fDeltaCos - fTmpSin * fDeltaSin;
}
//防止出现累计误差
pVertices[98].position = D3DXVECTOR3(fSin, -1.0f, fCos);
pVertices[98].normal = D3DXVECTOR3(fSin, 0.0f, fCos);
pVertices[99].position = D3DXVECTOR3(0.0f, 1.0f, 1.0f);
pVertices[99].normal = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
使用圆筒坐标填充顶点缓冲后,即可使用它进行渲染。但是,在渲染圆筒之前首先要设定场景中的材质与光源。
步骤二 --- 设置材质与光源
在Direct3D中使用光照,你需要创建一个或多个光源。要决定几何物体反射的颜色,你需要创建用于渲染几何物体的材质。渲染场景之前,光照程序调用设置材质和方向光源的函数SetupLights。
创建材质:
材质规定几何物体表面受到光照时反射的颜色。下段代码使用D3DMATERIAL9结构创建一个黄色的材质。
D3DMATERIAL9 material;
ZeroMemory(&material, sizeof( D3DMATERIAL9));
material.Diffuse.r = material.Ambient.r = 1.0f;
material.Diffuse.g = material.Ambient.g = 1.0f;
material.Diffuse.b = material.Ambient.b = 0.0f;
material.Diffuse.a = material.Ambient.a = 1.0f;
g_pd3dDevice->SetMaterial( &material );
将材质的漫反射色和环境色设置为黄色。调用SetMaterial函数将该材质用于Direct3D设备的场景渲染。SetMaterial函数仅接受待设置材质的地址作为参数。调用该函数后,每一个图元使用刚设定的材质进行渲染,直到下一次调用SetMaterial指定不同的材质。(因此,如果场景中包含多个不同材质的几何体时,需要将几何体按材质归类后进行渲染。)
创建光源:
Direct3D中具有三种可用的光源类型:
l 点光源
l 方向光源
l 聚光灯
此例子三创建一个方向光源(光线方向一致),改代码使得光源方向随时间不断调整。
下一段代码使用D3DLIGHT9结构创建一个方向光源。
D3DXVECTOR3
vecDir;
D3DLIGHT9
light;
ZeroMemory(&light, sizeof(D3DLIGHT9));
light.Type = D3DLIGHT_DIRECTIONAL;
//光源类型:方向光源
light.Diffuse.r = 1.0f;
//设置光源漫反射色为白色。R, G, B取值[0.0f,1.0f]
light.Diffuse.g = 1.0f;
light.Diffuse.b = 1.0f;
//设置光源方向
vecDir = D3DXVECTOR3( cosf(timeGetTime()/360.0f),
0.0f,
sinf(timeGetTime()/360.0f) );
//归一化方向向量
D3DXVec3Normalize( (D3DXVECTOR3*)&light.Direction, &vecDir );
light.Range = 1000.0f;
//设置光源能照射到的最远距离
调用D3DXVec3Normalize归一化指定光源方向的向量。
光照范围指定该方向光源光照能影响的最远距离。此成员不影响方向光源。
下面的代码调用SetLight设置光源。
g_pd3dDevice->SetLight(0, TRUE);
SetLight函数的第一个参数指定光源索引,(注意:如果该索引相关光源已存在,调用该函数将用新的光源覆盖原有的光源。)第二个参数指定定义该光源的结构指针。
下面代码开启0号光源。
g_pd3dDevice->LightEnable(0, TRUE);
LightEnable函数第一个参数指定光源索引,第二个参数指定开启光源(TRUE)或关闭光源(FALSE)。
下面代码调用Direct3D设备接口的SetRenderState方法开启光照计算。
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
SetRenderState函数第一个参数为需要改变设备状态变量,第二个参数设置该状态的值。
最后再次调用SetRenderState开启环境光(环境光使场景中每一个物体接受相同环境光照)。
g_pd3dDevice->SetRenderState(D3DRS_AMBIENT, 0x00202020);
上面的代码为场景设置灰色的环境光。