DirectX9 - 2. návod - Vertex buffer
DirectX9 - 2. návod - Vertex buffer
2. Vertex buffer
Cíl
Příprava
InitD3D - Vytvoření vykreslovacího zařízení
InitVB - Vytvoření a naplnění Vertex Bufferu
Cleanup - Uvolnění alokovaných proměnných
Render - Funkce pro vykreslení
MsgProc - Zachytávání a zpracování zpráv okna
WinMain - Vstupní bod naší aplikace
Poznámka na závěr
Příklad je zde ke staženíCíl
V tomto tutoriálu si už konečně vykreslíme nějaké body. Dozvíte se zde základy konceptu vertex bufferu, Direct3D objektu sloužícího k uchování vertexů. Ty mohou být definovány jakkoliv chceme vytvořením uživatelké struktury a k ní odpovídajícího FVF (flexibylního vertex-formátu). V tomto příkladu používáme vertexy, které jsou již promítnuty (jsou již v 2D rozměrech okna, nikoliv v 3D souřadnicích) a osvětleny (nepoužíváme Direct3D osvětlování, ale nastavíme napevno naše vlastní barvy)
nahoru
Příprava
Založíme si projekt (viz. Nultý tutorial). A můžeme začít psát zdrojový text... Nejdříve naincludujeme potřebnou DirectX hlavičku, v našem případě tedy napíšeme#include <d3d9.h>Nyní si připravíme globální proměnné, které budeme potřebovat. Oproti minulému tutoriálu přibude náš Vertex buffer, dále strukturu pro náš uživatelský vertex (v D3D ho můžeme definovat podle naší vůle, může obsahovat prvky jako pozici, normálu, barvu, atd.) a přidáme ještě FVF (flexibilní vertex-formát), který D3D řekne jak má na náš vertex nahlížet.
LPDIRECT3D9 g_pD3D = NULL; // Potřeba k vytvoření D3DDevice
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; // Samotné vykreslovací zařízení
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; // Buffer k uchování vertexů
// Struktura pro náš uživatelský vertex
struct CUSTOMVERTEX
{
FLOAT x, y, z, rhw; // Transformovaná pozice pro vertex
DWORD color; // A jeho barva
};
// Naše uživatelský FVF (flexibilní vertex-formát), popisující naši uživatelsky
// definovanou strukturu CUSTOMVERTEX
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
Nyní si vytvoříme funkci InitD3D, ve které provedeme všechny přípravy našeho
vykreslovacího zařízení (Direct3D Device)
nahoru
InitD3D - Vytvoření vykreslovacího zařízení
HRESULT InitD3D( HWND hWnd )Tato funkce byla detailně popsána v 1. návodu a i v tomto použijeme shodné konstrukce, proto se jí zde už nebudu příliž věnovat a pouze uvedu celý její zdrojový text.
// Vytvořímee D3D objekt
if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
return E_FAIL;
// Nastavíme strukturu použitou pro vytvoření D3DDevice
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
// Vytvoříme D3DDevice (vykreslovací zařízení)
if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_pd3dDevice ) ) )
{
return E_FAIL;
}
// Zde se obvykle zapisuje počáteční nastavení D3DDevice
return S_OK;
nahoru
InitVB - Vytvoření a naplnění Vertex Bufferu
HRESULT InitVB()V této funkci si vytvoříme Vertex buffer a naplníme ho našimi vertexy. Vertex buffer je v podstatě pouze kus paměti, kde jsou uloženy informace o vertexech. Po jeho vytvoření ho musíme Lock() - uzamknout (získáme jeho ukazatel do paměti a v některých případech se při tom také přesune z grafické karty do paměti) a na konci opět Unlock() - odemknout (zajistí i příp. navrácení zpět do GK). Jedině získáním ukazatele do paměti můžeme totiž obsah tohoto bufferu měnit. Pro indexy používá D3D také Index buffer. To si ale ukážeme v některém z dalších tutoriálů. Jak už jsem zmínil, oba buffery mohou vznikat přímo v paměti grafické karty. Tím umožníme hardwarové zpracování, což vede k velkému nárůstu výkonu.
Nejprve si tedy vytvoříme tři vertexy. To by nám mělo stačit k vykreslení našeho prvního polygonu.
CUSTOMVERTEX vertices[] =
{
{ 150.0f, 50.0f, 0.5f, 1.0f, 0xffff0000, }, // x, y, z, rhw, barva
{ 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00, },
{ 50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff, },
};
Teď si vytvoříme náš vertex buffer. Zde se alokuje dostatek paměti
(z výchozího prostoru) abychom mohli uchovat 3 vertexy. Také
specifikujeme naše uživatelské FVF, takže vertex buffer bude vědět
jakou strukturu dat do něho vkládáme.
if( FAILED( g_pd3dDevice->CreateVertexBuffer( 3*sizeof(CUSTOMVERTEX),
0, D3DFVF_CUSTOMVERTEX,
D3DPOOL_DEFAULT, &g_pVB,
NULL ) ) )
{
return E_FAIL;
}
Nyní vertex buffer naplníme. Jak už jsme si řekli, nejprve ho musíme
uzamknout v paměti, abychom k němu získali přístup. Opět připomínám
že je to z důvodu, že vertex buffer může ležet v paměti grafické karty.
void* pVertices;
if( FAILED( g_pVB->Lock( 0, sizeof(vertices), (void**)&pVertices, 0 ) ) )
return E_FAIL;
memcpy( pVertices, vertices, sizeof(vertices) );
g_pVB->Unlock();
return S_OK;
nahoru
Cleanup - Uvolnění alokovaných proměnných
VOID Cleanup()I tato funkce byla podrobněji nastíněna v 1. příkladu. Jedinou změnou zde je nutnost krom d3dDevice (vykreslovacího zařízení) a D3D (Instance Direct3D) uvolnit také vertex buffer.
if( g_pVB != NULL )
g_pVB->Release();
if( g_pd3dDevice != NULL )
g_pd3dDevice->Release();
if( g_pD3D != NULL )
g_pD3D->Release();
nahoru
Render - Funkce pro vykreslení
VOID Render()Již známá funkce starající se o vykreslení scény. Tentokrát ale bude konečně něco skutečně vykreslovat. Nejprve si stejně jako v minulém příkladu vyčistíme Back buffer a zahájíme vykreslování
// Vyčistíme back buffer modrou barvou
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET,
D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
// Zahájíme vykreslování
if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
{
Zde vykreslíme trojúhelníky ve vertex bufferu.
To je rozděleno do několika
kroků. Nejprve nastavíme "proud" vertexů, tedy náš
vertex buffer spolu
s informací po jakém kroku se v něm budeme pohybovat. Druhým krokem je
říci D3D jaký vertex shader má použít. Plně uživatelské
vertex shadery
budou tématem nějakého velmi pokročilého tutoriálu. Ve většině (i v tom
našem případu) stačí prostě jen říci FVF, tedy formát jak má D3D s naším
vertexem nakládat. Nakonec zavoláme DrawPrimitive(), funkci, která již
provede samo vykreslení (v našem případě tedy vykreslí jediný polygon)
g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) );
g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 );
Nakonec ukončíme vykreslování a zobrazíme vše co jsme vykreslili (tedy obsah Back bufferu)
na obrazovku
// Ukončíme vykreslování
g_pd3dDevice->EndScene();
}
// Zobrazíme obsah backbufferu na obrazovku
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
nahoru
MsgProc - Zachytávání a zpracování zpráv okna
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )Opět dvě funkce na závěr, které ani tak pro DirectX slouží spíše pro režii Windows.
Trochu jsme si je nastínili opět již v minulém příkladu. Rozdílem zde ale je způsob zavolání překreslení naší scény. V minulém příkladu se scéna překreslila jen na vyžádání o překreslení okna. V reálných případech je ale třeba scénu překreslovat stále, nebo alespoň v krátkých časových intervalech. Jistě nechceme, aby jsme ve hře zjistili novou situaci jen například minimalizací a maximalizací okna:) Proto namísto zavolání funkce Render z této funkce na událost PAINT ji budeme volat v cyklu v druhé funkci WinMain
switch( msg )
{
case WM_DESTROY:
Cleanup();
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc( hWnd, msg, wParam, lParam );
nahoru
WinMain - Vstupní bod pro naši aplikaci
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )Hlavní funkce, po spuštění se volá právě ona.
Hned na začátku si vytvoříme a zaregistrujeme třídu našeho okna a poté si okno vytvoříme.
// Registrace třídy okna
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
"Dx9 Tutorial 2", NULL };
RegisterClassEx( &wc );
// Vytvoření okna aplikace
HWND hWnd = CreateWindow( "Dx9 Tutorial 2", "Dx9 2 - Vertex buffer",
WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
NULL, NULL, wc.hInstance, NULL );
Inicializujeme si Direct3D a pokud vše proběhne jak má, vytvoříme a naplníme
náš Vertex buffer. Pokud
i to proběhlo bez problémů, tak zobrazíme okno aplikace.
// Inicializace Direct3D
if( SUCCEEDED( InitD3D( hWnd ) ) )
{
// Vytvoření vertex bufferu
if( SUCCEEDED( InitVB() ) )
{
// Zobrazení okna
ShowWindow( hWnd, SW_SHOWDEFAULT );
UpdateWindow( hWnd );
Nyní vstoupíme do smyčky zpracování zpráv. Zde je onen rozdíl oproti minulému příkladu, kdy nově vykreslujeme
scénu stále dokola dokud neni třeba zpracovat nějakou zprávu (stisk klávesy, ukončení aplikace atd.). V 1. příkladu
jsme volali funkci GetMessage(), která ale pozastaví běh programu dokud nepřijde nějaká zpráva.
Takové zamrznutí do např. stisku kláves se nám ale v DirectX aplikacích příliš nehodí a proto použijeme PeekMessage, která jen
zjistí, zda je třeba nějakou zprávu zpracovat a jinak nechá program pokračovat.
// Vstoupíme do smyčky zpracování zpráv
MSG msg;
ZeroMemory( &msg, sizeof(msg) );
while( msg.message!=WM_QUIT )
{
if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
Render();
}
Nakonec opět odregistrujeme třídu našeho windows okna a ukončíme naši aplikaci.
}
}
UnregisterClass( "Dx9 Tutorial 2", wc.hInstance );
return 0;
nahoru
Poznámka na závěr
Tento text je téměř přesným opisem výsledného zdrojového textu. Vynechány byly částečně komentáře, které nahrazuje tento text. Zdrojový text na konci této stránky ale ony komentáře obsahuje a umožní Vám to lepší orientaci i bez stálého připojení na tyto stránky.Dále byly vynechány složené závorky jednotlivých těl funkcí.
nahoru
Příklad je zde ke stažení
- Příklad #2 Src - Zdrojové texty s projektem pro Microsoft Visual Studio 2005
- Příklad #2 Exe - Spustitelný, již zkompilovaný program
nahoru
| Jan Zelený | 21.3.2007 |
© 2008 Jan Zelený | monade.cz





