光照
使用光照时,我们无需自行指定顶点的颜色。Direct3D会将顶点送入光照的计算引擎,依据光源类型,材质以及物体表面相对光源的朝向,计算出每个顶点的颜色值。
1.光照的组成
在Direct3D的光照模型中,有3种类型的光。
1.1环境光。这种类型的光经其他表面反射到达物体表面,并照亮整个场景。
1.2漫射光。当光到达某一表面时,将沿着各个方向均匀反射。从各个方向观察,亮度均相同。
1.3镜面光。当光到达某一表面时,将沿着特定方向反射。只能在一定的角度才能看到高亮度。镜面光计算量较大,默认情况下Direct3D禁用镜面反射。启用镜面光的方法:
Device->SetRenderState(D3DRS_SPECULARENABLE,true);
每一种类型的光都可以用结构D3DCOLORVALUE和D3DXCOLOR来表示。这些类型描述了光线的颜色。描述光线颜色时,D3DXCOLOR结构中的Alpha值都将被忽略。
D3DXCOLOR WhiteCo(1.0f,1.0f, 1.0f, 1.0f)
2.材质
在现实世界中物体的颜色是由物体所反射的光的颜色决定的。例如红色的球会反射所有的红色入射光,并吸收非红色的光。在Direct3D中,通过定义物体的材质来模拟此现象。
typedef struct D3DMATERIAL9 {
D3DCOLORVALUE Diffuse; //材质多漫射光的反射率
D3DCOLORVALUE Ambient; //材质对环境光的反射率
D3DCOLORVALUE Specular; //材质对镜面光的反射率
D3DCOLORVALUE Emissive; //用于增强物体的亮度
float Power; //指定镜面高光点的锐度。
} D3DMATERIAL9, *LPD3DMATERIAL9;
顶点结构中不含有材质属性,我们可以用函数对材质进行设定。
HRESULT IDirect3DDevice9::SetMaterial(
CONST D3DMATERIAL9 * pMaterial
);
3.顶点法线
顶点法线描述的是构成多边形的各个顶点的法线。顶点法线的FVF标志D3DFVF_NORMAL.
使顶点法向量重新规范化:Device->SetRenderState(D3DRS_NORMALIZENORMALS,true);
4.光源。Direct3D支持3种类型的光源
4.1点光源 在世界坐标系中有固定位置,向各个方向发射光线。
4.2方向光 光源没有位置信息,发射的光线平行的沿某一方向走。
4.3聚光灯 光源有位置信息,光源锥形的沿特定方向传播。有内部锥形,外部锥形。
光源用D3DLIGHT9表示。
typedef struct D3DLIGHT9 {
D3DLIGHTTYPE Type; //光源类型 D3DLIGHT_POINT,D3DLIGHT_SPOT,D3DLIGHT_DIRECTIONAL
D3DCOLORVALUE Diffuse; //光源发出的漫射光的颜色
D3DCOLORVALUE Specular; //光源发出的镜面光的颜色
D3DCOLORVALUE Ambient; //光源发出的环境光的颜色
D3DVECTOR Position; //光源在世界坐标系中的位置向量,对方向光无意义
D3DVECTOR Direction; //光在世界坐标系中传播方向的向量,对点光源无意义。
float Range; //光线消亡前所能到达的最大光程。最大取值(FLT_MAX开平方), 对方向光无意义
float Falloff; //仅用于聚光灯。定义光强从内锥形到外锥形的衰减。一般取值1.0f.
float Attenuation0; //紧接着的3个参数定义了光强随距离的衰减方式
float Attenuation1;
float Attenuation2;
float Theta; //仅用于聚光灯,定义了内锥形的圆锥角,单位弧度
float Phi; //仅用于聚光灯,定义了外锥形的圆锥角,单位弧度。
} D3DLIGHT9, *LPD3DLIGHT;
#ifndef __d3dUtilityH__ #define __d3dUtilityH__ #include <d3dx9.h> #include <string> namespace d3d { bool InitD3D( HINSTANCE hInstance, // [in] Application instance. int width, int height, // [in] Backbuffer dimensions. bool windowed, // [in] Windowed (true)or full screen (false). D3DDEVTYPE deviceType, // [in] HAL or REF IDirect3DDevice9** device);// [out]The created device. int EnterMsgLoop( bool (*ptr_display)(float timeDelta)); LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); template<class T> void Release(T t) { if( t ) { t->Release(); t = 0; } } template<class T> void Delete(T t) { if( t ) { delete t; t = 0; } } const D3DXCOLOR WHITE( D3DCOLOR_XRGB(255, 255, 255) ); const D3DXCOLOR BLACK( D3DCOLOR_XRGB( 0, 0, 0) ); const D3DXCOLOR RED( D3DCOLOR_XRGB(255, 0, 0) ); const D3DXCOLOR GREEN( D3DCOLOR_XRGB( 0, 255, 0) ); const D3DXCOLOR BLUE( D3DCOLOR_XRGB( 0, 0, 255) ); const D3DXCOLOR YELLOW( D3DCOLOR_XRGB(255, 255, 0) ); const D3DXCOLOR CYAN( D3DCOLOR_XRGB( 0, 255, 255) ); const D3DXCOLOR MAGENTA( D3DCOLOR_XRGB(255, 0, 255) ); D3DMATERIAL9 InitMtrl(D3DXCOLOR a,D3DXCOLOR d,D3DXCOLOR s,D3DXCOLOR e,float p); const D3DMATERIAL9 White_Mtrl=InitMtrl(WHITE,WHITE,WHITE,BLACK,8.0f); const D3DMATERIAL9 Red_Mtrl=InitMtrl(RED,RED,RED,BLACK,8.0f); const D3DMATERIAL9 Green_Mtrl=InitMtrl(GREEN,GREEN,GREEN,BLACK,8.0f); const D3DMATERIAL9 Blue_Mtrl=InitMtrl(BLUE,BLUE,BLUE,BLACK,8.0f); D3DLIGHT9 InitDirectionalLight(D3DVECTOR* direction,D3DXCOLOR * color); D3DLIGHT9 InitPointLight(D3DXVECTOR3* position,D3DXCOLOR * color); D3DLIGHT9 InitSpotLight(D3DXVECTOR3* position, D3DXVECTOR3* direction, D3DXCOLOR* color); } #endif // __d3dUtilityH__
#include "d3dUtility.h" bool d3d::InitD3D( HINSTANCE hInstance, int width, int height, bool windowed, D3DDEVTYPE deviceType, IDirect3DDevice9** device) { WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC)d3d::WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(0, IDI_APPLICATION); wc.hCursor = LoadCursor(0, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = 0; wc.lpszClassName = "Direct3D9App"; if( !RegisterClass(&wc) ) { ::MessageBox(0, "RegisterClass() - FAILED", 0, 0); return false; } HWND hwnd = 0; hwnd = ::CreateWindow("Direct3D9App", "Direct3D9App", WS_EX_TOPMOST, 0, 0, width, height, 0 /*parent hwnd*/, 0 /* menu */, hInstance, 0 /*extra*/); if( !hwnd ) { ::MessageBox(0, "CreateWindow() - FAILED", 0, 0); return false; } ::ShowWindow(hwnd, SW_SHOW); ::UpdateWindow(hwnd); // // Init D3D: // HRESULT hr = 0; // Step 1: Create the IDirect3D9 object. IDirect3D9* d3d9 = 0; d3d9 = Direct3DCreate9(D3D_SDK_VERSION); if( !d3d9 ) { ::MessageBox(0, "Direct3DCreate9() - FAILED", 0, 0); return false; } // Step 2: Check for hardware vp. D3DCAPS9 caps; d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, deviceType, &caps); int vp = 0; if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) vp = D3DCREATE_HARDWARE_VERTEXPROCESSING; else vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; // Step 3: Fill out the D3DPRESENT_PARAMETERS structure. D3DPRESENT_PARAMETERS d3dpp; d3dpp.BackBufferWidth = width; d3dpp.BackBufferHeight = height; d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; d3dpp.BackBufferCount = 1; d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; d3dpp.MultiSampleQuality = 0; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.hDeviceWindow = hwnd; d3dpp.Windowed = windowed; d3dpp.EnableAutoDepthStencil = true; d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; d3dpp.Flags = 0; d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // Step 4: Create the device. hr = d3d9->CreateDevice( D3DADAPTER_DEFAULT, // primary adapter deviceType, // device type hwnd, // window associated with device vp, // vertex processing &d3dpp, // present parameters device); // return created device if( FAILED(hr) ) { // try again using a 16-bit depth buffer d3dpp.AutoDepthStencilFormat = D3DFMT_D16; hr = d3d9->CreateDevice( D3DADAPTER_DEFAULT, deviceType, hwnd, vp, &d3dpp, device); if( FAILED(hr) ) { d3d9->Release(); // done with d3d9 object ::MessageBox(0, "CreateDevice() - FAILED", 0, 0); return false; } } d3d9->Release(); // done with d3d9 object return true; } int d3d::EnterMsgLoop( bool (*ptr_display)(float timeDelta) ) { MSG msg; ::ZeroMemory(&msg, sizeof(MSG)); static float lastTime = (float)timeGetTime(); while(msg.message != WM_QUIT) { if(::PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } else { float currTime = (float)timeGetTime(); float timeDelta = (currTime - lastTime)*0.001f; ptr_display(timeDelta); lastTime = currTime; } } return msg.wParam; } D3DMATERIAL9 d3d::InitMtrl(D3DXCOLOR a,D3DXCOLOR d,D3DXCOLOR s,D3DXCOLOR e,float p) { D3DMATERIAL9 mtrl; mtrl.Ambient=a; mtrl.Diffuse =d; mtrl.Emissive =e; mtrl.Power =p; mtrl.Specular=s; return mtrl; } D3DLIGHT9 d3d::InitDirectionalLight(D3DVECTOR* direction,D3DXCOLOR* color) { D3DLIGHT9 light; memset(&light,0,sizeof(light)); light.Type=D3DLIGHT_DIRECTIONAL; light.Ambient=*color * 0.4f; light.Diffuse=*color; light.Specular=*color * 0.6f; light.Direction= *direction; return light; } D3DLIGHT9 d3d::InitPointLight(D3DXVECTOR3* position,D3DXCOLOR * color) { D3DLIGHT9 light; memset(&light,0,sizeof(light)); light.Type=D3DLIGHT_POINT; light.Ambient=*color * 0.4f; light.Diffuse=*color; light.Specular=*color * 0.6f; light.Position=*position; light.Range = 1000.0f; light.Falloff = 1.0f; light.Attenuation0 = 1.0f; light.Attenuation1 = 0.0f; light.Attenuation2 = 0.0f; return light; } D3DLIGHT9 d3d::InitSpotLight(D3DXVECTOR3* position, D3DXVECTOR3* direction, D3DXCOLOR* color) { D3DLIGHT9 light; memset(&light,0,sizeof(light)); light.Type=D3DLIGHT_SPOT; light.Ambient = *color * 0.0f; light.Diffuse = *color; light.Specular = *color * 0.6f; light.Position = *position; light.Direction = *direction; light.Range = 1000.0f; light.Falloff = 1.0f; light.Attenuation0 = 1.0f; light.Attenuation1 = 0.0f; light.Attenuation2 = 0.0f; light.Theta = 0.4f; light.Phi = 0.9f; return light;}
#include "d3dUtility.h" IDirect3DDevice9* Device = 0; const int Width = 640; const int Height = 480; IDirect3DVertexBuffer9* vb = 0; D3DXMATRIX WorldMatrix; struct Vertex //顶点结构:位置和顶点法线 { Vertex(){} Vertex(float x, float y, float z,float nx,float ny,float nz) { _x = x; _y = y; _z = z; _nx=nx;_ny=ny;_nz=nz; } float _x, _y, _z; float _nx,_ny,_nz; static const DWORD FVF; }; const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL; // // Framework Functions // bool Setup() { Device->SetRenderState(D3DRS_LIGHTING, true); //设置光照可用 Device->CreateVertexBuffer( //创建顶点缓存 12 * sizeof(Vertex), // size in bytes D3DUSAGE_WRITEONLY, // flags Vertex::FVF, // vertex format D3DPOOL_MANAGED, // managed memory pool &vb, // return create vertex buffer 0); // not used - set to 0 Vertex* vertices; vb->Lock(0, 0, (void**)&vertices, 0); //获取指向顶点缓存内容的指针 vertices[0] = Vertex(-1.0f, 0.0f, -1.0f,0.0f,0.707f,-0.707f); vertices[1] = Vertex( 0.0f, 1.0f, 0.0f,0.0f,0.707f,-0.707f); vertices[2] = Vertex( 1.0f, 0.0f, -1.0f,0.0f,0.707f,-0.707f); vertices[3] = Vertex(-1.0f, 0.0f, 1.0f,-0.707f,0.707f,0.0f); vertices[4] = Vertex( 0.0f, 1.0f, 0.0f,-0.707f,0.707f,0.0f); vertices[5] = Vertex( -1.0f, 0.0f, -1.0f,-0.707f,0.707f,0.0f); vertices[6] = Vertex( 1.0f, 0.0f, -1.0f, 0.707f, 0.707f, 0.0f); vertices[7] = Vertex( 0.0f, 1.0f, 0.0f, 0.707f, 0.707f, 0.0f); vertices[8] = Vertex( 1.0f, 0.0f, 1.0f, 0.707f, 0.707f, 0.0f); vertices[9] = Vertex( 1.0f, 0.0f, 1.0f, 0.0f, 0.707f, 0.707f); vertices[10] = Vertex( 0.0f, 1.0f, 0.0f, 0.0f, 0.707f, 0.707f); vertices[11] = Vertex(-1.0f, 0.0f, 1.0f, 0.0f, 0.707f, 0.707f); vb->Unlock(); D3DMATERIAL9 Mtrl; //定义材质,设置材质对各种光的反射率 Mtrl.Ambient =d3d::RED; Mtrl.Diffuse=d3d::BLUE; Mtrl.Specular=d3d::CYAN; Mtrl.Emissive=d3d::BLACK; Mtrl.Power=5.0f; Device->SetMaterial(&Mtrl); D3DLIGHT9 light; //设置方向光,并对其值赋值 memset(&light,0,sizeof(light)); light.Type=D3DLIGHT_DIRECTIONAL; light.Diffuse=d3d::BLUE; light.Specular=d3d::BLUE; light.Ambient=d3d::BLUE; light.Direction=D3DXVECTOR3(1.0f,0.0f,0.0f); Device->SetLight(0,&light); Device->LightEnable(0,true); Device->SetRenderState(D3DRS_NORMALIZENORMALS,true); //使顶点法向量重新规范化 Device->SetRenderState(D3DRS_SPECULARENABLE,true); //启用镜面光 D3DXVECTOR3 pos(0.0f, 1.0f, -3.0f); D3DXVECTOR3 target(0.0f, 0.0f, 0.0f); D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); D3DXMATRIX V; D3DXMatrixLookAtLH(&V, &pos, &target, &up); //取景变换矩阵,设置摄像机在时间坐标系中位置,界坐标系的被观察点,世界坐标系中表示“向上”方向的向量 Device->SetTransform(D3DTS_VIEW, &V); //转换成观察者坐标系 // // Set the projection matrix. // D3DXMATRIX proj; D3DXMatrixPerspectiveFovLH( &proj, D3DX_PI * 0.5f, // 90 - degree (float)Width / (float)Height, 1.0f, 1000.0f); Device->SetTransform(D3DTS_PROJECTION, &proj); //投影 return true; } void Cleanup() { d3d::Release<IDirect3DVertexBuffer9*>(vb); } bool Display(float timeDelta) { if( Device ) { D3DXMATRIX yRot; static float y = 0.0f; D3DXMatrixRotationY(&yRot, y); y += timeDelta; if( y >= 6.28f ) y = 0.0f; Device->SetTransform(D3DTS_WORLD, &yRot); Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0); Device->BeginScene(); Device->SetStreamSource(0, vb, 0, sizeof(Vertex)); Device->SetFVF(Vertex::FVF); Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 4); //画出顶点缓存 Device->SetFVF(Vertex::FVF); Device->EndScene(); Device->Present(0, 0, 0, 0); } return true; } // // WndProc // LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) { case WM_DESTROY: ::PostQuitMessage(0); break; case WM_KEYDOWN: if( wParam == VK_ESCAPE ) ::DestroyWindow(hwnd); break; } return ::DefWindowProc(hwnd, msg, wParam, lParam); } // // WinMain // int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd) { if(!d3d::InitD3D(hinstance, Width, Height, true, D3DDEVTYPE_HAL, &Device)) { ::MessageBox(0, "InitD3D() - FAILED", 0, 0); return 0; } if(!Setup()) { ::MessageBox(0, "Setup() - FAILED", 0, 0); return 0; } d3d::EnterMsgLoop( Display ); Cleanup(); Device->Release(); return 0; }
人生无处不代码,没有代码不人生。