Direct3D 初涉: 光照

1. 光照的组成

在 Direct3D 的光照模型中,光源发出的光由以下 种分量或者 种类型的光组成:

(1) 环境光(Ambient Light):光经其他表面反射到达物体表面,并照亮整个场景。个人理解:白天阴天的时候为什么还能看见物体?环境光!

(2) 漫射光(Diffuse Light):光沿着特定的方向传播。当它到达某一表面时,将沿着各个方向均匀反射。无论从那个方向来看,表面亮度均相同,所以采用该模型时,无须考虑观察者的位置。

(3) 镜面光(Specular Light):光沿特定方向传播。当此类光到达一个表面时,将严格地沿着另一个方向反射,从而形成只能在一定角度范围内才能观察到的高亮度照射。相对前两种光照类型,镜面光的计算量要大的多,因此默认情况下 Direct3D 不进行镜面反射计算;如果想启动镜面光,必须将绘制状态 D3DRS_SPECULARENABLE 设为 true 

 

每种类型的光都可用结构 D3DCOLORVALUE 或 D3DXCOLOR 来表示,这些类型描述了光线的颜色( Alpha 值被忽略)

 

2. 材质

在现实世界中,我们所看到的物体颜色是由该物体所反射的光的颜色决定的(物体对光进行的部分吸收)。例如:我的显示器边缘是黑色的,这是由于它吸收了所有非黑色的光,所以它显示为黑色。

Direct3D 通过定义物体的材质(materials)来模拟同样的现象。允许我们定义物体表面对各种颜色光的反射比例。在代码中使用结构 D3DMATERIAL9 来表示。Direct3D 使用 SetMaterial 来设定当前材质。

 

当物体能收吸收所有的光时,它将显示为黑色;当物体能反射所有的光时,它将显示为白色。

 

3. 顶点法线

面法线是一个描述多边形朝向的向量。顶点法线描述构成多边形各个顶点的法线。 Direct3D 需要知道顶点的法线方向,以确定光线到达表示时的入射角。

 

4. 光源

Direct3D 支持三种类型的光源:

(1) 点光源(Point lights),在世界坐标系位置固定,并且向所有的方向发射光线。

(2) 方向光(Directional lights),没有位置信息,发射的光线平行地沿着某一特定的方向传播。

(3) 聚光灯(Spot lights),和手电筒类似,有位置信息,其发射的光线成锥形,沿着特定的方向传播。

在程序代码中,光源用结构 D3DLIGHT9 来表示。

 

5. 例程(Lighting)

(1) 将光照和颜色整合到框架中,将 DrawColorTriangle 中的GlobalColorConstant.h 添加到框架中。

(2) 新建文件 GlobalMtrlConstant.h ,保存常用材质的常量。

 

#ifndef __GLOBALMTRLCONSTANCE_H__
#define __GLOBALMTRLCONSTANCE_H__

D3DMATERIAL9 InitMtrl(D3DXCOLOR ambient, D3DXCOLOR diffuse, D3XCOLOR specular,
D3DXCOLOR emissive, float power)
{
D3DMATRIAL9 mtrl;
mtrl.Ambient = ambient;
mtrl.Diffuse = diffuse;
mtrl.Specular = specular;
mtrl.Emissive = emissive;
mtrl.Power = p;
return mtrl;
}

const D3DMATERIAL9 WHITE_MTRL = InitMtrl(WHITE, WHITE, WHITE, BLACK, 8.0f);
const D3DMATERIAL9 RED_MTRL = InitMtrl(RED, RED, RED, BLACK, 8.0f);
const D3DMATERIAL9 GREEN_MTRL = InitMtrl(GREEN, GREEN, GREEN, BLACK, 8.0f);
const D3DMATERIAL9 BLUE_MTRL = InitMtrl(BLUE, BLUE, BLUE, BLACK, 8.0f)
const D3DMATERIAL9 YELLOW_MTRL = InitMtrl(YELLOW, YELLOW, YELLOW, BLACK, 8.0f);

#endif /* __GLOBALMTRLCONSTANCE_H__ */

(3) 新建工程 Lighting ,将框架代码拷贝到新工程中(以下在 Lighting 上操作)

(4) 新建文件 NormalVertex.h,添加法线顶点的定义。

 

#ifndef __NORMALVERTEX_H__
#define __NORMALVERTEX_H__

struct NormalVertex
{
public:
NormalVertex()
{
}

NormalVertex(float x, float y, float z, float nx, float ny, float nz)
{
m_x = x; m_y = y; m_z = z;
m_nx = nx; m_ny = ny; m_nz = nz;
}

public:
float m_x, m_y, m_z;
float m_nx, m_ny, m_nz;
static const DWORD FVF;
};

const DWORD NormalVertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL;

#endif /* __NORMALVERTEX_H__ */

(5) 向 Direct3DFrame 类中添加成员变量,保存金字塔的定点数据。

IDirect3DVertexBuffer9 * m_pyramid; // 金字塔顶点缓存

(6) 在 Setup 中添加场景光照和其他操作

 

bool Direct3DFrame::Setup()
{
// 启用光照
m_device->SetRenderState(D3DRS_LIGHTING, true);

// 创建顶点缓存,并指定构成金字塔的三角形单元的顶点,顶点法线预先计算好
m_device->CreateVertexBuffer(
12 * sizeof(NormalVertex),
D3DUSAGE_WRITEONLY,
NormalVertex::FVF,
D3DPOOL_MANAGED,
&m_pyramid,
0);

NormalVertex* nv;
m_pyramid->Lock(0, 0, (void**)&nv, 0);

// font face
nv[0] = NormalVertex(-1.0f, 0.0f, -1.0f, 0.0f, 0.707f, -0.707f);
nv[1] = NormalVertex( 0.0f, 1.0f, 0.0f, 0.0f, 0.707f, -0.707f);
nv[2] = NormalVertex( 1.0f, 0.0f, -1.0f, 0.0f, 0.707f, -0.707f);

// left face
nv[3] = NormalVertex(-1.0f, 0.0f, 1.0f, -0.707f, 0.707f, 0.0f);
nv[4] = NormalVertex( 0.0f, 1.0f, 0.0f, -0.707f, 0.707f, 0.0f);
nv[5] = NormalVertex(-1.0f, 0.0f, -1.0f, -0.707f, 0.707f, 0.0f);

// right face
nv[6] = NormalVertex( 1.0f, 0.0f, -1.0f, 0.707f, 0.707f, 0.0f);
nv[7] = NormalVertex( 0.0f, 1.0f, 0.0f, 0.707f, 0.707f, 0.0f);
nv[8] = NormalVertex( 1.0f, 0.0f, 1.0f, 0.707f, 0.707f, 0.0f);

// back face
nv[9] = NormalVertex( 1.0f, 0.0f, 1.0f, 0.0f, 0.707f, 0.707f);
nv[10] = NormalVertex( 0.0f, 1.0f, 0.0f, 0.0f, 0.707f, 0.707f);
nv[11] = NormalVertex(-1.0f, 0.0f, 1.0f, 0.0f, 0.707f, 0.707f);

m_pyramid->Unlock();

// 设置物体材质
D3DMATERIAL9 mtrl;
mtrl.Ambient = WHITE;
mtrl.Diffuse = WHITE;
mtrl.Specular = WHITE;
mtrl.Emissive = BLACK;
mtrl.Power = 0.5f;

m_device->SetMaterial(&mtrl);

// 创建并启用光源
D3DLIGHT9 dir;
::ZeroMemory(&dir, sizeof(dir));
dir.Type = D3DLIGHT_DIRECTIONAL;
dir.Diffuse = WHITE;
dir.Specular = WHITE * 0.3f;
dir.Ambient = WHITE * 0.6f;
dir.Direction = D3DXVECTOR3(1.0f, 0.0f, 0.0f);

m_device->SetLight(0, &dir);
m_device->LightEnable(0, true);

// 重新规范化法向量,启用镜面光
m_device->SetRenderState(D3DRS_NORMALIZENORMALS, true);
m_device->SetRenderState(D3DRS_SPECULARENABLE, true);

D3DXVECTOR3 position(0.0f, 0.0f, -2.5f);
D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
D3DXMATRIX v;
D3DXMatrixLookAtLH(&v, &position, &target, &up);

m_device->SetTransform(D3DTS_VIEW, &v);

// Set the projection matrix.
D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(
&proj,
D3DX_PI * 0.5f, // 90 - degree
(float)m_width / (float)m_height,
1.0f,
1000.0f);
m_device->SetTransform(D3DTS_PROJECTION, &proj);

return true;
}

(7) 在 Display 添加代码,使金字塔显示和旋转起来

 

bool Direct3DFrame::Display(float timeDelta)
{
if (m_device) {
D3DXMATRIX yRot;
D3DXMatrixRotationY(&yRot, m_yRotAngle);
m_yRotAngle += timeDelta;

if( m_yRotAngle >= 6.28f ) {
m_yRotAngle = 0.0f;
}

m_device->SetTransform(D3DTS_WORLD, &yRot);

m_device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0);
m_device->BeginScene();

m_device->SetStreamSource(0, m_pyramid, 0, sizeof(NormalVertex));
m_device->SetFVF(NormalVertex::FVF);
m_device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 4);

m_device->EndScene();
m_device->Present(0, 0, 0, 0);
}
return true;
}

 

(8) 在 Cleanup 中释放资源(在以后的博客中不再提示)。

运行后效果图:

 

 

/*******************************************************/ 

** 本文由 独酌逸醉 原创,转载请注明博客链接,谢谢!

** 小弟初学 Direct3D,文章中如果存在错误,烦请指证,不胜感激!

** 参考书籍:《DirectX 9.0 3D 游戏开发编程基础》 

** 时间:2011.11.23

/*******************************************************/



posted @ 2011-11-23 23:42 独酌逸醉 阅读(...) 评论(...) 编辑 收藏