在Direct3D中创建光源相对比较简单,因为只是为了使用光源,而不需要知道太多Direct3D内部使用的算法。所以即使对先前光照和阴影的讨论一头雾水,也不用担心。有一些亲自实践的经历之后,读者就可以使用Direct3D中的光照,而不必知道它的工作机理或自己动手计算光照方程。
默认情况下,即使没有指定任何光源,Direct3D也会打开硬件光照。到目前为止,都是将其设置为FALSE,那是因为还没有用到光照。在初始化中调用SetRenderState()函数,并将该函数的参数D3DRS_LIGHTING设为FALSE。
为了在Direct3D中创建光源,必须创建一个D3DLIGHT对象。为了方便起见,程序清单3.6定义了该结构。不必设置所有的属性值,而属性值的设置取决于创建的光源类型。例如,如果正在使用方向性光源就没必要设置光源位置,这是因为方向性光源本身并没有位置,只有一个方向。
typedef struct _D3DLIGHT9
{
D3DLIGHTTYPE Type; // 定义了所要创建的光源类型
D3DCOLORVALUE Diffuse; // 该光源所发出的漫射光的颜色
D3DCOLORVALUE Specular; // 该光源所发出的镜面光的颜色
D3DCOLORVALUE Ambient; // 该光源所发出的环境光的颜色
D3DVECTOR Position; // 用于描述光源在世界坐标系中位置的向量。
D3DVECTOR Direction; // 一个描述光在世界坐标系中传播方向的向量。
float Range; // 光线“消亡”前,所能达到的最大光程。
float Falloff; // 仅用于聚光灯。定义了光强从内锥形到外锥形的衰减方式
float Attenuation0; // 该衰减变量定义了光强随距离衰减的方式。仅用于点光源和聚光灯
float Attenuation1; // 同Attenuation0
float Attenuation2; // 同Attenuation0
float Theta; // 仅用于聚光灯。指定了内部锥形的圆锥角,单位为弧度
float Phi; // 仅用于聚光灯。指定了外部锥形的圆锥角,单位为弧度
}D3DLIGHT9;
可以手动设置D3DLIGHT9结构中的成员变量,不用它们的时候也可以置之不理。每个成员变量的定义如下所示:
■ Type
指的是光源类型(可以是D3DLIGHT_POINT、D3DLIGHT_SPOT或D3DLIGHT_DIRECTION)。
■ Diffuse
是漫反射在场景中的光数量。
■ Specular 指的是镜面光发射在场景中的该类型光的数量。
■ Ambient
指的是场景中的全局光照量。
■ Position 在光源是点光源或聚光源时,指光源的位置。
■ Direction
是光发出的方向(可以是聚光,也可以是方向光)。
■ Range 是光作用的距离,点光源作用的半径。
■ Falloff
是聚光光源在内部和外部锥体中的亮度衰减量。
■ Attenuation* 指明了光在距离上亮度的改变方式,只有点光源和聚光光源才用到该参数。
■
Theta 指的是聚光光源内部的锥体弧度角。
■ Phi 指的是聚光光源外面的锥体弧度角。
D3DLIGHT9结构包含多个成员变量。其中大部分成员变量可以忽略,这取决于要创建的光源类型。例如,如果要创建的是点光源,那就用不到Theta、Phi和Direction这三个变量。一旦创建并设置了D3DLIGHT9结构,剩下要做的工作就是激活光源。
g_D3DDevice->SetLight(0, &g_light);
g_D3DDevice->LightEnable(0, TRUE);
SetLight() 函数是一个设备对象函数,用于给Direct3D发送光源对象。它以基0的光源索引和D3DLIGHT9对象为参数,基0指的是正在使用的第一盏灯,1指的是第二盏灯,依此类推。一旦Direct3D有了光源,就必须启用该光源。调用LightEnable()函数即可启用光源,该函数的参数包括光源索引和一个开启或关闭光源的布尔变量。true(真)意味着打开光源,false(假)意味着关闭光源。
在Direct3D和OpenGL中一次可以使用数量多达8个的光源。在创建硬件光源时,一定要牢记这一点。
此时,就得到了Direct3D中的光源。同样,可以有选择性地指定对象的材质。本章前面已经讨论了如何定义材质确定光线和对象相互作用的方法。例如,塑料材质与光线的相互作用和木头或金属材质与光线的相互作用相比要轻微许多。程序清单3.8给出了该结构的定义。
typedef struct D3DMATERIAL9 {
D3DCOLORVALUE Diffuse; // 指定表面反射的漫反射光
D3DCOLORVALUE Ambient; // 指定表面反射的环境光
D3DCOLORVALUE Specular; // 指定表面反射的镜面光
D3DCOLORVALUE Emissive; // 表面本身自发光
float Power; // 镜面高光
} D3DMATERIAL9, *LPD3DMATERIAL9;
Direct3D材质结构由5个成员变量组成,每个变量指明了对对象最终外观的不同贡献值。D3DMATERIAL9结构的成员变量定义如下:
■
Diffuse 指的是对象的漫反射属性(对象均匀反射的光量)。
■ Ambient 指的是对象发射回场景中的环境色数目。
■ Specular
指的是对象反射回场景中的聚光数量。
■ Emissive 指的是在给定表面上发射出的光。
■ Power
指定了镜面亮光区的亮度,该值越大,亮光区越亮。
创建材质后,所要做的全部工作就是在渲染对象和万事俱备之前启用它们。调用Direct3D的设备函数SetMaterial()即可完成该设置。
HRESULT SetMaterial(CONST D3DMATERIAL9* pMaterial);
代码示例:
#include<d3d9.h>
#include<d3dx9.h>
#define WINDOW_CLASS "UGPDX"
#define WINDOW_NAME "Lighting Built-in Direct3D Shapes"
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
// Function Prototypes...
bool InitializeD3D(HWND hWnd, bool fullscreen);
bool InitializeObjects();
void RenderScene();
void Shutdown();
// Direct3D object and device.
LPDIRECT3D9 g_D3D = NULL;
LPDIRECT3DDEVICE9 g_D3DDevice = NULL;
// Matrices.
D3DXMATRIX g_projection;
D3DXMATRIX g_ViewMatrix;
D3DXMATRIX g_WorldMatrix;
// Mesh objects
LPD3DXMESH g_teapot = NULL;
LPD3DXMESH g_cube = NULL;
LPD3DXMESH g_sphere = NULL;
LPD3DXMESH g_torus = NULL;
D3DMATERIAL9 g_material;
// Scene light source. 光源对象
D3DLIGHT9 g_light;
LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
case WM_KEYUP:
if(wp == VK_ESCAPE) PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, msg, wp, lp);
}
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE ph, LPSTR cmd, int s)
{
// Register the window class
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
WINDOW_CLASS, NULL };
RegisterClassEx(&wc);
// Create the application's window
HWND hWnd = CreateWindow(WINDOW_CLASS, WINDOW_NAME,
WS_OVERLAPPEDWINDOW, 100, 100, WINDOW_WIDTH, WINDOW_HEIGHT,
GetDesktopWindow(), NULL, wc.hInstance, NULL);
// Initialize Direct3D
if(InitializeD3D(hWnd, false))
{
// Show the window
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);
// Enter the message loop
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
RenderScene();
}
}
// Release any and all resources.
Shutdown();
// Unregister our window.
UnregisterClass(WINDOW_CLASS, wc.hInstance);
return 0;
}
bool InitializeD3D(HWND hWnd, bool fullscreen)
{
D3DDISPLAYMODE displayMode;
// Create the D3D object.
g_D3D = Direct3DCreate9(D3D_SDK_VERSION);
if(g_D3D == NULL) return false;
// Get the desktop display mode.
if(FAILED(g_D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,
&displayMode))) return false;
// Set up the structure used to create the D3DDevice
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
if(fullscreen)
{
d3dpp.Windowed = FALSE;
d3dpp.BackBufferWidth = WINDOW_WIDTH;
d3dpp.BackBufferHeight = WINDOW_HEIGHT;
}
else
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = displayMode.Format;
// Create the D3DDevice
if(FAILED(g_D3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp,
&g_D3DDevice))) return false;
// Set the projection matrix. 创建透视投影矩阵
D3DXMatrixPerspectiveFovLH(&g_projection, 45.0f,
WINDOW_WIDTH/WINDOW_HEIGHT, 0.1f, 1000.0f);
g_D3DDevice->SetTransform(D3DTS_PROJECTION, &g_projection);
// Initialize any objects we will be displaying.
if(!InitializeObjects()) return false;
return true;
}
bool InitializeObjects()
{
// Set default rendering states. 默认为TRUE
g_D3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
/*
环境光是周围的光,从各个方向照射而来.
C++应用程序调用IDirect3DDevice9::SetRenderState方法设置环境光的颜色,
并将D3DRS_AMBIENT枚举类型值作为第一个参数传入。第二个参数是颜色值,默认值为零。
//设置环境光
d3dDevice->SetRenderState(D3DRS_AMBIENT, 0x00202020);
*/
g_D3DDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_COLORVALUE(0.3f, 0.3f, 0.3f, 1.0f));
// Setup the light source. 创建方向光源
g_light.Type = D3DLIGHT_DIRECTIONAL;
g_light.Direction = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
g_light.Diffuse.r = g_light.Diffuse.g = 1;
g_light.Diffuse.b = g_light.Diffuse.a = 1;
g_light.Specular.r = g_light.Specular.g = 1;
g_light.Specular.b = g_light.Specular.a = 1;
// Register the light.
g_D3DDevice->SetLight(0, &g_light);
g_D3DDevice->LightEnable(0, TRUE);
// Setup the material properties for the teapot. 创建材质
ZeroMemory(&g_material, sizeof(D3DMATERIAL9));
g_material.Diffuse.r = g_material.Ambient.r = 0.6f;
g_material.Diffuse.g = g_material.Ambient.g = 0.6f;
g_material.Diffuse.b = g_material.Ambient.b = 0.7f;
g_material.Specular.r = 0.4f;
g_material.Specular.g = 0.4f;
g_material.Specular.b = 0.4f;
g_material.Power = 8.0f;
// Create the objects.
if(FAILED(D3DXCreateTeapot(g_D3DDevice, &g_teapot, NULL)))
return false;
if(FAILED(D3DXCreateBox(g_D3DDevice, 2, 2, 2, &g_cube, NULL)))
return false;
if(FAILED(D3DXCreateSphere(g_D3DDevice, 1.5, 25, 25,
&g_sphere, NULL))) return false;
if(FAILED(D3DXCreateTorus(g_D3DDevice, 0.5f, 1.2f, 25, 25,
&g_torus, NULL))) return false;
// Define camera information. 创建视图矩阵
D3DXVECTOR3 cameraPos(0.0f, 0.0f, -8.0f);
D3DXVECTOR3 lookAtPos(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 upDir(0.0f, 1.0f, 0.0f);
// Build view matrix.
D3DXMatrixLookAtLH(&g_ViewMatrix, &cameraPos, &lookAtPos, &upDir);
return true;
}
void RenderScene()
{
// Clear the backbuffer.
g_D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET,
D3DCOLOR_XRGB(0,0,0), 1.0f, 0);
// Begin the scene. Start rendering.
g_D3DDevice->BeginScene();
// Apply the view (camera). 设置视图矩阵
g_D3DDevice->SetTransform(D3DTS_VIEW, &g_ViewMatrix);
// Draw teapot.
D3DXMatrixTranslation(&g_WorldMatrix, 2.0f, -2.0, 0.0f);
g_D3DDevice->SetTransform(D3DTS_WORLD, &g_WorldMatrix); //设置世界矩阵
g_D3DDevice->SetMaterial(&g_material); //设置teapot的材质
g_teapot->DrawSubset(0);
// Draw Cube.
D3DXMatrixTranslation(&g_WorldMatrix, -2.0f, -2.0, 0.0f);
g_D3DDevice->SetTransform(D3DTS_WORLD, &g_WorldMatrix);
g_D3DDevice->SetMaterial(&g_material); //设置Cube的材质
g_cube->DrawSubset(0);
// Draw Sphere.
D3DXMatrixTranslation(&g_WorldMatrix, 2.0f, 2.0, 0.0f);
g_D3DDevice->SetTransform(D3DTS_WORLD, &g_WorldMatrix);
g_D3DDevice->SetMaterial(&g_material); //设置Sphere的材质
g_sphere->DrawSubset(0);
// Draw Torus.
D3DXMatrixTranslation(&g_WorldMatrix, -2.0f, 2.0, 0.0f);
g_D3DDevice->SetTransform(D3DTS_WORLD, &g_WorldMatrix);
g_D3DDevice->SetMaterial(&g_material); //设置Torus的材质
g_torus->DrawSubset(0);
// End the scene. Stop rendering.
g_D3DDevice->EndScene();
// Display the scene.
g_D3DDevice->Present(NULL, NULL, NULL, NULL);
}
void Shutdown()
{
// Release all resources.
if(g_D3DDevice != NULL) g_D3DDevice->Release();
if(g_D3D != NULL) g_D3D->Release();
if(g_teapot != NULL) { g_teapot->Release(); g_teapot = NULL; }
if(g_cube != NULL) { g_cube->Release(); g_cube = NULL; }
if(g_sphere != NULL) { g_sphere->Release(); g_sphere = NULL; }
if(g_torus != NULL) { g_torus->Release(); g_torus = NULL; }
}
该函数首先设置D3DLIGHT9对象。因为正在创建的是方向光,所以只要设置光源类型、方向和漫反射及镜面属性(从技术角度而言,镜面属性是可选的)即可。接下来,调用SetLights()函数设置灯源。它的参数包括正在设置的灯源索引和灯源自身。然后调用EnableLight()函数开启灯源。以便使其在场景中处于活动状态。该函数以想要打开的灯源索引为参数,只要将发送给该喊俗话的参数值设为FALSE(假)或TRUE(真)即可。TRUE意味着开灯,而FALSE意味着关灯。
设置灯源之后,为对象创建材质。对该对象的灰色阴影设置不同的颜色,为稍微不同的灰影区设置特定的颜色。如果想要物体显示红色材质,就要将漫反射颜色的红色成分设置的比其他的高一些,从而使物体上的红色比蓝色显示的多一些。镜面反射能力说明了镜面亮光区的强烈程度。该值越高,亮光区显得越亮。
创建完材质后,就要创建网面对象和视场信息。这可以用在Shapes演示程序中的相同方法完成。演示程序中最后是用于渲染和关闭工作的函数。对这两个函数几乎没有做任何改动。