17. 增加对硬件光照的支持
该游戏项目一开始先创建名为light.h、material.h的两个文件。
light.h头文件用于创建灯源结构,该结构用于创建Direct3D中的灯源对象。以某种方式实现该结构,这样也可将其用于OpenGL或其他渲染系统。光源结构模仿了Direct3D中的D3DLIGHT结构,所以它们是完全兼容的。
light.h
#ifndef _UGP_LIGHT_H_
#define _UGP_LIGHT_H_
struct stLight
{
stLight()
{
type = 0;
posX = 0, posY = 0, posZ = 0;
dirX = 0, dirY = 0, dirZ = 0;
ambientR = ambientG = ambientB = ambientA = 1;
diffuseR = diffuseG = diffuseB = diffuseA = 1;
specularR = specularG = specularB = specularA = 0;
range = 0;
falloff = 0;
attenuation0 = 0;
attenuation1 = 0;
attenuation2 = 0;
theta = 0;
phi = 0;
}
int type;
float posX, posY, posZ;
float dirX, dirY, dirZ;
float ambientR, ambientG, ambientB, ambientA;
float diffuseR, diffuseG, diffuseB, diffuseA;
float specularR, specularG, specularB, specularA;
float range;
float falloff;
float attenuation0;
float attenuation1;
float attenuation2;
float theta;
float phi;
};
#endif
material.h
// ========================================================================
//§ material.h 文件
//§
//§ 【描述】:定义材质结构:stMaterial
//§
// ========================================================================
#ifndef _UGP_MATERIAL_H_
#define _UGP_MATERIAL_H_
struct stMaterial
{
stMaterial()
{
emissiveR = emissiveG = emissiveB = emissiveA = 0;
ambientR = ambientG = ambientB = ambientA = 1;
diffuseR = diffuseG = diffuseB = diffuseA = 1;
specularR = specularG = specularB = specularA = 0;
power = 0;
}
float emissiveR, emissiveG, emissiveB, emissiveA;
float ambientR, ambientG, ambientB, ambientA;
float diffuseR, diffuseG, diffuseB, diffuseA;
float specularR, specularG, specularB, specularA;
float power;
};
#endif
Direct3D渲染系统需要一种创建光源和材质的方法。这可以通过为渲染系统添加三个新函数来实现。第一个函数是SetMaterial(),它的参数包括材质(stMaterial)对象,并将其施加到渲染设备上。第二个新函数是SetLight(),它的参数包括灯源对象、灯源索引(因为Direct3D和OpenGL由多达8个的硬件光源,所以索引值可以是0~7),并且调用必须的Direct3D函数用于在硬件中创建和注册光源。最后一个新函数是DisalbeLight(),它以想要关闭灯源的索引为参数。给出了新增加的CD3DRenderer和CRenderInterface类。
class CRenderInterface
{
// ...The beginning of the class functions...
// 设置材质
virtual void SetMaterial(stMaterial *mat) = 0;
// 设置并启用光源
virtual void SetLight(stLight *light, int index) = 0;
// 关闭光源
virtual void DisableLight(int index) = 0;
// ...The rest of the class's functions...
};
class CD3DRenderer : public CRenderInterface
{
// ...The beginning of the class functions...
// 设置材质
void SetMaterial(stMaterial *mat);
// 设置并启用光源
void SetLight(stLight *light, int index);
// 关闭光源
void DisableLight(int index);
// ...The rest of the class's functions...
};
D3DRenderer.cpp源文件实现了三个和Direct3D打交道的函数。SetMaterial()函数使用材质结构中的信息创建Direct3D材质对象,并调用Direct3D设备函数SetMaterial()将该对象直接发送给Direct3D。这同样使用于SetLight()函数。SetLight()函数的参数包括光源结构,该函数使用该参数设置D3DLIGHT9对象,并将其直接发送给Direct3D。DisableLight()函数的参数包括光源索引,它将关闭注册在该索引上的所有光源。给出了这些函数的源代码。
// 设置材质
void CD3DRenderer::SetMaterial(stMaterial *mat)
{
if(!mat || !m_Device) return;
D3DMATERIAL9 m = { mat->diffuseR, mat->diffuseG,
mat->diffuseB, mat->diffuseA,
mat->ambientR, mat->ambientG,
mat->ambientB, mat->ambientA,
mat->specularR, mat->specularG,
mat->specularB, mat->specularA,
mat->emissiveR, mat->emissiveG,
mat->emissiveB, mat->emissiveA,
mat->power
};
m_Device->SetMaterial(&m);
}
// 设置并启用光源
void CD3DRenderer::SetLight(stLight *light, int index)
{
if(!light || !m_Device || index < 0) return;
D3DLIGHT9 l;
l.Ambient.a = light->ambientA;
l.Ambient.r = light->ambientR;
l.Ambient.g = light->ambientG;
l.Ambient.b = light->ambientB;
l.Attenuation0 = light->attenuation0;
l.Attenuation1 = light->attenuation1;
l.Attenuation2 = light->attenuation2;
l.Diffuse.a = light->diffuseA;
l.Diffuse.r = light->diffuseR;
l.Diffuse.g = light->diffuseG;
l.Diffuse.b = light->diffuseB;
l.Direction.x = light->dirX;
l.Direction.y = light->dirY;
l.Direction.z = light->dirZ;
l.Falloff = light->falloff;
l.Phi = light->phi;
l.Position.x = light->posX;
l.Position.y = light->posY;
l.Position.z = light->posZ;
l.Range = light->range;
l.Specular.a = light->specularA;
l.Specular.r = light->specularR;
l.Specular.g = light->specularG;
l.Specular.b = light->specularB;
l.Theta = light->theta;
if(light->type == LIGHT_POINT) l.Type = D3DLIGHT_POINT;
else if (light->type == LIGHT_SPOT) l.Type = D3DLIGHT_SPOT;
else l.Type = D3DLIGHT_DIRECTIONAL;
m_Device->SetLight(index, &l);
m_Device->LightEnable(index, TRUE);
}
// 关闭光源
void CD3DRenderer::DisableLight(int index)
{
if(!m_Device) return;
m_Device->LightEnable(index, FALSE);
}
还需要在“defines.h”头文件里加入这几行代码:
// Light type defines.
// 光源类型
#define LIGHT_POINT 1 // 点光源
#define LIGHT_DIRECTIONAL 2 // 方向光
#define LIGHT_SPOT 3 // 聚光灯