为场景增添光照效果

 基于上一篇文章的框架,写了个增加光照效果的程序。

先贴一下代码:

  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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章