基於上一篇文章的框架,寫了個增加光照效果的程序。
先貼一下代碼:
- //////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // 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) 同理可得其他。
附圖: