Direct3D极速入门宝典
其实DirectX9.0里有非常详细的教程和参考,大多数人只需要看看这些帮助就可以自己学习D3D了,我的这篇文章适合那些很懒但想快速入门、不懂英文或编程知识很欠缺的人看。装好DirectX9.0后,打开VC.net,新建一个Win32工程,在StdAfx.h里添加下面的语句:
#include <d3d9.h> // D3D标准头文件
#include <D3dx9math.h> // D3D数学库头文件 #include <stdio.h> // 这个不用我说了吧? #pragma comment( lib, "d3d9" ) // D3D的静态库 #pragma comment( lib, "d3dx9" ) // D3D数学库的静态库 |
#include "stdafx.h"
#define MAX_LOADSTRING 100 HINSTANCE g_hInst; HWND g_hWnd; IDirect3D9 *g_pD3D; IDirect3DDevice9 *g_pd3dDevice; IDirect3DVertexBuffer9 *g_pVB; TCHAR szTitle[MAX_LOADSTRING]; TCHAR szWindowClass[MAX_LOADSTRING]; ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); void OnIdle( void ); void OnCreate( HWND hWnd ); HRESULT InitD3D( void ); HRESULT CreateObject( void ); void ReleaseD3D( void ); HRESULT SetModalMatrix( void ); HRESULT SetProjMatrix( WORD wWidth, WORD wHeight ); void BeforePaint( void ); int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { MSG msg; HACCEL hAccelTable; LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_D3DTEST, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_D3DTEST); while ( true ) { if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } continue; } if ( WM_QUIT == msg.message ) { break; } OnIdle(); } UnregisterClass( szWindowClass, g_hInst ); return (int)msg.wParam; } ATOM MyRegisterClass( HINSTANCE hInstance ) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_D3DTEST); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = (LPCTSTR)IDC_D3DTEST; wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL); return RegisterClassEx(&wcex); } BOOL InitInstance( HINSTANCE hInstance, int nCmdShow ) { g_hInst = hInstance; CreateWindow( szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL ); if ( !g_hWnd ) { return FALSE; } ShowWindow( g_hWnd, nCmdShow ); UpdateWindow( g_hWnd ); return TRUE; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; switch (message) { case WM_CREATE: OnCreate( hWnd ); break; case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); switch (wmId) { case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_SIZE: SetProjMatrix( LOWORD( lParam ), HIWORD( lParam ) ); break; case WM_DESTROY: ReleaseD3D(); PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } void OnCreate( HWND hWnd ) { g_hWnd = hWnd; InitD3D(); CreateObject(); } void ReleaseD3D( void ) { } HRESULT InitD3D( void ) { return S_OK; } void BeforePaint( void ) { } HRESULT CreateObject( void ) { return S_OK; } void OnIdle( void ) { } HRESULT SetModalMatrix( void ) { return S_OK; } HRESULT SetProjMatrix( WORD wWidth, WORD wHeight ) { return S_OK; } |
g_hInst = hInstance;
在创建窗体时,我并没有直接将CreateWindow的返回值——创建成功的窗体句柄赋给全局变量g_hWnd,因为CreateWindow函数在执行中会引发几个消息,其中有WM_CREATE,在这个消息的处理函数中OnCreate中,我执行了下面的语句:
g_hWnd = hWnd;
这样,我们就可以早一点用到g_hWnd了。SetProjMatrix是设置投影矩阵的,投影矩形仅受窗口纵横比影响,所以在WM_SIZE事件时调用时就可以了。而SetModalMatrix是设置模型矩阵的,也就是眼睛点和视点之类的东东,所以它一定要在Rander的时候,准确的说是在Render之前执行。InitD3D在窗体创建时调用,用于初始化D3D设备。CreateObject和InitD3D一样是初始化函数,它用于创建三维对象。
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; // 翻转缓冲区时不改动后台缓冲
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; // ARGB颜色模式
g_pd3dDevice->SetRenderState( D3DRS_AMBIENT, D3DCOLOR_COLORVALUE( 0.6f, 0.6f, 0.6f, 1.0 ) );
mtrl.Diffuse.g = mtrl.Ambient.g = 200.0f / 255.0f;
mtrl.Diffuse.b = mtrl.Ambient.b = 255.0f / 255.0f;
mtrl.Diffuse.a = mtrl.Ambient.a = 1.0f;
{ 5.0f, 5.0f, 0.0f }, { 5.0f, 5.0f, 10.0f },
{ 5.0f, -5.0f, 0.0f }, { 5.0f, -5.0f, 10.0f },
{-5.0f, -5.0f, 0.0f }, {-5.0f, -5.0f, 10.0f },
{-5.0f, 5.0f, 0.0f }, {-5.0f, 5.0f, 10.0f },
};
0, 4, 6, 0, 2, 4,
0, 6, 7, 0, 7, 1,
0, 3, 2, 0, 1, 3,
5, 2, 3, 5, 4, 2,
5, 6, 4, 5, 7, 6,
5, 1, 7, 5, 3, 1,
};
{
D3DVECTOR pos;
D3DVECTOR normal;
};
for ( int i = 0; i < 36; i++ )
{
ExpandBox[i].pos = SrcBox[ wIndex[i] ];
}
{
D3DVECTOR Tri[3];
Tri[0] = ExpandBox[ i * 3 + 0 ].pos;
Tri[1] = ExpandBox[ i * 3 + 1 ].pos;
Tri[2] = ExpandBox[ i * 3 + 2 ].pos;
ExpandBox[ i * 3 + 0 ].normal.x = 0.0f;
ExpandBox[ i * 3 + 0 ].normal.y = 0.0f;
ExpandBox[ i * 3 + 0 ].normal.z = 1.0f;
CalcNormal( Tri, &(ExpandBox[ i * 3 + 0 ].normal) );
ExpandBox[ i * 3 + 1 ].normal = ExpandBox[ i * 3 + 0 ].normal;
ExpandBox[ i * 3 + 2 ].normal = ExpandBox[ i * 3 + 0 ].normal;
}
那么到这里你可以会问另一个问题,也许D3D可以知道我们的元素里包含顶点的哪些数据,但D3D又是怎样得知这些数据在结构体里,也就是在内存中的排列顺序的呢?很不幸,D3D无法得知你的排列顺序,但D3D指定了这些数据的排列顺序,比如法向量一定在顶点后面,而颜色又一定要放在法向量后面。关于这个排列顺序表你得去看看MSDN里关于Vertex Formats的详细说明了。下面我们来创建顶点数组:
if( FAILED( g_pd3dDevice->CreateVertexBuffer( sizeof(ExpandBox),
0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL ) ) ) { return E_FAIL; } |
VOID* pVertices;
if( FAILED( g_pVB->Lock( 0, sizeof(ExpandBox), (void**)&pVertices, 0 ) ) ) return E_FAIL; MoveMemory( pVertices, ExpandBox, sizeof(ExpandBox) ); g_pVB->Unlock(); |
D3DXMATRIX matProj;
D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, (float)wWidth / (float)wHeight, 1.0f, 100.0f ); return g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj ); |
static float fRadius = 0.5f;
fRadius -= 0.003f; if ( fRadius < 0) { fRadius = D3DX_PI * 2 ; } D3DXMATRIX matWorld; D3DXMatrixRotationZ( &matWorld, 0.0f ); if ( FAILED( g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld ) ) ) { return E_FAIL; } D3DXMATRIX matView; D3DXMatrixLookAtLH( &matView, &D3DXVECTOR3( cosf( fRadius ) * 40.0f, sinf( fRadius ) * 40.0f, 30.0 ), &D3DXVECTOR3( 0.0f, 0.0f, 0.0f ), &D3DXVECTOR3( 0.0f, 0.0f, 1.0f ) ); g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView ); return S_OK; |
if ( g_pd3dDevice != NULL )
{ g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255,255,200), 1.0f, 0 ); if ( SUCCEEDED( g_pd3dDevice->BeginScene() ) ) { BeforePaint(); if ( FAILED( SetModalMatrix() ) ) { return; } g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) ); g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX ); g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 12 ); g_pd3dDevice->EndScene(); g_pd3dDevice->Present( NULL, NULL, NULL, NULL ); } } |
g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
另外,D3D的背景色是在Clear的时候指定的。现在你可以运行你的程序了,但是你会发现,盒子是黑色的一片,那是因为你没有打开任何光源。而OpenGL在没有打开任何光源时物体是纯白色的,呵呵,具有一定的迷惑性哦~现在我们来打开一个灯,来照亮这个物体。就在BeforePaint里:
D3DLIGHT9 light;
ZeroMemory( &light, sizeof(light) ); light.Position = D3DXVECTOR3( 30.0f, 30.0f, 30.0f ); light.Attenuation1 = 0.05f; light.Diffuse.r = 1.0f; light.Diffuse.g = 1.0f; light.Diffuse.b = 1.0f; light.Range = 1000.0f; light.Type = D3DLIGHT_POINT; g_pd3dDevice->SetLight( 0, &light ); g_pd3dDevice->LightEnable( 0, TRUE); |
现在你的代码应该是这样样子:
// D3D006.cpp : 定义应用程序的入口点。 #include "stdafx.h" #define MAX_LOADSTRING 100 struct CUSTOMVERTEX // 全局变量: HINSTANCE hInst; // 当前实例 // 此代码模块中包含的函数的前向声明: void OnIdle( void ); class CTimer private: int APIENTRY _tWinMain(HINSTANCE hInstance, // TODO: 在此放置代码。 // 初始化全局字符串 // 执行应用程序初始化: hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_D3D006)); // 主消息循环: UnregisterClass(szWindowClass, hInst); return (int) msg.wParam;
// wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; return RegisterClassEx(&wcex); // hInst = hInstance; // 将实例句柄存储在全局变量中 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, if (!hWnd) ShowWindow(hWnd, nCmdShow); return TRUE; // switch (message) // “关于”框的消息处理程序。 case WM_COMMAND: void OnCreate(HWND hWnd) void ReleaseD3D() HRESULT InitD3D() D3DMATERIAL9 mtrl; return S_OK; void BeforePaint(void) HRESULT CreateObject() return S_OK; void OnIdle() HRESULT SetModalMatrix() return S_OK; HRESULT SetProjMatrix(WORD wWidth, WORD wHeight) |