基于上一篇文章的框架,写了个增加光照效果的程序。
先贴一下代码:
- //////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // File: light.cpp
- // by tianzhihen
- // 2008.10.13
- // MSVC++ 8.0
- //////////////////////////////////////////////////////////////////////////////////////////////////
- #include "d3dUtility.h"
- //
- // 全局变量
- //
- IDirect3DDevice9* Device = 0;
- const int Width = 640;
- const int Height = 480;
- // 金字塔顶点缓存
- IDirect3DVertexBuffer9* Pyramid = 0;
- //
- // 类和结构体
- //
- struct Vertex
- {
- Vertex(){}
- Vertex(float x,float y,float z,float nx,float ny,float nz)
- {
- _x = x; _y = y; _z = z;
- _nx = nx; _ny = ny; _nz = nz;
- }
- float _x,_y,_z;
- float _nx,_ny,_nz; //顶点法向量
- static const DWORD FVF;
- };
- const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL;
- //
- // 框架函数
- //
- bool Setup()
- {
- //
- // 打开光照开关
- //
- Device->SetRenderState(D3DRS_LIGHTING,true);
- //
- // 创建顶点/索引缓存
- //
- Device->CreateVertexBuffer(
- 12*sizeof(Vertex), //注意是12个顶点
- D3DUSAGE_WRITEONLY, // 只写
- Vertex::FVF,
- D3DPOOL_MANAGED,
- &Pyramid,
- 0);
- //
- // 用金字塔顶点数据填充缓存
- //
- Vertex* v;
- Pyramid->Lock(0,0,(void**)&v,0);
- // front face
- v[0] = Vertex(-1.0f, 0.0f, -1.0f, 0.0f, 0.707f, -0.707f);
- v[1] = Vertex( 0.0f, 1.0f, 0.0f, 0.0f, 0.707f, -0.707f);
- v[2] = Vertex( 1.0f, 0.0f, -1.0f, 0.0f, 0.707f, -0.707f);
- // left face
- v[3] = Vertex(-1.0f, 0.0f, 1.0f, -0.707f, 0.707f, 0.0f);
- v[4] = Vertex( 0.0f, 1.0f, 0.0f, -0.707f, 0.707f, 0.0f);
- v[5] = Vertex(-1.0f, 0.0f, -1.0f, -0.707f, 0.707f, 0.0f);
- // right face
- v[6] = Vertex( 1.0f, 0.0f, -1.0f, 0.707f, 0.707f, 0.0f);
- v[7] = Vertex( 0.0f, 1.0f, 0.0f, 0.707f, 0.707f, 0.0f);
- v[8] = Vertex( 1.0f, 0.0f, 1.0f, 0.707f, 0.707f, 0.0f);
- // back face
- v[9] = Vertex( 1.0f, 0.0f, 1.0f, 0.0f, 0.707f, 0.707f);
- v[10] = Vertex( 0.0f, 1.0f, 0.0f, 0.0f, 0.707f, 0.707f);
- v[11] = Vertex(-1.0f, 0.0f, 1.0f, 0.0f, 0.707f, 0.707f);
- Pyramid->Unlock();
- //
- // 创建/设置 材质
- //
- D3DMATERIAL9 mtrl = d3d::InitMtrl(d3d::WHITE,d3d::WHITE,d3d::WHITE,d3d::BLACK,5.0f);
- Device->SetMaterial(&mtrl);
- //
- // 创建方向光
- //
- D3DXCOLOR lc = d3d::WHITE;
- D3DLIGHT9 dir = d3d::InitDirectionalLight(&D3DXVECTOR3(1.0f,0.0f,0.0f),&lc);
- Device->SetLight(0,&dir);
- Device->LightEnable(0,true);
- //
- // 启用镜面高光,并规范化法向量
- //
- Device->SetRenderState(D3DRS_NORMALIZENORMALS,true);
- Device->SetRenderState(D3DRS_SPECULARENABLE,true);
- //
- // 创建取景变换矩阵
- //
- 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);
- Device->SetTransform(D3DTS_VIEW,&V);
- //
- // 设置投影矩阵
- //
- D3DXMATRIX proj;
- D3DXMatrixPerspectiveFovLH(
- &proj,
- D3DX_PI*0.5f,
- (float)Width/(float)Height,
- 1.0f,
- 1000.0f);
- Device->SetTransform(D3DTS_PROJECTION,&proj);
- return true;
- }
- void Cleanup()
- {
- d3d::Release<IDirect3DVertexBuffer9*>(Pyramid);
- }
- bool Display(float timeDelta)
- {
- if (Device)
- {
- //
- // 旋转矩阵
- //
- D3DXMATRIX yRot;
- // 绕 y 方向每帧递增角度
- static float y = 0.0f;
- D3DXMatrixRotationY(&yRot,y);
- y += timeDelta;
- // 角度达到360度后归零
- if (y>=6.28f)
- {
- y = 0.0f;
- }
- Device->SetTransform(D3DTS_WORLD,&yRot);
- //
- // 绘制场景
- //
- Device->Clear(0,0,D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER , 0xffffffff,1.0f,0);
- Device->BeginScene();
- Device->SetStreamSource(0,Pyramid,0,sizeof(Vertex));
- Device->SetFVF(Vertex::FVF);
- // 绘制4个三角形图元
- Device->DrawPrimitive(D3DPT_TRIANGLELIST,0,4);
- Device->EndScene();
- Device->Present(0,0,0,0);
- }
- return true;
- }
- //
- // WndProc
- //
- LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
- {
- switch(msg)
- {
- case WM_DESTROY:
- ::PostQuitMessage(0);
- break;
- case WM_KEYDOWN:
- if(wParam == VK_ESCAPE)
- ::DestroyWindow(hwnd);
- break;
- }
- return ::DefWindowProc(hwnd,msg,wParam,lParam);
- }
- //
- // WinMain
- //
- int WINAPI WinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPSTR lpCmdLine,
- int nShowCmd )
- {
- //程序入口地址
- //初始化D3D
- if (!d3d::InitD3D(hInstance,640,480,true,D3DDEVTYPE_HAL,&Device))
- {
- ::MessageBox(0, "InitD3D() - FAILED", 0, 0);
- return 0;
- }
- //分配资源
- if (!Setup())
- {
- ::MessageBox(0, "Setup() - FAILED", 0, 0);
- return 0;
- }
- //进入消息循环
- d3d::EnterMsgLoop(Display);
- //清理资源
- Cleanup();
- Device->Release();
- return 0;
- }
值得注意的是顶点法向量的计算。这里金字塔的各顶点的法向量为此顶点所在三角形面的面法向量。由于同一顶点当处于不同三角形面时的法向量不同,所以这里创建了12个顶点缓存。
- Device->CreateVertexBuffer(
- 12*sizeof(Vertex), //注意是12个顶点
- D3DUSAGE_WRITEONLY, // 只写
- Vertex::FVF,
- D3DPOOL_MANAGED,
- &Pyramid,
- 0);
那么顶点法向量是如何求得的呢? 设有3个顶点p0 p1 p2,则 p1 - p0 = u p2 - p0 = v 则面法线为 n = u x v (叉乘) 由于各顶点的法向量与面法线相同,所以 n0 = n1 = n2 = n
例如对于front面
// front face
v[0] = Vertex(-1.0f, 0.0f, -1.0f, 0.0f, 0.707f, -0.707f); //设为p0
v[1] = Vertex( 0.0f, 1.0f, 0.0f, 0.0f, 0.707f, -0.707f); //设为p1
v[2] = Vertex( 1.0f, 0.0f, -1.0f, 0.0f, 0.707f, -0.707f); //设为p2
p1 - p0 = u =(1,1,1) p2 - p0 = v =(2,0,0) n = u x v =(1,1,1) x (2,0,0) =(0,2,-2) (ps:这个不用我说了吧,不知道的google去) 单位化之则面法线为 n = ( 0.0f, 0.707f, -0.707f) 同理可得其他。
附图: