DirectX SDK 2006学习笔记6——光照和材质

来源:互联网 发布:淘宝宝贝上架最佳时间 编辑:程序博客网 时间:2024/05/19 11:47

  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正方向照射过来,因此镜面反射只有在右侧才有效果。最终效果如下
原创粉丝点击