DirectX SDK 2006学习笔记6——光照和材质
来源:互联网 发布:淘宝宝贝上架最佳时间 编辑:程序博客网 时间:2024/05/19 11:47
Copyright © MikeFeng QQ: 76848502
D3D支持三种光:环境光(Ambient Light),漫反射(Diffuse Light)和镜面反射(Specular Light)。镜面反射需要更多计算,因此D3D默认关闭镜面反射。可以通过给SetRenderState传D3DRS_AMBIENT来设置环境光。
在现实世界中,我们看到一个红色的物体是因为其他颜色的光线被这个物体吸收了,我们看到的是这个物体反射的红色光。D3D引入材质这个概念来模拟现实。
Typpdef struct D3DMATERIAL9 {
D3DCOLORVALUE Diffuse, Ambient, Specular, Emissive;
Float Power;
} D3DMATERIAL9;
D3DCOLORVALUE是一个由r, g,b,a四个元素组成的色彩结构。
Diffuse, Ambient, Specular分别代表这三种光的反射量,而Emissive让物体使物体更亮,看上去就像本身在发光一样。Power描述镜面反射高亮的程度,值越高越亮。注意如果一个物体吸收所有颜色的光,那么它就是黑色的。可以通过SetMaterial函数来设定材质。
D3DMATERIAL9 blueMaterial;
…
Device->SetMaterial(&blueMaterial)
D3D引入顶点法线和面法线来解决光照问题。面法线描述了一个多边形的朝向,顶点法线的朝向可以自己定义。D3D必须知道顶点法线的方向以便计算它的反射光。
在定义顶点的时候可以加入顶点法线的朝向。
struct Vertex
{
float _x, _y, _z;
float _nx, _ny, _nz;
static const DWORD FVF;
}
const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL;
顶点颜色的信息被法线信息替代了,因为我们需要根据材质和法线信息自己计算反射光。简单的图形可以由观察得到顶点法线的信息,但是复杂的图形需要通过计算得到。一种计算方法是将顶点法线等同于面法线。对于一个三角形来说,只要叉积的方式求出面发现即可。另一种更科学的方法是法线平均法。例如三棱锥的情况,最上面那个顶点的法线Vn是由包含着个定点的三个侧面的法线V1, V2, V3平均数决定的。即Vn=(V1+V2+V3)/3。前面学过由于矩阵转换可能会导致法线不偏移的现象,我们可以通过
Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);
来重新归一(Renormalize)法线。
D3D中的光源有3种类型:点光源(向四周发射的光源),平行光源,锥形光源(类似于电筒光)。在锥形光源中有两个角度来描述其大小,一个描述内锥,一个描述外锥。D3D中描述光源的结构是D3DLIGHT9:
typedef struct _D3DLIGHT9 {
D3DLIGHTTYPE Type;
D3DCOLORVALUE Diffuse;
D3DCOLORVALUE Specular;
D3DCOLORVALUE Ambient;
D3DVECTOR Position;
D3DVECTOR Direction;
float Range;
float Falloff;
float Attenuation0;
float Attenuation1;
float Attenuation2;
float Theta;
float Phi;
} D3DLIGHT9;
可以通过SetLight函数来设定光源。可以通过LightEnable函数来打开关闭光源。
给一个物体添加光照分为以下几步:
① 启用光照
② 为每个物体创建材质,并在渲染前为这些物体配上材质
③ 创建一个或多个光源,设置并启用它们
④ 启用附加的光照状态,例如镜面高光。
设定光源最好不要放在OnCreateDevice中,因为切换全屏/窗口或者改变窗口大小时会丢失光源信息。应放在OnResetDevice中。
Example 1 Cube Without Lighting
前面有个旋转平面的例子,现在为了要使用光照,立体的东西效果好一点。下面是一个立体方块的例子,和平面的类似,不过顶点定义中多了顶点法线。
// cube.h
#ifndef __cubeH__
#define __cubeH__
#include "dxstdafx.h"
structVertex
{
Vertex(){}
Vertex(
floatx, float y, float z,
floatnx, float ny, float nz,
floatu, float v)
{
_x = x; _y = y; _z = z;
_nx = nx; _ny = ny; _nz = nz;
_u = u; _v = v;
}
float_x, _y, _z;
float_nx, _ny, _nz;
float_u, _v; // texture coordinates
};
#define FVF_VERTEX (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1)
class Cube
{
public:
Cube(IDirect3DDevice9* device);
~Cube();
HRESULTOnResetDevice();
VOIDOnLostDevice();
bool OnFrameMove( double fTime ) ;
bool OnFrameRender( D3DMATERIAL9* mtrl, IDirect3DTexture9* tex );
private:
HRESULTCreate();
D3DMATERIAL9 m_Mtrl;
IDirect3DDevice9* m_device;
IDirect3DVertexBuffer9* m_vb;
IDirect3DIndexBuffer9* m_ib;
};
#endif//__cubeH__
//其他函数和plain.h中类似,因此只列出Create函数。其余函数请参照前面的Plain类
HRESULTCube::Create()
{
m_device->CreateVertexBuffer(
24 * sizeof(Vertex),
D3DUSAGE_WRITEONLY,
FVF_VERTEX,
D3DPOOL_MANAGED,
&m_vb,
NULL);
Vertex* v;
m_vb->Lock(0, 0, (void**)&v, 0);
// build box
// x, y, z, nx, ny, nz, tu, tv
// fill in the front face vertex data
v[0] = Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
v[1] = Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
v[2] = Vertex( 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f);
v[3] = Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);
// fill in the back face vertex data
v[4] = Vertex(-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
v[5] = Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f);
v[6] = Vertex( 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f);
v[7] = Vertex(-1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f);
// fill in the top face vertex data
v[8] = Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
v[9] = Vertex(-1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
v[10] = Vertex( 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f);
v[11] = Vertex( 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);
// fill in the bottom face vertex data
v[12] = Vertex(-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f);
v[13] = Vertex( 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f);
v[14] = Vertex( 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f);
v[15] = Vertex(-1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f);
// fill in the left face vertex data
v[16] = Vertex(-1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
v[17] = Vertex(-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
v[18] = Vertex(-1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
v[19] = Vertex(-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f);
// fill in the right face vertex data
v[20] = Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
v[21] = Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
v[22] = Vertex( 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
v[23] = Vertex( 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f);
m_vb->Unlock();
m_device->CreateIndexBuffer(
36 * sizeof(WORD),
D3DUSAGE_WRITEONLY,
D3DFMT_INDEX16,
D3DPOOL_MANAGED,
&m_ib,
0);
WORD* i = 0;
m_ib->Lock(0, 0, (void**)&i, 0);
// fill in the front face index data
i[0] = 0; i[1] = 1; i[2] = 2;
i[3] = 0; i[4] = 2; i[5] = 3;
// fill in the back face index data
i[6] = 4; i[7] = 5; i[8] = 6;
i[9] = 4; i[10] = 6; i[11] = 7;
// fill in the top face index data
i[12] = 8; i[13] = 9; i[14] = 10;
i[15] = 8; i[16] = 10; i[17] = 11;
// fill in the bottom face index data
i[18] = 12; i[19] = 13; i[20] = 14;
i[21] = 12; i[22] = 14; i[23] = 15;
// fill in the left face index data
i[24] = 16; i[25] = 17; i[26] = 18;
i[27] = 16; i[28] = 18; i[29] = 19;
// fill in the right face index data
i[30] = 20; i[31] = 21; i[32] = 22;
i[33] = 20; i[34] = 22; i[35] = 23;
m_ib->Unlock();
returnS_OK;
}
主函数和旋转平面基本相同,只是声明了一个不同的全局变量:
Cube* g_pCube = NULL;
在未启用光照模式[SetRenderState( D3DRS_LIGHTING, FALSE) ]的情况下效果如图:
Example 2 Cube With Material & Lighting
Step 1 启用光照( D3D中默认启用 )
pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
Step 2 为每个物体创建材质,并在渲染前为这些物体配上材质
在主回调函数OnCreateDevice中定义光源
在上例Cube的构造函数添加材质代码,使其如下
Cube::Cube(IDirect3DDevice9* device)
{
// save a ptr to the device
m_device = device;
m_Mtrl.Ambient = D3DXCOLOR(1,0,0,0);
m_Mtrl.Diffuse = D3DXCOLOR(0,0,0,0);
m_Mtrl.Specular = D3DXCOLOR(1,1,1,0);
m_Mtrl.Emissive = D3DXCOLOR(0,1,0,0);
m_Mtrl.Power = 5.0f;
}
其中m_Mtrl是cube.h中定义的成员变量D3DMATERIAL9,Emissive对于材质来说很重要,因为它决定了我们看见的纹理颜色范围,这里定义的是只显示绿色。
在Cube::OnFrameRender中添加如下代码
bool Cube::OnFrameRender( D3DMATERIAL9* mtrl, IDirect3DTexture9* tex)
{
if( mtrl )
m_device->SetMaterial(mtrl);
else if (&m_Mtrl)
m_device->SetMaterial(&m_Mtrl);
if( tex )
m_device->SetTexture(0, tex);
m_device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
m_device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
m_device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
m_device->SetStreamSource(0, m_vb, 0, sizeof(Vertex));
m_device->SetIndices(m_ib);
m_device->SetFVF(FVF_VERTEX);
m_device->DrawIndexedPrimitive(
D3DPT_TRIANGLELIST,
0,
0,
24,
0,
12);
return true;
}
如果主回调函数中未给此函数设置材质,那么它将使用自己的成员变量m_Mtrl作为材质。
Step 3 创建一个或多个光源,设置并启用它们
在主回调函数OnCreateDevice中添加如下代码
D3DLIGHT9dir;
::ZeroMemory(&dir, sizeof(dir));
dir.Type = D3DLIGHT_DIRECTIONAL;
dir.Diffuse = D3DXCOLOR(1,1,0,1);
dir.Specular = D3DXCOLOR(0,0,11);
dir.Ambient = D3DXCOLOR(1,0,0,1);
dir.Direction = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
pd3dDevice->SetLight(0, &dir);
pd3dDevice->LightEnable(0, true);
上面的参数只允许光源产生蓝色镜面反射光,以及红色+绿色环境光。该光源是平行光源。
在主回调函数OnFrameRender中添加如下代码,以重新归一法线和启用镜面反射
pd3dDevice->SetRenderState(D3DRS_NORMALIZENORMALS, true);
pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, true);
注意不要忘记在适当时间禁用镜面反射,以提高效率。
pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, false);
Step 4 启用附加的光照状态(未设置)
蓝色部分为镜面反射,绿色部分为材质本身反射。因为光源由x正方向照射过来,因此镜面反射只有在右侧才有效果。最终效果如下
- DirectX SDK 2006学习笔记6——光照和材质
- 6、光照和材质
- Windows 8 Directx 开发学习笔记(九)材质定义及混合光照效果实现
- DirectX SDK 2006学习笔记1——框架
- DirectX SDK 2006学习笔记2——顶点缓冲
- DirectX SDK 2006学习笔记3——索引缓冲
- DirectX SDK 2006学习笔记4——纹理
- OSG学习笔记20——光照和材质的设置(正确绘制出想要的颜色)
- DirectX SDK 2006学习笔记5——动画和矩阵变换
- DirectX SDK 2006学习笔记7——多重纹理和纹理混合
- Direct 3D学习笔记(三)——光照与材质
- 菜鸟DirectX之光照与材质
- DirectD3D-光照和材质
- Direct3D光照和材质
- D3D9 光照和材质
- OpenGL光照和材质
- OpenGL 光照和材质
- OpenGL中的颜色、光照和材质(一)——OpenGL的光照模型
- SMTP协议原始命令码和工作原理
- 高效工作的方法之一
- 一个研究生毕业以后的人生规划(转自天涯虚拟社区)
- 有趣的历史,据说都是真的
- 行列转换一
- DirectX SDK 2006学习笔记6——光照和材质
- 关于 20 分钟 写一个操作系统, (应该是写一个引导记录后打出来几句话)
- spring的学习资料(收集中)
- 十八个让你专心工作的方法
- 用新的高级加密标准保持数据的安全性
- spring中基础核心接口
- c#里如何实现控件数组
- Prototype.js是个不错的东西
- XML的几个常用类