DirectX SDK 2006學習筆記6——光照和材質

  Copyright © MikeFeng  QQ: 76848502

  D3D支持三種光:環境光(Ambient Light),漫反射(Diffuse Light)和鏡面反射(Specular Light)。鏡面反射需要更多計算,因此D3D默認關閉鏡面反射。可以通過給SetRenderState傳D3DRS_AMBIENT來設置環境光。
  在現實世界中,我們看到一個紅色的物體是因爲其他顏色的光線被這個物體吸收了,我們看到的是這個物體反射的紅色光。D3D引入材質這個概念來模擬現實。
  Typpdef struct D3DMATERIAL9 {
        D3DCOLORVALUE Diffuse, Ambient, Specular, Emissive;
        Float Power;
  } D3DMATERIAL9;
  D3DCOLORVALUE是一個由r, g,b,a四個元素組成的色彩結構。
  Diffuse, Ambient, Specular分別代表這三種光的反射量,而Emissive讓物體使物體更亮,看上去就像本身在發光一樣。Power描述鏡面反射高亮的程度,值越高越亮。注意如果一個物體吸收所有顏色的光,那麼它就是黑色的。可以通過SetMaterial函數來設定材質。
  D3DMATERIAL9 blueMaterial;
  
  Device->SetMaterial(&blueMaterial)
 
  D3D引入頂點法線和麪法線來解決光照問題。面法線描述了一個多邊形的朝向,頂點法線的朝向可以自己定義。D3D必須知道頂點法線的方向以便計算它的反射光。
  在定義頂點的時候可以加入頂點法線的朝向。
struct Vertex
{
float _x, _y, _z;
float _nx, _ny, _nz;
static const DWORD FVF;
}
const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL;
  頂點顏色的信息被法線信息替代了,因爲我們需要根據材質和法線信息自己計算反射光。簡單的圖形可以由觀察得到頂點法線的信息,但是複雜的圖形需要通過計算得到。一種計算方法是將頂點法線等同於面法線。對於一個三角形來說,只要叉積的方式求出面發現即可。另一種更科學的方法是法線平均法。例如三棱錐的情況,最上面那個頂點的法線Vn是由包含着個定點的三個側面的法線V1, V2, V3平均數決定的。即Vn=(V1+V2+V3)/3。前面學過由於矩陣轉換可能會導致法線不偏移的現象,我們可以通過
Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);
來重新歸一(Renormalize)法線。
 
D3D中的光源有3種類型:點光源(向四周發射的光源),平行光源,錐形光源(類似於電筒光)。在錐形光源中有兩個角度來描述其大小,一個描述內錐,一個描述外錐。D3D中描述光源的結構是D3DLIGHT9:
typedef struct _D3DLIGHT9 {
D3DLIGHTTYPE Type;
D3DCOLORVALUE Diffuse;
D3DCOLORVALUE Specular;
D3DCOLORVALUE Ambient;
D3DVECTOR Position;
D3DVECTOR Direction;
float Range;
float Falloff;
float Attenuation0;
float Attenuation1;
float Attenuation2;
float Theta;
float Phi;
} D3DLIGHT9;
可以通過SetLight函數來設定光源。可以通過LightEnable函數來打開關閉光源。
 
給一個物體添加光照分爲以下幾步:
     啓用光照
     爲每個物體創建材質,並在渲染前爲這些物體配上材質
     創建一個或多個光源,設置並啓用它們
     啓用附加的光照狀態,例如鏡面高光。
設定光源最好不要放在OnCreateDevice中,因爲切換全屏/窗口或者改變窗口大小時會丟失光源信息。應放在OnResetDevice中。
  
Example 1           Cube Without Lighting
前面有個旋轉平面的例子,現在爲了要使用光照,立體的東西效果好一點。下面是一個立體方塊的例子,和平面的類似,不過頂點定義中多了頂點法線。
// cube.h
#ifndef __cubeH__
#define __cubeH__
 
#include "dxstdafx.h"
 
structVertex
{
        Vertex(){}
        Vertex(
                floatx, float y, float z,
                floatnx, float ny, float nz,
                floatu, float v)
        {
                _x = x_y = y_z = z;
                _nx = nx; _ny = ny; _nz = nz;
                _u = u_v = v;
        }
        float_x, _y, _z;
        float_nx, _ny, _nz;
        float_u, _v; // texture coordinates
};
#define FVF_VERTEX (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1)
 
class Cube
{
public:
        Cube(IDirect3DDevice9* device);
        ~Cube();
 
 
        HRESULTOnResetDevice();
        VOIDOnLostDevice();
        bool OnFrameMove( double fTime ) ;
        bool OnFrameRender( D3DMATERIAL9* mtrl, IDirect3DTexture9* tex );
 
private:
        HRESULTCreate();
        D3DMATERIAL9                    m_Mtrl;
        IDirect3DDevice9*       m_device;
        IDirect3DVertexBuffer9* m_vb;
        IDirect3DIndexBuffer9m_ib;
};
#endif//__cubeH__
 
//其他函數和plain.h中類似,因此只列出Create函數。其餘函數請參照前面的Plain
HRESULTCube::Create()
{
        m_device->CreateVertexBuffer(
                24 * sizeof(Vertex),
                D3DUSAGE_WRITEONLY,
                FVF_VERTEX,
                D3DPOOL_MANAGED,
                &m_vb,
                NULL);
 
        Vertex* v;
        m_vb->Lock(0, 0, (void**)&v, 0);
 
        // build box
 
        // x, y, z, nx, ny, nz, tu, tv
        // fill in the front face vertex data
        v[0] = Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
        v[1] = Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
        v[2] = Vertex( 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f);
        v[3] = Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);
 
        // fill in the back face vertex data
        v[4] = Vertex(-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
        v[5] = Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f);
        v[6] = Vertex( 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f);
        v[7] = Vertex(-1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f);
 
        // fill in the top face vertex data
        v[8] = Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
        v[9] = Vertex(-1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
        v[10] = Vertex( 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f);
        v[11] = Vertex( 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);
 
        // fill in the bottom face vertex data
        v[12] = Vertex(-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f);
        v[13] = Vertex( 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f);
        v[14] = Vertex( 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f);
        v[15] = Vertex(-1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f);
 
        // fill in the left face vertex data
        v[16] = Vertex(-1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
        v[17] = Vertex(-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
        v[18] = Vertex(-1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
        v[19] = Vertex(-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f);
 
        // fill in the right face vertex data
        v[20] = Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
        v[21] = Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
        v[22] = Vertex( 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
        v[23] = Vertex( 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f);
 
        m_vb->Unlock();
 
        m_device->CreateIndexBuffer(
                36 * sizeof(WORD),
                D3DUSAGE_WRITEONLY,
                D3DFMT_INDEX16,
                D3DPOOL_MANAGED,
                &m_ib,
                0);
 
        WORD* i = 0;
        m_ib->Lock(0, 0, (void**)&i, 0);
 
        // fill in the front face index data
        i[0] = 0; i[1] = 1; i[2] = 2;
        i[3] = 0; i[4] = 2; i[5] = 3;
 
        // fill in the back face index data
        i[6] = 4; i[7] = 5; i[8] = 6;
        i[9] = 4; i[10] = 6; i[11] = 7;
 
        // fill in the top face index data
        i[12] = 8; i[13] = 9; i[14] = 10;
        i[15] = 8; i[16] = 10; i[17] = 11;
 
        // fill in the bottom face index data
        i[18] = 12; i[19] = 13; i[20] = 14;
        i[21] = 12; i[22] = 14; i[23] = 15;
 
        // fill in the left face index data
        i[24] = 16; i[25] = 17; i[26] = 18;
        i[27] = 16; i[28] = 18; i[29] = 19;
 
        // fill in the right face index data
        i[30] = 20; i[31] = 21; i[32] = 22;
        i[33] = 20; i[34] = 22; i[35] = 23;
 
        m_ib->Unlock();
 
        returnS_OK;
}
 
主函數和旋轉平面基本相同,只是聲明瞭一個不同的全局變量:
Cube* g_pCube = NULL;
在未啓用光照模式[SetRenderState( D3DRS_LIGHTING, FALSE) ]的情況下效果如圖:
 
 
Example 2           Cube With Material & Lighting
 
Step 1 啓用光照( D3D中默認啓用 )
pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
 
Step 2 爲每個物體創建材質,並在渲染前爲這些物體配上材質
在主回調函數OnCreateDevice中定義光源
在上例Cube的構造函數添加材質代碼,使其如下
 
Cube::Cube(IDirect3DDevice9* device)
{
        // save a ptr to the device
        m_device = device;
        m_Mtrl.Ambient = D3DXCOLOR(1,0,0,0);
        m_Mtrl.Diffuse = D3DXCOLOR(0,0,0,0);
        m_Mtrl.Specular = D3DXCOLOR(1,1,1,0);
        m_Mtrl.Emissive = D3DXCOLOR(0,1,0,0);
m_Mtrl.Power = 5.0f;
}
其中m_Mtrl是cube.h中定義的成員變量D3DMATERIAL9,Emissive對於材質來說很重要,因爲它決定了我們看見的紋理顏色範圍,這裏定義的是隻顯示綠色。
 在Cube::OnFrameRender中添加如下代碼
bool Cube::OnFrameRender( D3DMATERIAL9* mtrl, IDirect3DTexture9* tex)
{
        if( mtrl )
                m_device->SetMaterial(mtrl);
        else if (&m_Mtrl)
                m_device->SetMaterial(&m_Mtrl);
 
        if( tex )
                m_device->SetTexture(0, tex);
 
        m_device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
        m_device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
        m_device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
        m_device->SetStreamSource(0, m_vb, 0, sizeof(Vertex));
        m_device->SetIndices(m_ib);
        m_device->SetFVF(FVF_VERTEX);
        m_device->DrawIndexedPrimitive(
                D3DPT_TRIANGLELIST,
                0,                 
                0,                 
                24,
                0,
                12); 
        return true;
}
  如果主回調函數中未給此函數設置材質,那麼它將使用自己的成員變量m_Mtrl作爲材質。
 
Step 3 創建一個或多個光源,設置並啓用它們
在主回調函數OnCreateDevice中添加如下代碼
        D3DLIGHT9dir;
        ::ZeroMemory(&dir, sizeof(dir));
        dir.Type      = D3DLIGHT_DIRECTIONAL;
 
        dir.Diffuse   = D3DXCOLOR(1,1,0,1);
        dir.Specular = D3DXCOLOR(0,0,11);
        dir.Ambient   = D3DXCOLOR(1,0,0,1);
        dir.Direction = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
 
        pd3dDevice->SetLight(0, &dir);
        pd3dDevice->LightEnable(0, true);
上面的參數只允許光源產生藍色鏡面反射光,以及紅色+綠色環境光。該光源是平行光源。
 
在主回調函數OnFrameRender中添加如下代碼,以重新歸一法線和啓用鏡面反射
      pd3dDevice->SetRenderState(D3DRS_NORMALIZENORMALS, true);
        pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, true);
注意不要忘記在適當時間禁用鏡面反射,以提高效率。
      pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, false);
 
Step 4 啓用附加的光照狀態(未設置)
 
藍色部分爲鏡面反射,綠色部分爲材質本身反射。因爲光源由x正方向照射過來,因此鏡面反射只有在右側纔有效果。最終效果如下
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章