D3D中的材质和光照处理
提示:
阅读本文需要一定的3D图形学和DirectX9基础,如果你发现阅读困难,请参阅 D3D中基本立体面的绘制。
本文用到的坐标系统变换函数请参阅 DirectX 9的坐标系统变换。
在现实世界中,物体在各种环境光线的照射下,吸收了光线的能量,并将这些能量进行反射而形成反射光。
颜色和光照
物体表面光泽是由于表面的反射光刺激人的眼睛视网膜而引起的一种视官感觉,这种色觉实际上是由光的波长决定的。每种波长的光在光谱中占据一定的位置,具有特定的色泽和亮度。
对于任意波长为s的光,由于s可分解为s = aR + bG + cB,其中R,G,B为红色光,绿色光和蓝色光的波长,于是在RGB为基底的情况下,光波长由三元数(a, b, c)来决定。因此光的颜色值量化可以这样来进行:首先用颜色值(1, 0, 0)表示红色R,用颜色值(0, 1, 0)表示绿色G,用(0, 0, 1)表示蓝色B,而任意波长的光的颜色值C则可用(r, g, b)表示,以表明颜色s = aR + bG + cB 是用RGB三色通过混色而得来的,其中r, g和b取区间的浮点数。
为了实现像素颜色由暗变明的处理,通常还添加一个alpha分量来说明颜色的饱和度。此时颜色值将用四元组(r ,g, b, a)来表示,其中a就是alpha分量,也是一个介于区间[0, 1]的浮点数。当r, g和b的颜色值确定以后,通过乘以一个a分量得到一个新的颜色值ar+ag+ab,这样获得的颜色值并没有改变原有颜色的RGB成分比例。
DirectX提供了以下的一个存放四元颜色值的结构体D3DCOLORVALUE,来看看具体定义:
Describes color values.
typedef struct D3DCOLORVALUE {
float r;
float g;
float b;
float a;
} D3DCOLORVALUE, *LPD3DCOLORVALUE;
Members
- r
- Floating-point value specifying the red component of a color. This value generally is in the range from 0.0 through 1.0, with 0.0 being black.
- g
- Floating-point value specifying the green component of a color. This value generally is in the range from 0.0 through 1.0, with 0.0 being black.
- b
- Floating-point value specifying the blue component of a color. This value generally is in the range from 0.0 through 1.0, with 0.0 being black.
- a
- Floating-point value specifying the alpha component of a color. This value generally is in the range from 0.0 through 1.0, with 0.0 being black.
Remarks
You can set the members of this structure to values outside the range of 0 through 1 to implement some unusual effects. Values greater than 1 produce strong lights that tend to wash out a scene. Negative values produce dark lights that actually remove light from a scene.
由于使用了浮点数作为颜色值的分量,因而可方便地定义颜色值的加法和乘法等运算,以产生新的颜色值。例如:对于颜色C1 = (a1, r1, g1, b1)和C2 = (a2, r2, g2, b2),定义C1+C2,kC1和C1C2的颜色值如下:
C1 + C2 = (a1 + a2, r1 + r2, g1 + g2, b1 + b2)
kC1 = (ka1, kr1, kg1, kb1)
C1C2 = (a1 x a2, r1 x r2, g1 x g2, b1 x b2)
为支持颜色值的算术运算,DirectX提供了一个颜色值的扩展结构D3DXCOLOR,来看看它的具体定义:
{
#ifdef __cplusplus
public :
D3DXCOLOR() {}
D3DXCOLOR( DWORD argb );
D3DXCOLOR( CONST FLOAT * );
D3DXCOLOR( CONST D3DXFLOAT16 * );
D3DXCOLOR( CONST D3DCOLORVALUE& );
D3DXCOLOR( FLOAT r, FLOAT g, FLOAT b, FLOAT a );
// casting
operator DWORD () const ;
operator FLOAT* ();
operator CONST FLOAT* () const ;
operator D3DCOLORVALUE* ();
operator CONST D3DCOLORVALUE* () const ;
operator D3DCOLORVALUE& ();
operator CONST D3DCOLORVALUE& () const ;
// assignment operators
D3DXCOLOR& operator += ( CONST D3DXCOLOR& );
D3DXCOLOR& operator -= ( CONST D3DXCOLOR& );
D3DXCOLOR& operator *= ( FLOAT );
D3DXCOLOR& operator /= ( FLOAT );
// unary operators
D3DXCOLOR operator + () const ;
D3DXCOLOR operator - () const ;
// binary operators
D3DXCOLOR operator + ( CONST D3DXCOLOR& ) const ;
D3DXCOLOR operator - ( CONST D3DXCOLOR& ) const ;
D3DXCOLOR operator * ( FLOAT ) const ;
D3DXCOLOR operator / ( FLOAT ) const ;
friend D3DXCOLOR operator * ( FLOAT, CONST D3DXCOLOR& );
BOOL operator == ( CONST D3DXCOLOR& ) const ;
BOOL operator != ( CONST D3DXCOLOR& ) const ;
#endif //__cplusplus
FLOAT r, g, b, a;
} D3DXCOLOR, *LPD3DXCOLOR;
此外,DirectX还提供了一个使用整数来定义颜色值的D3DCOLOR类型,D3DCOLOR类型实际上是一个32bit的 DWORD类型。
typedef DWORD D3DCOLOR;
该类型的颜色值Alpha, Red, Green, Blue分量各占8个bit,并从高位字节到低位字节进行存放,各分量的值范围均为0~255之间。由于使用整数,D3DCOLOR类型的颜色值的范围相对于使用浮点数的D3DCOLORVALUE颜色值的范围少。
为了方便定义D3DCOLOR类型的颜色值, DirectX还提供了如下的两个可直接将4个0~255之间的整数定义为D3DCOLOR颜色值的宏。
#define D3DCOLOR_ARGB(a,r,g,b) \
((D3DCOLOR)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff)))
#define D3DCOLOR_RGBA(r,g,b,a) D3DCOLOR_ARGB(a,r,g,b)
还可利用DirectX提供的D3DCOLOR_XRGB(r, g, b)宏,定义Alpha值为255的D3DCOLOR颜色值。
光源设置
颜色是光的视觉感觉,DirectX为了支持场景的光照渲染,提供了点光源(Point Light),聚焦光源(Spot Light),方向光源(Directional Light)和环境光源(Ambient Light)4种标准类型的光源设置。
点光源是一种向空间各个方向等强度发射光的光源,一个灯泡就是点光源的例子,点光源发射光的强度随着距离的增加而逐渐减弱。
聚焦光源的发光区域是一个圆锥体,内锥光的强度沿着聚焦光的主发射方向,随距离的增加而逐步衰减,外锥光的强度沿着外径随距离的增加而逐步衰减。手电筒是聚焦光源的一个例子。
方向光源从无限远处以特定的方向发射平行光线到无穷远处,光的强度不随距离的增加而衰减。太阳光就是方向光源的一个例子,方向光源不需要设置光源位置,衰减系数和光照的作用范围。
环境光是根据场景中的各种光源的综合照射效果所模拟出的一种光源,这种光源的光来自各个方向,不做任何的衰减处理,能均匀地照射物体表面的各个部位。
如下图所示:环境光、点光源、聚光灯以及直射光分别以不同的方式发射光线
当光源确定以后,就可以进一步设置照射光的各种属性,以确定物体表面反射光的颜色。
光源及其反射光线的各种属性可利用D3DLIGHT9结构体来进行设置,下面是该结构体的具体定义:
Defines a set of lighting properties.
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, *LPD3DLIGHT;
Members
- Type
- Type of the light source. This value is one of the members of the D3DLIGHTTYPE enumerated type.
- Diffuse
- Diffuse color emitted by the light. This member is a D3DCOLORVALUE structure.
- Specular
- Specular color emitted by the light. This member is a D3DCOLORVALUE structure.
- Ambient
- Ambient color emitted by the light. This member is a D3DCOLORVALUE structure.
- Position
- Position of the light in world space, specified by a D3DVECTOR structure. This member has no meaning for directional lights and is ignored in that case.
- Direction
- Direction that the light is pointing in world space, specified by a D3DVECTOR structure. This member has meaning only for directional and spotlights. This vector need not be normalized, but it should have a nonzero length.
- Range
- Distance beyond which the light has no effect. The maximum allowable value for this member is the square root of FLT_MAX. This member does not affect directional lights.
- Falloff
- The effect of falloff on the lighting is subtle. Furthermore, a small performance penalty is incurred by shaping the falloff curve. For these reasons, most developers set this value to 1.0.
- Attenuation0
- Value specifying how the light intensity changes over distance. Attenuation values are ignored for directional lights. This member represents an attenuation constant. For information about attenuation, see Light Properties (Direct3D 9). Valid values for this member range from 0.0 to infinity. For non-directional lights, all three attenuation values should not be set to 0.0 at the same time.
- Attenuation1
- Value specifying how the light intensity changes over distance. Attenuation values are ignored for directional lights. This member represents an attenuation constant. For information about attenuation, see Light Properties (Direct3D 9). Valid values for this member range from 0.0 to infinity. For non-directional lights, all three attenuation values should not be set to 0.0 at the same time.
- Attenuation2
- Value specifying how the light intensity changes over distance. Attenuation values are ignored for directional lights. This member represents an attenuation constant. For information about attenuation, see Light Properties (Direct3D 9). Valid values for this member range from 0.0 to infinity. For non-directional lights, all three attenuation values should not be set to 0.0 at the same time.
- Theta
- Angle, in radians, of a spotlight's inner cone - that is, the fully illuminated spotlight cone. This value must be in the range from 0 through the value specified by Phi.
- Phi
- Angle, in radians, defining the outer edge of the spotlight's outer cone. Points outside this cone are not lit by the spotlight. This value must be between 0 and pi.
Decrease in illumination between a spotlight's inner cone (the angle specified by Theta) and the outer edge of the outer cone (the angle specified by Phi).
光源参数设置完毕,还需要利用IDirect3DDevice9接口的 SetLight函数将光源设置到渲染管道流水线中。
Assigns a set of lighting properties for this device.
HRESULT SetLight(
DWORD Index,
CONST D3DLight9 * pLight
);
Parameters
- Index
- [in] Zero-based index of the set of lighting properties to set. If a set of lighting properties exists at this index, it is overwritten by the new properties specified in pLight.
- pLight
- [in] Pointer to a D3DLIGHT9 structure, containing the lighting parameters to set.
Return Values
If the method succeeds, the return value is D3D_OK. If the method fails, the return value can be D3DERR_INVALIDCALL.
Remarks
Set light properties by preparing a D3DLIGHT9 structure and then calling the IDirect3DDevice9::SetLight method. The IDirect3DDevice9::SetLight method accepts the index at which the device should place the set of light properties to its internal list of light properties, and the address of a prepared D3DLIGHT9 structure that defines those properties. You can call IDirect3DDevice9::SetLight with new information as needed to update the light's illumination properties.
The system allocates memory to accommodate a set of lighting properties each time you call the IDirect3DDevice9::SetLight method with an index that has never been assigned properties. Applications can set a number of lights, with only a subset of the assigned lights enabled at a time. Check the MaxActiveLights member of the D3DCAPS9 structure when you retrieve device capabilities to determine the maximum number of active lights supported by that device. If you no longer need a light, you can disable it or overwrite it with a new set of light properties.
The following example prepares and sets properties for a white point-light whose emitted light will not attenuate over distance.
// Assume d3dDevice is a valid pointer to an IDirect3DDevice9 interface.
D3DLight9 d3dLight;
HRESULT hr;
// Initialize the structure.
ZeroMemory(&D3dLight, sizeof(d3dLight));
// Set up a white point light.
d3dLight.Type = D3DLIGHT_POINT;
d3dLight.Diffuse.r = 1.0f;
d3dLight.Diffuse.g = 1.0f;
d3dLight.Diffuse.b = 1.0f;
d3dLight.Ambient.r = 1.0f;
d3dLight.Ambient.g = 1.0f;
d3dLight.Ambient.b = 1.0f;
d3dLight.Specular.r = 1.0f;
d3dLight.Specular.g = 1.0f;
d3dLight.Specular.b = 1.0f;
// Position it high in the scene and behind the user.
// Remember, these coordinates are in world space, so
// the user could be anywhere in world space, too.
// For the purposes of this example, assume the user
// is at the origin of world space.
d3dLight.Position.x = 0.0f;
d3dLight.Position.y = 1000.0f;
d3dLight.Position.z = -100.0f;
// Don't attenuate.
d3dLight.Attenuation0 = 1.0f;
d3dLight.Range = 1000.0f;
// Set the property information for the first light.
hr = d3dDevice->SetLight(0, &d3dLight);
if (SUCCEEDED(hr))
// Handle Success
else
// Handle failure
Enable a light source by calling the IDirect3DDevice9::LightEnable method for the device.
将光源设置给渲染管道流水线后,还需要利用IDirect3DDevice9接口的LightEnable函数开启该光源,否则渲染管道流水线将不会采用该光源的参数进行光照处理。
Enables or disables a set of lighting parameters within a device.
HRESULT LightEnable(
DWORD LightIndex,
BOOL bEnable
);
- LightIndex
- [in] Zero-based index of the set of lighting parameters that are the target of this method.
- bEnable
- [in] Value that indicates if the set of lighting parameters are being enabled or disabled. Set this parameter to TRUE to enable lighting with the parameters at the specified index, or FALSE to disable it.
If the method succeeds, the return value is D3D_OK. If the method fails, the return value can be D3DERR_INVALIDCALL.
If a value for LightIndex is outside the range of the light property sets assigned within the device, the IDirect3DDevice9::LightEnable method creates a light source represented by a D3DLIGHT9 structure with the following properties and sets its enabled state to the value specified in bEnable.
Member | Default |
---|---|
Type | D3DLIGHT_DIRECTIONAL |
Diffuse | (R:1, G:1, B:1, A:0) |
Specular | (R:0, G:0, B:0, A:0) |
Ambient | (R:0, G:0, B:0, A:0) |
Position | (0, 0, 0) |
Direction | (0, 0, 1) |
Range | 0 |
Falloff | 0 |
Attenuation0 | 0 |
Attenuation1 | 0 |
Attenuation2 | 0 |
Theta | 0 |
Phi | 0 |
当光源设置和开启后,就需要调用IDirect3DDevice9接口的SetRenderState函数打开渲染管道流水线的光照开关。这样,渲染管道流水线才会真正执行光照流程处理。
材质设置
物体受到光照而呈现出的表面颜色,一方面是照射光源的属性来决定,另一方面则取决于物体对光的反射属性和物体自身的表面颜色,即由物体的材质属性来决定。
物体的材质属性可用D3DMATERIAL9结构体进行设置:
Specifies material properties.
typedef struct D3DMATERIAL9 {
D3DCOLORVALUE Diffuse;
D3DCOLORVALUE Ambient;
D3DCOLORVALUE Specular;
D3DCOLORVALUE Emissive;
float Power;
} D3DMATERIAL9, *LPD3DMATERIAL9;
Members
- Diffuse
- Value specifying the diffuse color of the material. See D3DCOLORVALUE.
- Ambient
- Value specifying the ambient color of the material. See D3DCOLORVALUE.
- Specular
- Value specifying the specular color of the material. See D3DCOLORVALUE.
- Emissive
- Value specifying the emissive color of the material. See D3DCOLORVALUE.
- Power
- Floating-point value specifying the sharpness of specular highlights. The higher the value, the sharper the highlight.
Remarks
To turn off specular highlights, set D3DRS_SPECULARENABLE to FALSE, using D3DRENDERSTATETYPE. This is the fastest option because no specular highlights will be calculated.
For more information about using the lighting engine to calculate specular lighting, see Specular Lighting (Direct3D 9).
当材质结构体填充以后,就可以利用IDirect3DDevice9接口的SetMaterial函数设置渲染管道流水线进行物体表面渲染时所应采用的材质参数。
Sets the material properties for the device.
HRESULT SetMaterial(
CONST D3DMATERIAL9 * pMaterial
);
Parameters
- pMaterial
- [in] Pointer to a D3DMATERIAL9 structure, describing the material properties to set.
Return Values
If the method succeeds, the return value is D3D_OK. D3DERR_INVALIDCALL if the pMaterial parameter is invalid.
顶点的法向量
三维场景物体的渲染归结为各个剖分三角形面的渲染,每个三角形面的顶点坐标值用来确定三维物体的空间形态,而顶点的颜色值则可根据某种插值方法而计算出物体表面各个像素的颜色值。
在光源光的照射下,顶点的颜色值由光的入射方向与顶点的法向量的夹角来决定。为此,三维物体的光照处理必须提供顶点的坐标和法向量的坐标值,这样渲染管道流水线执行到光照流程这一步时,就可取出顶点的法向量坐标值来计算顶点的实际光照颜色。
如下图所示:每个顶点都有一个法线指向特定的方向,根据光线与法线之间的角度来确定光线如何在多边形面上反弹以及如何进行阴影计算。
顶点往往是邻接三角形面的公共交点,它的法向量可取为这些邻接三角形面的法向量的一个平均向量,如此渲染出来顶点附近的表面颜色值将会是均匀变化的。如果顶点法向量直接取为某个所在三角形面的法向量,那么各个面的颜色将会互不相同。
无论顶点法向量采用三角形面的法向量,还是邻接三角形面法向量的平均,都需要利用已知的三个顶点坐标,计算出三个顶点所在平面的法向量。
好了,我们现在来看看具体的编码。
需要在工程中设置链接d3dx9.lib d3d9.lib dxguid.lib dinput8.lib winmm.lib。
由于文件中用到了GE_APP和GE_INPUT这两个类,它们的具体使用说明请参阅 主窗口和DirectInput的封装。
同时用到了GE_TIMER这个类,具体请参阅 游戏中时间的封装。
若发现代码中存在错误,敬请指出。
源码下载
LightMaterial.h的定义:
[Include File]
PURPOSE:
Define for light and meterial.
*************************************************************************************/
#ifndef LIGHT_MATERIAL_H
#define LIGHT_MATERIAL_H
struct CUSTOM_VERTEX
{
float x, y, z;
float nx, ny, nz;
};
#define CUSTOM_VERTEX_FVF (D3DFVF_XYZ | D3DFVF_NORMAL)
class LIGHT_MATERIAL
{
private :
IDirect3D9* _d3d;
IDirect3DDevice9* _d3d_device;
IDirect3DVertexBuffer9* _vertex_buffer;
public :
LIGHT_MATERIAL();
~LIGHT_MATERIAL();
bool Create_D3D_Device(HWND hwnd, bool full_screen = true );
bool Init_Vertex_Buffer();
void Compute_Triangle_Normal(D3DXVECTOR3& v1, D3DXVECTOR3& v2, D3DXVECTOR3& v3, D3DVECTOR& normal);
void Set_Camera();
void Set_Point_Light(D3DCOLORVALUE& dif, D3DCOLORVALUE& amb, D3DCOLORVALUE& spe, D3DVECTOR& pos);
void Set_Object_Material(D3DCOLORVALUE& dif, D3DCOLORVALUE& amb, D3DCOLORVALUE& spe,
D3DCOLORVALUE& emi, float power);
void Render();
void Release_COM_Object();
};
#endif
LightMaterial.cpp的定义:
[Implement File]
PURPOSE:
Define for light and meterial.
*************************************************************************************/
#include "GE_COMMON.h"
#include "LightMaterial.h"
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
//------------------------------------------------------------------------------------
// Constructor, do nothing.
//------------------------------------------------------------------------------------
LIGHT_MATERIAL::LIGHT_MATERIAL()
{
}
//------------------------------------------------------------------------------------
// Release all com object.
//------------------------------------------------------------------------------------
LIGHT_MATERIAL::~LIGHT_MATERIAL()
{
Release_COM_Object();
}
//------------------------------------------------------------------------------------
// Create direct3D interface and direct3D device.
//------------------------------------------------------------------------------------
bool LIGHT_MATERIAL::Create_D3D_Device(HWND hwnd, bool full_screen)
{
// Create a IDirect3D9 object and returns an interace to it.
_d3d = Direct3DCreate9(D3D_SDK_VERSION);
if (_d3d == NULL)
return false ;
// retrieve adapter capability
D3DCAPS9 d3d_caps;
_d3d->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3d_caps);
bool hardware_process_enable = (d3d_caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ? true : false );
// Retrieves the current display mode of the adapter.
D3DDISPLAYMODE display_mode;
if (FAILED(_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &display_mode)))
return false ;
// set present parameter for direct3D device
D3DPRESENT_PARAMETERS present_param = {0};
present_param.BackBufferWidth = WINDOW_WIDTH;
present_param.BackBufferHeight = WINDOW_HEIGHT;
present_param.BackBufferFormat = display_mode.Format;
present_param.BackBufferCount = 1;
present_param.hDeviceWindow = hwnd;
present_param.Windowed = !full_screen;
present_param.SwapEffect = D3DSWAPEFFECT_FLIP;
present_param.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
// Creates a device to represent the display adapter.
DWORD behavior_flags;
behavior_flags = hardware_process_enable ?
D3DCREATE_HARDWARE_VERTEXPROCESSING : D3DCREATE_SOFTWARE_VERTEXPROCESSING;
if (FAILED(_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, behavior_flags,
&present_param, &_d3d_device)))
{
return false ;
}
// create successfully
return true ;
}
//------------------------------------------------------------------------------------
// Initialize vertex buffer.
//------------------------------------------------------------------------------------
bool LIGHT_MATERIAL::Init_Vertex_Buffer()
{
CUSTOM_VERTEX custom_vertex[12] = {0};
D3DXVECTOR3 v[]={
D3DXVECTOR3(5.0f, 6.0f, 5.0f), // left triangle
D3DXVECTOR3(6.0f, 0.0f, 3.0f),
D3DXVECTOR3(1.0f, 0.0f, 7.0f),
D3DXVECTOR3(5.0f, 6.0f, 5.0f), // right triangle
D3DXVECTOR3(10.0f, 0.0f, 8.0f),
D3DXVECTOR3(6.0f, 0.0f, 3.0f),
D3DXVECTOR3(5.0f, 6.0f, 5.0f), // back triangle
D3DXVECTOR3(1.0f, 0.0f, 7.0f),
D3DXVECTOR3(10.0f, 0.0f, 8.0f),
D3DXVECTOR3(1.0f, 0.0f, 7.0f), // bottom triangle
D3DXVECTOR3(6.0f, 0.0f, 3.0f),
D3DXVECTOR3(10.0f, 0.0f, 8.0f)
};
D3DVECTOR normal;
// compute all triangle normal
for ( int i = 0; i < 12; i += 3)
{
// compute current triangle's normal
Compute_Triangle_Normal(v[i], v[i+1], v[i+2], normal);
// assign current vertex coordinate and current triangle normal to custom vertex array
for ( int j = 0; j < 3; j++)
{
int k = i + j;
custom_vertex[k].x = v[k].x;
custom_vertex[k].y = v[k].y;
custom_vertex[k].z = v[k].z;
custom_vertex[k].nx = normal.x;
custom_vertex[k].ny = normal.y;
custom_vertex[k].nz = normal.z;
}
}
BYTE* vertex_data;
// create vertex buffer
if (FAILED(_d3d_device->CreateVertexBuffer(12 * sizeof (CUSTOM_VERTEX), 0, CUSTOM_VERTEX_FVF,
D3DPOOL_DEFAULT, &_vertex_buffer, NULL)))
{
return false ;
}
// get data pointer to vertex buffer
if (FAILED(_vertex_buffer->Lock(0, 0, ( void **) &vertex_data, 0)))
return false ;
// copy custom vertex data into vertex buffer
memcpy(vertex_data, custom_vertex, sizeof (custom_vertex));
// unlock vertex buffer
_vertex_buffer->Unlock();
return true ;
}
//------------------------------------------------------------------------------------
// Set camera position.
//------------------------------------------------------------------------------------
void LIGHT_MATERIAL::Set_Camera()
{
D3DXVECTOR3 eye(-6.0, 1.5, 10.0);
D3DXVECTOR3 at(6.0, 2.0, 3.0);
D3DXVECTOR3 up(0.0, 1.0, 0.0);
D3DXMATRIX view_matrix;
// Builds a left-handed, look-at matrix.
D3DXMatrixLookAtLH(&view_matrix, &eye, &at, &up);
// Sets d3d device view transformation state.
_d3d_device->SetTransform(D3DTS_VIEW, &view_matrix);
D3DXMATRIX proj_matrix;
// Builds a left-handed perspective projection matrix based on a field of view.
D3DXMatrixPerspectiveFovLH(&proj_matrix, D3DX_PI/2, 800/600, 1.0, 1000.0);
// Sets d3d device projection transformation state.
_d3d_device->SetTransform(D3DTS_PROJECTION, &proj_matrix);
// enable automatic normalization of vertex normals
_d3d_device->SetRenderState(D3DRS_NORMALIZENORMALS, true );
}
//------------------------------------------------------------------------------------
// Set point light.
//------------------------------------------------------------------------------------
void LIGHT_MATERIAL::Set_Point_Light(D3DCOLORVALUE& dif, D3DCOLORVALUE& amb, D3DCOLORVALUE& spe, D3DVECTOR& pos)
{
D3DLIGHT9 light;
// clear memory with 0
ZeroMemory(&light, sizeof (D3DLIGHT9));
light.Type = D3DLIGHT_POINT;
light.Diffuse = dif;
light.Ambient = amb;
light.Specular = spe;
light.Position = pos;
light.Attenuation0 = 1.0;
light.Attenuation1 = 0.0;
light.Attenuation2 = 0.0;
light.Range = 1000.0;
// Assigns point lighting properties for this device
_d3d_device->SetLight(0, &light);
// enable point light
_d3d_device->LightEnable(0, TRUE);
// enable light
_d3d_device->SetRenderState(D3DRS_LIGHTING, TRUE);
// add ambient light
_d3d_device->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(50, 50, 50));
}
//------------------------------------------------------------------------------------
// Sets the material properties for the device.
//------------------------------------------------------------------------------------
void LIGHT_MATERIAL::Set_Object_Material(D3DCOLORVALUE& dif, D3DCOLORVALUE& amb, D3DCOLORVALUE& spe,
D3DCOLORVALUE& emi, float power)
{
D3DMATERIAL9 material;
material.Diffuse = dif;
material.Ambient = amb;
material.Specular = spe;
material.Emissive = emi;
material.Power = power;
// Sets the material properties for the device.
_d3d_device->SetMaterial(&material);
}
//------------------------------------------------------------------------------------
// Compute triangle normal.
//------------------------------------------------------------------------------------
void LIGHT_MATERIAL::Compute_Triangle_Normal(D3DXVECTOR3& v1, D3DXVECTOR3& v2, D3DXVECTOR3& v3, D3DVECTOR& normal)
{
D3DXVECTOR3 vec1 = v1 - v2;
D3DXVECTOR3 vec2 = v1 - v3;
D3DXVECTOR3 normal_vec;
D3DXVec3Cross(&normal_vec, &vec1, &vec2);
D3DXVec3Normalize(&normal_vec, &normal_vec);
normal = (D3DVECTOR) normal_vec;
}
//------------------------------------------------------------------------------------
// Render object.
//------------------------------------------------------------------------------------
void LIGHT_MATERIAL::Render()
{
if (_d3d_device == NULL)
return ;
// clear surface with black
_d3d_device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0, 0);
// begin scene
_d3d_device->BeginScene();
// Binds a vertex buffer to a device data stream.
_d3d_device->SetStreamSource(0, _vertex_buffer, 0, sizeof (CUSTOM_VERTEX));
// Sets the current vertex stream declaration.
_d3d_device->SetFVF(CUSTOM_VERTEX_FVF);
// Renders a sequence of nonindexed, geometric primitives of the specified type from the current
// set of data input streams.
_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 4);
// end scene
_d3d_device->EndScene();
// Presents the contents of the next buffer in the sequence of back buffers owned by the device.
_d3d_device->Present(NULL, NULL, NULL, NULL);
}
//------------------------------------------------------------------------------------
// Release vertex buffer, D3D device, D3D.
//------------------------------------------------------------------------------------
void LIGHT_MATERIAL::Release_COM_Object()
{
Safe_Release(_vertex_buffer);
Safe_Release(_d3d_device);
Safe_Release(_d3d);
}
再编写个测试用例,main.cpp的定义如下所示:
[Implement File]
PURPOSE:
Test for material and lighting.
*************************************************************************************/
#define DIRECTINPUT_VERSION 0x0800
#include "GE_APP.h"
#include "GE_INPUT.h"
#include "GE_TIMER.h"
#include "LightMaterial.h"
#pragma warning(disable : 4305 4996)
int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
GE_APP ge_app;
GE_INPUT ge_input;
GE_TIMER ge_timer;
LIGHT_MATERIAL light_material;
MSG msg = {0};
float last_time = 0;
// set for material property
D3DCOLORVALUE material_dif = {1.0f, 1.0f, 1.0f, 1.0f};
D3DCOLORVALUE material_amb = {1.0f, 1.0f, 1.0f, 1.0f};
D3DCOLORVALUE material_spe = {1.0f, 0.0f, 0.0f, 1.0f};
D3DCOLORVALUE material_emi = {0.0f, 0.0f, 1.0f, 1.0f};
// set for light property
D3DCOLORVALUE light_dif = {1.0f, 0.0f, 0.0f, 1.0f};
D3DCOLORVALUE light_amb = {0.0f, 0.7f, 0.0f, 1.0f};
D3DCOLORVALUE light_spe = {0.0f, 0.0f, 0.0f, 0.0f};
D3DVECTOR light_pos = {5.0f, 6.0f, -20.0f};
// create window
if (! ge_app.Create_Window("Material and light test", instance, cmd_show))
return false ;
HWND hwnd = ge_app.Get_Window_Handle();
// create directinput
ge_input.Create_Input(instance, hwnd);
SetWindowPos(hwnd, 0, 0,0,0,0, SWP_NOSIZE);
SetCursorPos(0, 0);
// initialize game time
ge_timer.Init_Game_Time();
// Create direct3D interface and direct3D device.
if (! light_material.Create_D3D_Device(hwnd, false ))
return false ;
// Initialize vertex buffer with curstom vertex structure.
if (! light_material.Init_Vertex_Buffer())
return false ;
// set camera position
light_material.Set_Camera();
// Sets the material properties for the device.
light_material.Set_Object_Material(material_dif, material_amb, material_spe, material_emi, 0);
// set point light
light_material.Set_Point_Light(light_dif, light_amb, light_spe, light_pos);
// render object
light_material.Render();
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, NULL, 0,0 , PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// read data from keyboard buffer
if (ge_input.Read_Keyboard())
{
// press "ESC", close window.
if (ge_input.Is_Key_Pressed(DIK_ESCAPE))
PostQuitMessage(0);
}
// If it is time to render, reset point light property and render it.
if ((ge_timer.Get_Game_Play_Time() - last_time) > 70)
{
if (light_amb.r < 1.0f)
light_amb.r += 0.001f;
else
light_amb.r = 0.0f;
if (light_amb.g < 1.0f)
light_amb.g += 0.02f;
else
light_amb.g = 0.0f;
if (light_amb.b < 1.0f)
light_amb.b += 0.03f;
else
light_amb.b = 0.0f;
// reset point light
light_material.Set_Point_Light(light_dif, light_amb, light_spe, light_pos);
// render object
light_material.Render();
// update last render time
last_time = ge_timer.Get_Game_Play_Time();
}
}
}
UnregisterClass(WINDOW_CLASS_NAME, instance);
return true ;
}
该函数为了每隔一段时间改变三棱锥的光照颜色,调用时钟的 Get_Game_Play_Time函数计算时间差,然后将顶点坐标和顶点法向量坐标倒入渲染管道流水线,架设摄影机进行取景,设置光源和三棱锥的材质属性。最后,在定时器计算出的一帧时间内,改变光源的颜色属性,完成每一帧的渲染。
输出效果: