Immediate Mode 教學 1 介紹了畫 "已經Transform成螢幕座標" 的三角形,感覺上跟2D功能很像,那當三角形資訊是尚未 Transform的,要如何投影到螢幕座標系統呢?整個投影的原理先不詳細介紹,先藉助 Direct3D本身的Transform機制,提供 Direct3D 世界座標的端點資料,由 Direct3D 幫我們轉換成螢幕座標並畫出來。
這次的範例仍然是畫一個三角形,但是三角形座標為世界座標。同時要畫世界座標的東西了,當然就要指定攝影機的位置與對哪一點拍攝,此時2D螢幕呈現的,其實就是以攝影機角度觀看的世界。 完整程式列表: //----------------------------------------------------------------------------- // File: d3d8im_2.cpp // // Desc: This is the second tutorial for using Direct3D 8.0. // // http://latte.fanmesh.com //----------------------------------------------------------------------------- #include <d3d8.h> #include <d3dx8.h> #include <d3dx8math.h>
//----------------------------------------------------------------------------- // Global variables //----------------------------------------------------------------------------- LPDIRECT3D8 pD3D = NULL; // Used to create the D3DDevice LPDIRECT3DDEVICE8 pDev = NULL; // Our RenderD3Ding device LPDIRECT3DVERTEXBUFFER8 pVB = NULL;
//----------------------------------------------------------------------------- // Name: InitD3D() // Desc: Initializes Direct3D //----------------------------------------------------------------------------- HRESULT InitD3D( HWND hWnd ) { // Create the D3D object, which is needed to create the D3DDevice. if( NULL == ( pD3D = Direct3DCreate8( D3D_SDK_VERSION ) ) ) return E_FAIL;
// Get the current desktop display mode D3DDISPLAYMODE d3ddm; if( FAILED( pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) ) return E_FAIL;
D3DPRESENT_PARAMETERS d3dpp; ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.Windowed = TRUE; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.BackBufferFormat = d3ddm.Format;
if( FAILED( pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &pDev ) ) ) { return E_FAIL; }
D3DXMATRIX matWorld; D3DXMatrixIdentity( &matWorld ); pDev->SetTransform( D3DTS_WORLD, &matWorld );
D3DXMATRIX matView; D3DXMatrixLookAtLH( &matView, &D3DXVECTOR3( 0.0f, 50.0f,-200.0f ), &D3DXVECTOR3( 0.0f, 0.0f, 0.0f ), &D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) ); pDev->SetTransform( D3DTS_VIEW, &matView );
D3DXMATRIX matProj; D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 500.0f ); pDev->SetTransform( D3DTS_PROJECTION, &matProj );
pDev->SetRenderState( D3DRS_LIGHTING, FALSE );
// Device state would normally be set here return S_OK; }
//----------------------------------------------------------------------------- // Name: CloseD3D() // Desc: Releases all previously initialized objects //----------------------------------------------------------------------------- void CloseD3D(void) { if( pVB != NULL) pVB->Release();
if( pDev != NULL) pDev->Release();
if( pD3D != NULL) pD3D->Release(); }
//----------------------------------------------------------------------------- // Name: RenderD3D() // Desc: Draws the scene //----------------------------------------------------------------------------- struct MYVERTEX { FLOAT x,y,z; DWORD color; };
#define NUM_VERT 3
struct MYVERTEX vert[NUM_VERT]= { 0, 50, 0, 0x00FF0000, 40, 0, 0, 0x000000FF, -40, 0, 0, 0x0000FF00 };
BOOL NeedCreateVBFlag=true; float rad=0.0f;
void RenderD3D(void) { if( NULL == pDev ) return;
// Clear the backbuffer to a blue color pDev->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
// Begin the scene pDev->BeginScene();
//----------------------------------------------------------------------------- // RenderD3Ding of scene objects can happen here //----------------------------------------------------------------------------- if(NeedCreateVBFlag) { pDev->CreateVertexBuffer(sizeof(MYVERTEX)*NUM_VERT, D3DUSAGE_WRITEONLY, D3DFVF_XYZ|D3DFVF_DIFFUSE, D3DPOOL_DEFAULT, &pVB); NeedCreateVBFlag=false; }
MYVERTEX *v; pVB->Lock(0,0,(BYTE **)&v,0); memcpy(v,vert,sizeof(MYVERTEX)*NUM_VERT); pVB->Unlock();
D3DXMATRIX matWorld; rad+= 0.001f; D3DXMatrixRotationY(&matWorld, rad); pDev->SetTransform( D3DTS_WORLD, &matWorld );
pDev->SetVertexShader(D3DFVF_XYZ|D3DFVF_DIFFUSE); pDev->SetStreamSource(0,pVB,sizeof(MYVERTEX)); pDev->DrawPrimitive(D3DPT_TRIANGLELIST,0,NUM_VERT/3); //-----------------------------------------------------------------------------
// End the scene pDev->EndScene();
// Present the backbuffer contents to the display pDev->Present( NULL, NULL, NULL, NULL ); }
//----------------------------------------------------------------------------- // Name: MsgProc() // Desc: The window's message handler //----------------------------------------------------------------------------- LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { switch( msg ) { case WM_DESTROY: PostQuitMessage( 0 ); return 0; }
return DefWindowProc( hWnd, msg, wParam, lParam ); }
//----------------------------------------------------------------------------- // Name: WinMain() // Desc: The application's entry point //----------------------------------------------------------------------------- INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT ) { MSG msg;
// Register the window class WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, "D3D Tutorial", NULL }; RegisterClassEx( &wc );
// Create the application's window HWND hWnd = CreateWindow( "D3D Tutorial", "D3D8 Tutorial 02", WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, GetDesktopWindow(), NULL, wc.hInstance, NULL );
// Initialize Direct3D if( SUCCEEDED( InitD3D( hWnd ) ) ) { // Show the window ShowWindow( hWnd, SW_SHOWDEFAULT ); UpdateWindow( hWnd );
while(1) { if(PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE )) { if( GetMessage( &msg, NULL, 0, 0 ) ) { TranslateMessage(&msg); DispatchMessage(&msg); } else { // This msg is WM_QUIT. break; } } else { RenderD3D(); } } }
// Clean up everything and exit the app CloseD3D(); UnregisterClass( "D3D Tutorial", wc.hInstance ); return 0; }
1.#include 除了 d3d8.h , 還要多個 d3dx8.h 與 d3dx8math.h 。 D3DX 提供了許多以 Direct3D 為基礎,有用的functions,到了世界座標幾乎是非用不可了。 //----------------------------------------------------------------------------- // File: d3d8im_2.cpp // // Desc: This is the second tutorial for using Direct3D 8.0. // // http://latte.fanmesh.com //----------------------------------------------------------------------------- #include <d3d8.h> #include <d3dx8.h> #include <d3dx8math.h>
2.此範例只需要3個全域變數 pD3D、pDev、pVB,其中 pD3D 就是最新的 Direct3D/DirectDraw 綜合體,或者該稱為 Direct Graphics, 得先產生就對了。 pDev是用來通知3D 硬體畫三角形。pVB 是 Vertex Buffer,現在通知 pDev畫三角形,三角形的資訊一定要放在 Vertex Buffer 了。這次的 Vertex Buffer ,放的就是世界座標的三角形。
//----------------------------------------------------------------------------- // Global variables //----------------------------------------------------------------------------- LPDIRECT3D8 pD3D = NULL; // Used to create the D3DDevice LPDIRECT3DDEVICE8 pDev = NULL; // Our RenderD3Ding device LPDIRECT3DVERTEXBUFFER8 pVB = NULL;
3.WinMain與 Message Procedure 與上一個範例一樣,不再介紹了。
4.架構與上一個範例一樣,分為 InitD3D(), RenderD3D(), CloseD3D() 三大塊。先看看 InitD3D() 與 CloseD3D()。
- InitD3D() 前半段沒變,到 CreateDevice()之前都一樣。
- pDev(LPDIRECT3DDEVICE8) 初始化好了後,有三個跟投影有關的矩陣要通知它,分別是 World, View, Projection。任何一個世界座標的物件,一定是依序乘上 world, view, projection 這三個矩陣。
- 如果要畫的物件還需要位移或旋轉,那就放進World Matrix。如果都不要動了,那就要利用D3DXMatrixIdentity(),將 matWorld 設定成一個 4x4 的單位矩陣。世界座標乘單位矩陣,不會有任何變化。
- View 矩陣則代表攝影機的角度,利用 D3DX 提供的D3DXMatrixLookAtLH() 產生較方便 - 第二與第三個參數分別為 LookFrom 與 LookAt,在此範例為 (0, 50,-200) 與 ( 0,0,0 ), 表示這台攝影機放在 (0, 50, -200) 這個座標,鏡頭看著 (0,0,0) 。第四個參數為 Up Vector - (0,1,0) ,表示這個世界中,"上"的抽象概念為 (0,1,0),如果你不填 (0,1,0),整個世界就改觀了。
- Projection 矩陣則控制投影,利用 D3DX 提供的D3DXMatrixPerspectiveFovLH產生較方便 - 第二個參數為 FOV (Field of View) ,單位為徑度,在此範例為D3DX_PI/4,也就是 45 度的視角。第三個參數為長寬比例的調整,沒什麼意外的話,填 1.0f ,表示長寬的比例不變。第四個與第五個參數為 Z 的區間,不在這個區間的,不畫出來。
- 本範例還不預備用到光,端點資料裡面的色彩是手動給的,所以不需要 Normal 值。從 DirectX 7.0 開始,不帶有Normal 值的端點,因為無法算"光",導致色彩都變成黑色,所以要用 pDev->SetRenderState( D3DRS_LIGHTING, FALSE ); 將計算光源的部分關掉,通知D3D直接使用端點資料裡面的彩色值。
|
至於 CloseD3D(),就是單純地釋放 pD3D, pDev, pVB 那三個全域變數。 //-----------------------------------------------------------------------------
// Name: InitD3D()
// Desc: Initializes Direct3D
//-----------------------------------------------------------------------------
HRESULT InitD3D( HWND hWnd )
{
// Create the D3D object, which is needed to create the D3DDevice.
if( NULL == ( pD3D = Direct3DCreate8( D3D_SDK_VERSION ) ) )
return E_FAIL;
// Get the current desktop display mode
D3DDISPLAYMODE d3ddm;
if( FAILED( pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) )
return E_FAIL;
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = d3ddm.Format;
if( FAILED( pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &pDev ) ) )
{
return E_FAIL;
}
D3DXMATRIX matWorld;
D3DXMatrixIdentity( &matWorld );
pDev->SetTransform( D3DTS_WORLD, &matWorld );
D3DXMATRIX matView;
D3DXMatrixLookAtLH( &matView, &D3DXVECTOR3( 0.0f, 50.0f,-200.0f ),
&D3DXVECTOR3( 0.0f, 0.0f, 0.0f ),
&D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );
pDev->SetTransform( D3DTS_VIEW, &matView );
D3DXMATRIX matProj;
D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 500.0f );
pDev->SetTransform( D3DTS_PROJECTION, &matProj );
pDev->SetRenderState( D3DRS_LIGHTING, FALSE );
// Device state would normally be set here
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: CloseD3D()
// Desc: Releases all previously initialized objects
//-----------------------------------------------------------------------------
void CloseD3D(void)
{
if( pVB != NULL)
pVB->Release();
if( pDev != NULL)
pDev->Release();
if( pD3D != NULL)
pD3D->Release();
}
5.RenderD3D() 就是畫三角形的部分:
- 這次自行設計的Vertex 格式為 (x,y,z) 與 端點顏色,其中 x,y,z 預備放的是世界座標。在此範例中,三個點分別為 (0, 50, 0), (40, 0, 0), (-40, 0, 0),各位可以開始想像,一台放在(0, 50, -200)的攝影機,看到這片三角形會長什麼樣了。
- 既然是3D了,當然要做點跟 2D不一樣的事。在此範例,提供一個隨時間改變的旋轉矩陣放進 World Matrix ,對 Y 軸旋轉。所有的端點,Transform之前,都一定會乘上這個 World Matrix。如此表現出來的,就是一片不斷旋轉的三角形。讀者可以將這片三角形擴充為一個立方體,會更清楚。
| //-----------------------------------------------------------------------------
// Name: RenderD3D()
// Desc: Draws the scene
//-----------------------------------------------------------------------------
struct MYVERTEX
{
FLOAT x,y,z;
DWORD color;
};
#define NUM_VERT 3
struct MYVERTEX vert[NUM_VERT]=
{
0, 50, 0, 0x00FF0000,
40, 0, 0, 0x000000FF,
-40, 0, 0, 0x0000FF00
};
BOOL NeedCreateVBFlag=true;
float rad=0.0f;
void RenderD3D(void)
{
if( NULL == pDev )
return;
// Clear the backbuffer to a blue color
pDev->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
// Begin the scene
pDev->BeginScene();
//-----------------------------------------------------------------------------
// RenderD3Ding of scene objects can happen here
//-----------------------------------------------------------------------------
if(NeedCreateVBFlag)
{
pDev->CreateVertexBuffer(sizeof(MYVERTEX)*NUM_VERT,
D3DUSAGE_WRITEONLY,
D3DFVF_XYZ|D3DFVF_DIFFUSE,
D3DPOOL_DEFAULT,
&pVB);
NeedCreateVBFlag=false;
}
MYVERTEX *v;
pVB->Lock(0,0,(BYTE **)&v,0);
memcpy(v,vert,sizeof(MYVERTEX)*NUM_VERT);
pVB->Unlock();
D3DXMATRIX matWorld;
rad+= 0.001f;
D3DXMatrixRotationY(&matWorld, rad);
pDev->SetTransform( D3DTS_WORLD, &matWorld );
pDev->SetVertexShader(D3DFVF_XYZ|D3DFVF_DIFFUSE);
pDev->SetStreamSource(0,pVB,sizeof(MYVERTEX));
pDev->DrawPrimitive(D3DPT_TRIANGLELIST,0,NUM_VERT/3);
//-----------------------------------------------------------------------------
// End the scene
pDev->EndScene();
// Present the backbuffer contents to the display
pDev->Present( NULL, NULL, NULL, NULL );
}
結論:
這片三角形,不斷地對 Y軸旋轉,各位應該會發現,有一半的時間會不見,這是因為 SetRenderState() 當中, D3DRS_CULLMODE 預設值是 D3DCULL_CCW,也就是說三角形的三個點必須順時鐘才畫,如果三個點變成逆時鐘,就會被過濾(CULL)掉。這片三角形對著 Y軸轉了 180度後,三個點的排列就變成逆時鐘了,也因此被過濾掉了。如果讀者想要全部的時間都出現,可下一道 SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE); ,就會不管順時鐘、逆時鐘都畫。
不過排列為逆時鐘就過濾(CULL_CCW),是一個簡單又實用的加速技巧,比如說一個3D人物,是由 1000片三角形所組成,因此大約正面500片三角形,背面500片三角形,當那個人物面向攝影機時,背面500片三角形,其實畫了也沒用, 會被正面的三角形蓋掉,不畫反而能增加一倍的速度。 因此,CULL_CCW盡量是不要關掉。
如何編譯:
1.Visual C++ 的 Tools -> Options -> Directories 當中, include files 與 library files分別要將 c:\mssdk\include 與 c:\mssdk\library 優先順序放到最前面。


2.在 Visual C++ 當中 new 一個 Win32 Application的專案(切記,不是 Win32 Console Application),將 d3d8im_2.cpp 加入此專案的 source 區。
 3.Visual C++ 的 Projects -> Projects Setting -> Link -> Object/library modules 最後面加上 d3d8.lib d3dx8.lib 兩個程式庫。
 4.按下 F7 ,即可編譯完成。 | |