爲場景增添光照效果

 基於上一篇文章的框架,寫了個增加光照效果的程序。

先貼一下代碼:

  1. //////////////////////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // File: light.cpp
  4. // by tianzhihen 
  5. // 2008.10.13
  6. // MSVC++ 8.0
  7. //////////////////////////////////////////////////////////////////////////////////////////////////
  8. #include "d3dUtility.h"
  9. //
  10. //  全局變量
  11. //
  12. IDirect3DDevice9* Device = 0;
  13. const int Width  = 640;
  14. const int Height = 480;
  15. // 金字塔頂點緩存 
  16. IDirect3DVertexBuffer9* Pyramid = 0;
  17. //
  18. // 類和結構體
  19. //
  20. struct Vertex 
  21. {
  22.     Vertex(){}
  23.     Vertex(float x,float y,float z,float nx,float ny,float nz)
  24.     {
  25.         _x = x; _y = y; _z = z;
  26.         _nx = nx; _ny = ny; _nz = nz;
  27.     }
  28.     float _x,_y,_z;
  29.     float _nx,_ny,_nz;  //頂點法向量
  30.     static const DWORD FVF;
  31. };
  32. const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL;
  33. //
  34. // 框架函數
  35. //
  36. bool Setup()
  37. {
  38.     //
  39.     // 打開光照開關
  40.     //
  41.     Device->SetRenderState(D3DRS_LIGHTING,true);
  42.     //
  43.     // 創建頂點/索引緩存
  44.     //  
  45.     Device->CreateVertexBuffer(
  46.         12*sizeof(Vertex),  //注意是12個頂點
  47.         D3DUSAGE_WRITEONLY, // 只寫
  48.         Vertex::FVF,
  49.         D3DPOOL_MANAGED,
  50.         &Pyramid,
  51.         0);
  52.     //
  53.     // 用金字塔頂點數據填充緩存
  54.     //
  55.     Vertex* v;
  56.     Pyramid->Lock(0,0,(void**)&v,0);
  57.         
  58.     // front face
  59.     v[0] = Vertex(-1.0f, 0.0f, -1.0f, 0.0f, 0.707f, -0.707f);
  60.     v[1] = Vertex( 0.0f, 1.0f,  0.0f, 0.0f, 0.707f, -0.707f);
  61.     v[2] = Vertex( 1.0f, 0.0f, -1.0f, 0.0f, 0.707f, -0.707f);
  62.     // left face
  63.     v[3] = Vertex(-1.0f, 0.0f,  1.0f, -0.707f, 0.707f, 0.0f);
  64.     v[4] = Vertex( 0.0f, 1.0f,  0.0f, -0.707f, 0.707f, 0.0f);
  65.     v[5] = Vertex(-1.0f, 0.0f, -1.0f, -0.707f, 0.707f, 0.0f);
  66.     // right face
  67.     v[6] = Vertex( 1.0f, 0.0f, -1.0f, 0.707f, 0.707f, 0.0f);
  68.     v[7] = Vertex( 0.0f, 1.0f,  0.0f, 0.707f, 0.707f, 0.0f);
  69.     v[8] = Vertex( 1.0f, 0.0f,  1.0f, 0.707f, 0.707f, 0.0f);
  70.     // back face
  71.     v[9]  = Vertex( 1.0f, 0.0f,  1.0f, 0.0f, 0.707f, 0.707f);
  72.     v[10] = Vertex( 0.0f, 1.0f,  0.0f, 0.0f, 0.707f, 0.707f);
  73.     v[11] = Vertex(-1.0f, 0.0f,  1.0f, 0.0f, 0.707f, 0.707f);
  74.     Pyramid->Unlock();
  75.     //
  76.     // 創建/設置 材質 
  77.     //
  78.     D3DMATERIAL9 mtrl = d3d::InitMtrl(d3d::WHITE,d3d::WHITE,d3d::WHITE,d3d::BLACK,5.0f);
  79.     Device->SetMaterial(&mtrl);
  80.     //
  81.     // 創建方向光
  82.     //
  83.     D3DXCOLOR lc = d3d::WHITE;
  84.     D3DLIGHT9 dir = d3d::InitDirectionalLight(&D3DXVECTOR3(1.0f,0.0f,0.0f),&lc);
  85.     Device->SetLight(0,&dir);
  86.     Device->LightEnable(0,true);
  87.     //
  88.     // 啓用鏡面高光,並規範化法向量
  89.     //
  90.     Device->SetRenderState(D3DRS_NORMALIZENORMALS,true);
  91.     Device->SetRenderState(D3DRS_SPECULARENABLE,true);
  92.     //
  93.     // 創建取景變換矩陣
  94.     //
  95.     D3DXVECTOR3 position(0.0f,0.0f,-3.0f);
  96.     D3DXVECTOR3 target(0.0f,0.0f,0.0f);
  97.     D3DXVECTOR3 up(0.0f,1.0f,0.0f);
  98.     D3DXMATRIX V;
  99.     D3DXMatrixLookAtLH(&V,&position,&target,&up);
  100.     Device->SetTransform(D3DTS_VIEW,&V);
  101.     //
  102.     // 設置投影矩陣
  103.     //
  104.     D3DXMATRIX proj;
  105.     D3DXMatrixPerspectiveFovLH(
  106.                 &proj,
  107.                 D3DX_PI*0.5f,
  108.                 (float)Width/(float)Height,
  109.                 1.0f,
  110.                 1000.0f);
  111.     Device->SetTransform(D3DTS_PROJECTION,&proj);
  112.     return true;
  113. }
  114. void Cleanup()
  115. {
  116.     d3d::Release<IDirect3DVertexBuffer9*>(Pyramid);
  117. }
  118. bool Display(float timeDelta)
  119. {
  120.     if (Device)
  121.     {
  122.         //
  123.         // 旋轉矩陣
  124.         //
  125.         D3DXMATRIX yRot;
  126.         // 繞 y 方向每幀遞增角度
  127.         static float y = 0.0f;
  128.         D3DXMatrixRotationY(&yRot,y);
  129.         y += timeDelta;
  130.         // 角度達到360度後歸零
  131.         if (y>=6.28f)
  132.         {
  133.             y = 0.0f;
  134.         }
  135.         Device->SetTransform(D3DTS_WORLD,&yRot);
  136.         //
  137.         // 繪製場景
  138.         //
  139.         Device->Clear(0,0,D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER , 0xffffffff,1.0f,0);
  140.         Device->BeginScene();
  141.         Device->SetStreamSource(0,Pyramid,0,sizeof(Vertex));
  142.         Device->SetFVF(Vertex::FVF);
  143.         // 繪製4個三角形圖元
  144.         Device->DrawPrimitive(D3DPT_TRIANGLELIST,0,4);
  145.         Device->EndScene();
  146.         Device->Present(0,0,0,0);
  147.     }
  148.     return true;
  149. }
  150. //
  151. // WndProc
  152. //
  153. LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
  154. {
  155.     switch(msg)
  156.     {
  157.     case WM_DESTROY:
  158.         ::PostQuitMessage(0);
  159.         break;
  160.     case WM_KEYDOWN:
  161.         if(wParam == VK_ESCAPE)
  162.             ::DestroyWindow(hwnd);
  163.         break;
  164.     }
  165.     return ::DefWindowProc(hwnd,msg,wParam,lParam);
  166. }
  167. //
  168. //  WinMain
  169. //
  170. int WINAPI WinMain(HINSTANCE hInstance,
  171.                    HINSTANCE hPrevInstance, 
  172.                    LPSTR lpCmdLine,
  173.                    int nShowCmd )
  174. {
  175.     //程序入口地址
  176.     //初始化D3D
  177.     if (!d3d::InitD3D(hInstance,640,480,true,D3DDEVTYPE_HAL,&Device))
  178.     {
  179.         ::MessageBox(0, "InitD3D() - FAILED", 0, 0);
  180.         return 0;
  181.     }
  182.     //分配資源
  183.     if (!Setup())
  184.     {
  185.         ::MessageBox(0, "Setup() - FAILED", 0, 0);
  186.         return 0;
  187.     }
  188.     //進入消息循環
  189.     d3d::EnterMsgLoop(Display);
  190.     //清理資源
  191.     Cleanup();
  192.     Device->Release();
  193.     return 0;
  194. }

值得注意的是頂點法向量的計算。這裏金字塔的各頂點的法向量爲此頂點所在三角形面的面法向量。由於同一頂點當處於不同三角形面時的法向量不同,所以這裏創建了12個頂點緩存。

  1.     Device->CreateVertexBuffer(
  2.         12*sizeof(Vertex),  //注意是12個頂點
  3.         D3DUSAGE_WRITEONLY, // 只寫
  4.         Vertex::FVF,
  5.         D3DPOOL_MANAGED,
  6.         &Pyramid,
  7.         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)  同理可得其他。

 

附圖:

 

發佈了126 篇原創文章 · 獲贊 10 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章