为了增强场景的真实感,我们就可以为场景增加光照(Lighting) 。光照也有助于描述实体形状和立体感。使用光照时,我们无需自定指.定顶点的颜色值; Direct3D会将顶点送入光照计算引擎(light engine),依据光源类型、材质(material) 以及物体表面相对于光源的朝向,计算出每个顶点的颜色值,基于某种光照模型计算出各顶点的颜色,以使绘制结果更加逼真。
3种类型的光照
在Direct3D的光照模型中,光源发出的光由以下3个分量或3种类型的光组成
说到一套完整的光照体系,有两对组成方面,第一,光照,第二,材质。这两者天生就是一对好搭档,我们可以把它们看做光照计算的两要素,想要绘制出具有光照的真实三维世界,两者缺一不可。
环境光(Ambient Light)

这种类型的光经其他表面反射到达物体表面,并照亮整个场景。例如,物体的某些部分被一定程度的照亮,但是物体并没有处于光源的直接照射下。物体之所以会被照亮,是由于其他物体对光的反射.
一个物体即使没有直接被光源照射,但是只要有光线通过其他物体的折射、反射到达物体,它也可能被看见。这种基于整个自然界环境的整体亮度,称为环境光(AmbientLight) 或者背景光。环境光没有位置或者方向上的特征,只有一个颜色亮度值,而且不会衰减,所以在
所有方向和所有物体表面上投射的环境光的数量是恒定不变的。想要以较低的代价和开销来近似模拟光照的话,直接开启环境光是一个不错的选择。
在Direct3D中环境光的设置非常简单,也就是用一下SetRenderState,
代码如下:
pd3dDevice->SetRenderState(D3DRS_ _A.M BIENT,D3DCOLOR_ _XRGB(36, 36,
36)); //设置一 下环境光其中第一个参数填D3DRS_ _AMBIENT,代表环境光的设置,而第二个参数填一个颜色值就可以了。
漫反射光(Diffuse Light)

这种光沿着特定的方向传播。当它到达某一表面时,将沿着各个方向均匀反射。由于漫反射光沿所有方向均匀反射,无论懂哪个方向观察,表面亮度均相同,所以采用该模型时,无需考虑观察者的位置。这样,漫反射光方程( Diffuse lighting equation)中,仅需考虑光传播
的方向以及表面的朝向。从一个光源发出的光一般都是这种类型的。
漫反射光在我们的生活中最为普遍,太阳的直射,日光灯的照射都可以看成漫反射的近似。这种类型的光沿着特定的方向传播。当它到达某一表面时,将沿着各个方向均匀反射,所以我们无论从哪个方向观察,物体表面的亮度都是相同的,所以采用漫反射这种光照模型时,
无需考虑观察者的位置,但是需要考虑漫反射光的空间位置和方向。从一个光源发出的光一般都是这种类型的。漫反射光并没有简洁的设置方法。
镜面光(Specular Light)
这种类型的光沿特定方向传播。当此类光到达一个表面时,将严格地沿着另一个方向反射,从而形成只能在一定角度范围内才能观察到的高亮度照射。因为在这种模型中,光线均沿同一个方向反射,所以在镜面光照方程(specular lighting equation)中不仅需要考虑光线的入
射方向和图元的表面朝向,而且还需要考虑观察点的位置。镜面光可用于模拟物体上的高光点,例如当光线照射到一个抛光的表面所形成的高亮照射。
镜面光与其它类型的光相比,计算量要大得多。因此Direct3D为其提供了开关选项。实际上,默认装填下,Direct3D 不进行镜面反射计算;如果您想启用镜面光,必须将绘制状态D3DRS_ SPECULARENABLE 设为true。
Device->SetRenderState(D3DRS_ SPECULARENABLE,true);每种类型的光都可用结构D3DCOLORVALUE或D3DXCOLOR来表示,这些类型描述了光线的颜色。
D3DXCOLOR redAmblent(1.0f,0.0f,0.0f,1.0f):
D3DXCOLOR blueDiffuse(0.0f,0.0f,1.0f,1.0f);
D3DXCOLOR whiteSpecular(1.0f,1.0f,1.0f,1.0f);描述光线的颜色时,D3DXCOLOR类中的Alpha值将被忽略。镜面反射光,顾名思义,沿着特定的方向传播,当此类光到达一个表面时,将严格地沿着另一个方向反射,从而形成只能在一个角度范围内才能观蔡到的高亮度照射。这种光照模型模拟了从光滑发光面如镜子、一块金属或者一块发光塑料等材料来进行光线反射的情形。如果我们移动一下光源的话,就会发现镜面亮光区所发生的变化,这意味着镜面反射取决于观察者的角度。我们可以这样来归纳,漫反射与视觉无关,而镜面反射与视觉相关。
需要注意的是,镜面光与其他类型的光相比,计算量要大得多,Direct3D默认情况是把镜面反射关起来的。如果我们想启用镜面反射的话,用下面的代码,即把渲染状态D3DRS_ _SPECULARENABLE设为true:
pd3dDevice->SetRenderState(D3DRS_ SPECULARENABLE,true);材质
在现实世界中,我们所观察到的物体的颜色是由该物体所反射的光的.颜色决定的。例如,一个纯红色的球体反射了全部分红色入射光,并吸收了所有非红色的光,所以该球体呈现为红色。Direct3D 通过定义物体的材质(materials)来模拟同样的现象。材质允许我们定义物体
表面对各种颜色光的反射比例。在代码中,材质用结构D3DMATERIAL9来表示。
对于光照计算,光照和材质两者缺一不可。 物体表面的材质属性决定,了它能反射什么颜色的光线以及能反射多少,而在Direct3D.中,关于物体表面的材质属性,由一个结构体D3DMATERIAL9来负责管理。
typedef struct _D3DMATERIAL9 {
D3DCOLORVALUE Diffuse; // 漫反射颜色
D3DCOLORVALUE Ambient; // 环境光颜色
D3DCOLORVALUE Specular; // 镜面光颜色
D3DCOLORVALUE Emissive; // 自发光颜色
float Power; // 镜面反射高光度
} D3DMATERIAL9, *LPD3DMATERIAL9;Diffuse:漫反射颜色,表示物体表面对各种光源的散射效果,即物体的基本颜色。
Ambient:环境光颜色,表示物体在没有直接光源照射时的颜色。
Specular:镜面光颜色,表示物体表面对光源的反射效果,即高光的颜色。
Emissive:自发光颜色,表示物体自身发出的颜色。
Power:镜面反射高光度,用于调节镜面反射的亮度和大小。
例如,假定有一个红色的球体。我们想将该球体的材质的属性定义为只反射红色光,而吸收所有其他颓色的光。

这儿,现们将绿色和蓝色分量设为0.表明该材质对这些颜色的光的反射率为0%.所红色分量设为1,裁明该材朋对红色光的反射丰为100%,注意,我们可以控制材质在各种类型光(环境光、漫射光和镜面光)照射F哪种断色的光将被反射。
您还需注意,如果用一个只能发出蓝色光的光源来照射一个红色的球体,则由于蓝色色光被完全吸收,而反射的红色光为0,所以该球体物无法被照亮。当个物体吸收了所有的光时,便呈现为黑色,类似地,如果一个物体能够100%地反射红色光,绿色光和蓝色光,它将呈现为白色。
顶点法线
面法线是一个描述多边形朝向的向量。
顶点法线(vector normal)正是基于上述思路而产生的,但它并不用
于指定每个多边形的法向量。顶点法线描述的是构成多边形的各个顶
点的法线。

Direct3D需要知道顶点的法线方向,以确定光线到达表面时的入射,角。而且,由于光照计算是对每个顶点进行的,所以Direct3D需要知道表面在每个顶点处的局部朝向(法线方向)。请注意,顶点法线与表面法线不一定相同。近似的球体或圆就是一个很好的例子

光源(Direct3D支持3种类型的光源)
点光源(Point lights)
该光源在世界坐标系中有固定的位置,并向所有的方向发射光线。点光源(Point Light)具有颜色和位置,但 没有方向,它向所有方向发射的光都一样。

它是一个从中心向空间中各个方向发射相等强度光线的光源,且光的亮度会不随着距离而衰减。要定义一个点光源的话,实例化一个D3DLIGHT9结构体,将第一个参数设为D3DLIGHT_ POINT然后进行其余参数的设置即可,这样实例化出的这个结构体就是一个点光源了。
方向光(Directional lights)
该光源没有位置信息,所发射的光线相互平行的沿某一特定方向传播方向光源是从无穷远处发出的一组平行、均匀的光线,在场景中以相同的方向传播,只具有颜色和方向,不受到衰减和范围的影响。同样地,要定义一个方向光源的话,实例化一个D3DLIGHT9结构体,将第一个参数设为D3DLIGHT_ DIRECTIONAL 然后进行其余参数的设置即可,这样实例化出的这个结构体就是一个方向光源了。
聚光灯(Spot lights)
这种类型的光源与手电筒类似;该光源有位置信息,其发射的光线呈锥形( conicalshape)沿着特定方向传播。这里的聚光灯光源,就是我们在演唱会时看到的那样的聚光灯,也如我们生活中的探照灯。聚光灯发出的光由一个明亮的内椎体(innercone)和大一点的外椎体(outercone) 组成。显然内锥体中的光是最亮的,内锥体到外椎体外围的光强逐渐衰减,到了外椎体以外,已经衰减得没有光了。

光源类型和光照类型的区别
在Direct3D中的光源类型和光照类型是两个完全不同的概念,光照模型描述的是光线的反射特征,而光源类型主要强调的是能够产生这些光照模型的方式以及光线的位置、方向、强度等特征。
示例代码
#include<d3d9.h>
#include <d3dx9.h>
#include <tchar.h>
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#pragma comment(lib,"winmm.lib") //调用PlaySound函数所需库文件
#define WINDOWWIDTH 800
#define WINDOWHEIGHT 600
// 函数声明
bool InitializeD3D(HWND hWnd);
bool InitializeObjects();
void Shutdown();
bool RenderScene(float timeDelta);
void Light_Set(LPDIRECT3DDEVICE9 pd3dDevice,UINT nType);
//IDirect3D9 对象
IDirect3D9 * g_D3d9 = NULL;
//设备对象
IDirect3DDevice9 * g_D3DDevice = NULL;
//茶壶
ID3DXMesh* Teapot = 0;
//----------------------设置顶点缓存四部曲1:设计顶点缓存格式-----------------//
//保存几何体的顶点缓冲区。
IDirect3DVertexBuffer9 * g_VertexBuffer = NULL;
//设置索引缓存
IDirect3DIndexBuffer9 *g_IndexBuffer=NULL;
//自定义顶点的结构体
struct Vertex
{
Vertex(){}
Vertex(float x, float y, float z)
{
_x = x; _y = y; _z = z;
}
float _x, _y, _z;
static const DWORD FVF;
};
//设置顶点的格式
const DWORD Vertex::FVF = D3DFVF_XYZ;
//回调函数
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
case WM_KEYUP:
if(wParam == VK_ESCAPE)
{
PostQuitMessage(0);
}
break;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevhInst, LPSTR cmdLine, int show)
{
WNDCLASSEX wndclass= {0};
wndclass.cbSize = sizeof( WNDCLASSEX ) ; //设置结构体的字节数大小
//窗口类的附加内存相当于缓冲区 一般为0
wndclass.cbClsExtra = 0;
//窗口的附加内存相当于缓冲区 一般为0
wndclass.cbWndExtra = 0;
//获取一个笔刷 作用:填充背景色
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
//鼠标指针
wndclass.hCursor = NULL;
//系统默认的图标
wndclass.hIcon = NULL;
//程序的实例句柄
wndclass.hInstance = hInstance;
//消息处理函数(系统调用)
wndclass.lpfnWndProc = WndProc;
//给类起个名 C++11 C14 "Main"
wndclass.lpszClassName = TEXT("Main");
//菜单 NULL为不要菜单
wndclass.lpszMenuName = NULL;
//窗口类的样式 大样式
wndclass.style = NULL;
RegisterClassEx(&wndclass);
//第四步 创建窗口 我们通过窗口句柄可以找到我们刚才创建的窗口
HWND hWnd = CreateWindow(TEXT("Main"), TEXT("MainWindows"), WS_OVERLAPPEDWINDOW, 100, 100, WINDOWWIDTH, WINDOWHEIGHT, NULL, NULL, hInstance, NULL);
//初始化失败则打印消息
if(!InitializeD3D(hWnd))
{
MessageBox(hWnd, TEXT("Direct3D初始化失败~!"), TEXT("呵呵的消息窗口"), 0); //使用MessageBox函数,创建一个消息窗口
}
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);
static float lastTime = (float)timeGetTime();
MSG msg={0};
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
float currTime = (float)timeGetTime();
float timeDelta = (currTime - lastTime)*0.001f;
RenderScene(timeDelta);
lastTime = currTime;
}
}
//释放所有资源。
Shutdown();
//注销我们的窗口
UnregisterClass(TEXT("Main"), wndclass.hInstance);
return 0;
}
//初始化DirectX四部曲
bool InitializeD3D(HWND hWnd)
{
//步骤1:设置硬件检查
g_D3d9 = Direct3DCreate9(D3D_SDK_VERSION);
if( !g_D3d9 )
{
MessageBox(0, "Direct3DCreate9() - FAILED", 0, 0);
return false;
}
//步骤2:检查硬件是否支持顶点运算
D3DCAPS9 caps;
//UINT Adapter:指定物理显卡的序号
//D3DDEVTYPE DeviceType:指定设备类型(例如硬件设备D3DDEVTYPE_HAL)或软件设备(D3DDEVTYPE_REF)
//D3DCAPS *pCaps:返回已初始化的设备性能结构实例
g_D3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);
int vp = 0;
if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
//硬件顶点运算
vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
else
vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
//步骤3设置D3DPRESENT_PARAMETERS结构
D3DPRESENT_PARAMETERS d3dpp={0};
//后台缓存中表面的宽度,单位为像素
d3dpp.BackBufferWidth = WINDOWWIDTH;
//后台缓存中表面的高度,单位为像素
d3dpp.BackBufferHeight = WINDOWHEIGHT;
//后台缓存的像素格式(如:32位像素格式:D3DFMT_A8R8G8B8)。
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
//所需使用的后台缓存的个数,通常指定为1,表明我们仅需要一个后台缓存。咱们指定为2个
d3dpp.BackBufferCount = 1;
//后台缓存所使用的多重采样类型。
d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
//多重采样的质量水平
d3dpp.MultiSampleQuality = 0;
//枚举类型的一个成员。该枚举类型指定了交换链中的缓存的页面置换方式。指定为D3DSWAPEFFECT_DISCARD时效率最高。
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
//与设备相关的窗口句柄。指定了所要进行绘制的应用程序窗口。
d3dpp.hDeviceWindow = hWnd;
//为true时,表示窗口模式。为false时,表示全屏模式
d3dpp.Windowed = true;
//设置为true,则Direct3D自动创建并维护深度缓存或模板缓存。
d3dpp.EnableAutoDepthStencil = true;
//深度缓存或模板缓存的像素格式(例如,用24位表示深度并将8位保留供模板缓存使用,D3DFMT_D24S8)
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
//一些附加的特性。可以指定为0(无标记)
d3dpp.Flags = 0;
//刷新频率。如果想使用默认的刷新频率,可将该参数指定为D3DPRESENT_RATE_DEFAULT。
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
//PresentationInterval :D3DPRESENT集合的一个成员。D3DPRESENT——INTERVAL_IMMEDIATE:立即提交
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
//步骤4 创建设备
if(FAILED(g_D3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,vp, &d3dpp, &g_D3DDevice)))
{
MessageBox(0, "CreateDevice() - FAILED", 0, 0);
return false;
}
//初始化显示的任何对象。
if(!InitializeObjects())
{
return false;
}
return true;
}
bool InitializeObjects()
{
//
// 创建茶壶
D3DXCreateTeapot(g_D3DDevice, &Teapot, 0);
//设置摄像机的位置
//相机的位置,沿着Z轴平移3个单位
D3DXVECTOR3 position(0.0f, 0.0f, -3.0f);
D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
D3DXMATRIX V;
D3DXMatrixLookAtLH(&V, &position, &target, &up);
g_D3DDevice->SetTransform(D3DTS_VIEW, &V);
//设置投影矩阵
D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(
&proj,
D3DX_PI * 0.5f, // 90 - degree
(float) WINDOWWIDTH / (float)WINDOWHEIGHT,
1.0f,
1000.0f);
g_D3DDevice->SetTransform(D3DTS_PROJECTION, &proj);
//设置线框模式
//g_D3DDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
D3DMATERIAL9 mtrl;
::ZeroMemory(&mtrl,sizeof(mtrl));
mtrl.Ambient=D3DXCOLOR(0.5f,0.5f,0.7f,1.0f);
mtrl.Diffuse=D3DXCOLOR(0.6f,0.6f,0.6f,1.0f);
mtrl.Specular=D3DXCOLOR(0.3f,0.3f,0.3f,0.3f);
mtrl.Emissive=D3DXCOLOR(0.3f,0.0f,0.1f,1.0f);
g_D3DDevice->SetMaterial(&mtrl);
//设置光照
Light_Set(g_D3DDevice,1);
g_D3DDevice->SetRenderState(D3DRS_LIGHTING,TRUE);
g_D3DDevice->SetRenderState(D3DRS_NORMALIZENORMALS,TRUE);
g_D3DDevice->SetRenderState(D3DRS_SPECULARENABLE,TRUE);
g_D3DDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_CCW);//开启背面消隐
return true;
}
void Light_Set(LPDIRECT3DDEVICE9 pd3dDevice,UINT nType)
{
//定义一个光照类型并初始化
static D3DLIGHT9 light;
::ZeroMemory(&light,sizeof(light));
//一个switch,给3种光源选项
switch (nType)
{
case 1: //点光源
light.Type=D3DLIGHT_POINT;
light.Ambient= D3DXCOLOR(0.6f,0.6f,0.6f,1.0f);
light.Diffuse=D3DXCOLOR(1.0f,1.0f,1.0f,1.0f);
light.Specular= D3DXCOLOR(0.3f,0.3f,0.3f,1.0f);
light. Position= D3DXVECTOR3(0.0f,200.0f,0.0f) ;
light. Attenuation0 = 1.0f;
light. Attenuation1 = 0.0f;
light. Attenuation2 = 0.0f;
light. Range = 300.0f;
break;
case 2: //平行光
light.Type= D3DLIGHT_DIRECTIONAL;
light. Ambient= D3DXCOLOR(0.5f,0.5f,0.5f,1.0f) ;
light. Diffuse=D3DXCOLOR(1.0f,1.0f,1.0f,1.0f) ;
light. Specular=D3DXCOLOR(0.3f,0.3f,0.3f,1.0f) ;
light. Direction=D3DXVECTOR3(1.0f,0.0f,0.0f) ;
break ;
case3://聚光灯
light. Type = D3DLIGHT_SPOT;
light. Position =D3DXVECTOR3(100.0f,100.0f,100.0f) ;
light. Direction= D3DXVECTOR3(-1.0f,-1.0f,-1.0f);
light. Ambient = D3DXCOLOR(0.3f,0.3f,0.3f,1.0f) ;
light. Diffuse = D3DXCOLOR(1.0f,1.0f,1.0f,1.0f) ;
light. Specular = D3DXCOLOR(0.3f,0.3f,0.3f,0.3f) ;
light. Attenuation0 = 1.0f;
light. Attenuation1 = 0.0f;
light. Attenuation2 = 0.0f;
light. Range= 300.0f;
light. Falloff= 0.1f;
light. Phi=D3DX_PI/3.0f;
light. Theta =D3DX_PI/6.0f;
break;
}
pd3dDevice->SetLight(0,&light); //设置光源
pd3dDevice->LightEnable(0,true);//启 用光照
pd3dDevice->SetRenderState(D3DRS_AMBIENT,D3DCOLOR_XRGB(36,36,36)); //设置一 下环境光
}
bool RenderScene(float timeDelta)
{
if( g_D3DDevice )
{
D3DXMATRIX Ry;
static float y = 0.0f;
D3DXMatrixRotationY(&Ry, y);
y += timeDelta;
if(y >= 6.28f)
y = 0.0f;
g_D3DDevice->SetTransform(D3DTS_WORLD, &Ry);
// 根据键盘按 下的情况设置相应的填充模式
if (::GetAsyncKeyState(0x31)&0x8000f) //若数字键1被按下,进行实体填充
g_D3DDevice-> SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID) ;
if (::GetAsyncKeyState(0x32)&0x8000f) //若数字键2被按下,进行线框填充
g_D3DDevice-> SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME) ;
//根据键盘按下的情况设置相应的光照类型
if (::GetAsyncKeyState(0x51) & 0x8000f) //若键盘上的按键Q被按下,光源类型设为点光源
Light_Set(g_D3DDevice,1) ;
if (::GetAsyncKeyState(0x57) & 0x8000f) //若键盘上的按键W被按下,光源类型设为平行光源
Light_Set(g_D3DDevice,2);
if (::GetAsyncKeyState(0x45) & 0x8000f) //若键盘上的按键E被按下,光源类型设为聚光灯
Light_Set(g_D3DDevice,3);
//渲染五部曲第一步:清理
g_D3DDevice->Clear(0,0,D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);
//渲染五部曲第二步开始场景
g_D3DDevice->BeginScene();
//渲染五部曲第三步渲染代码
Teapot->DrawSubset(0);
//渲染五部曲第四步:结束场景
g_D3DDevice->EndScene();
//渲染五部曲翻转显示
g_D3DDevice->Present(0, 0, 0, 0);
}
return true;
}
//释放所有资源。
void Shutdown()
{
if(g_D3DDevice != NULL)
{
g_D3DDevice->Release();
}
if(g_D3d9 != NULL)
{
g_D3d9->Release();
}
if(g_VertexBuffer != NULL)
{
g_VertexBuffer->Release();
}
if(g_IndexBuffer != NULL)
{
g_IndexBuffer->Release();
}
}
















