Wprowadzenie
Poniższy artykuł ma na celu przekonanie młodych programistów gier do DirectX oraz
ułatwienie życia tym, którzy zastanawiają się nad przesiadką z OpenGL'a. Przedstawię tutaj
parę podstawowych kwestii w programowaniu w DirectX 9 przy okazji pisania swojej
własnej kasy do symulacji trybu bezpośredniego znanego z OpenGL.
Implementacja klasy do obsługi buforów wierzchołków
Wydaje mi się, że każdy początkujący programista ma przerażenie w oczach, gdy zobaczy
operacje konieczne, aby w ogóle utworzyć bufor wierzchołków, a co dopiero coś narysować
na ekranie. Te operacje mogą się początkowo wydawać trudne i trochę niepotrzebne.
Operacje konieczne do zainicjalizowania bufora wierzchołków:
1. Inicjalizacja DirectX 9
2. Utworzenie bufora wierzchołków (g_d3d9->CreateVertexBuffer(& ))
3. Ustalenie formatu wierzchołków, które będą przechowywane w buforze
4. Zablokowanie bufora
5. Skopiowanie przygotowanych wierzchołków w odpowiednim formacie do bufora
6. Odblokowanie bufora
Teraz, aby jeszcze cos narysować za pomocą tego bufora konieczne są następujące operacje:
1. Ustawienie zródła skąd będą pobierane dane o wierzchołkach
2. Ustawienie formatu wierzchołków
3. Narysowanie wierzchołków (np. za pomocą g_d3d9->DrawPrimitive(& ))
I na koniec po zakończeniu pracy z buforem zwolnienie bufora wierzchołków.
Ufff& aż tyle operacji do zapamiętania dla początkującego w DirectX może być
zniechęcające. To nie to samo, co stare dobre glBegin i glEnd. Dlatego, aby uniknąć
ewentualnych błędów przy wykonywaniu tych samych czynności zamkniemy je w klasę.
Przykładowa implementacja (musimy wykorzystać szablony, aby nasza klasa była jak
najbardziej elastyczna):
// niezbędne nagłówki #include
#include // klasa do obsługi
"statycznego" bufora wierzchołków template class DXVertexBuffer { public: //
domyslny konstruktor DXVertexBuffer() {} // utworzymy również konstruktor mogący służyć
utworzenie bufora DXVertexBuffer(LPDIRECT3DDEVICE9 device, T* data, int count, UINT
format) { // sprawdzenie czy przypadkiem bufor już nie jest utworzony if(vb) Release();
uFormat = format; device->CreateVertexBuffer( count * sizeof(T), 0, format,
D3DPOOL_DEFAULT, &vb, NULL); VOID* pVertices; vb->Lock( 0, 0, (void**)&pVertices, 0 )
; memcpy( pVertices, data, sizeof(T) * count); vb->Unlock(); } // domyślny destruktor, jeśli
przypadkiem zapomnieliśmy zwolnić // bufora to zwolni go za nas ~DXVertexBuffer() { if(vb)
Release(); } // tworzy bufor void Create(LPDIRECT3DDEVICE9 device, T* data, int count,
UINT format) { // sprawdzenie czy przypadkiem bufor już nie jest utworzony // jeśli tak to
robimy to jeszcze raz(zabezpieczenie& ) if(vb) Release(); uFormat = format; device-
>CreateVertexBuffer( count * sizeof(T), 0, format, D3DPOOL_DEFAULT, &vb, NULL); VOID*
pVertices; vb->Lock( 0, 0, (void**)&pVertices, 0 ) ; memcpy( pVertices, data, sizeof(T) *
count); vb->Unlock(); } // renderuje bufor void Render(LPDIRECT3DDEVICE9 device,
D3DPRIMITIVETYPE type, int count) { if(!vb) return; device->SetStreamSource( 0, vb, 0,
sizeof(T) ); device ->SetFVF( uFormat ); device ->DrawPrimitive( type, 0, count); } //
funkcja dostępu do bufora wierzchołków, daje możliwość dynamicznej // zmiany
wierzchołków w buforze LPDIRECT3DVERTEXBUFFER9 GetVertexBuffer() { return vb; } //
zwalnia bufor wierzchołków void Release() { if(vb) vb->Release(); } private:
LPDIRECT3DVERTEXBUFFER9 vb; // bufor wierzcholków UINT uFormat; // format
wierzcholków };
Kod wydaje mi się, że jest dość przejrzysty. Jeśli czegoś nie rozumiesz polecam zerknąć do
odpowiedniej książki o DirectX lub do SDK. Wadą powyższego kodu jest to, że prawie w
każdej funkcji konieczne jest przekazywanie wskaznika do urządzenia DirectX. Można to
rozwiązać poprzez utworzenie globalnego obiektu DirectX.
Teraz czas na przedstawienie wykorzystanie tej klasy w praktyce(na podstawie kodu z SDK):
// obiekt urządzenia DirectX 9 LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; // struktura
przykładowych wierzchołków struct CUSTOMVERTEX { FLOAT x, y, z, rhw; DWORD color; };
// nasz format wierzcholkow #define D3DFVF_CUSTOMVERTEX
(D3DFVF_XYZRHW|D3DFVF_DIFFUSE) DXVertexBuffer< CUSTOMVERTEX > g_VertexBuffer;
// domyslny konstruktor // inicjalizacja bufora gdzieś w jakiejś funkcji.... CUSTOMVERTEX
g_Vertices[] = // przykładowe wierzchołki { { 150.0f, 50.0f, 500.5f, 1.0f, 0x00ff0000, }, {
250.0f, 250.0f, 500.5f, 1.0f, 0x0000ff00, }, { 50.0f, 250.0f, 500.5f, 1.0f, 0x0000ffff, }, };
g_VertexBuffer->Create(pd3dDevice, g_Vertices, 3, D3DFVF_CUSTOMVERTEX); // i teraz
użycie w funkcji renderujacej.... g_VertexBuffer->Render(g_pd3dDevice,
D3DPT_TRIANGLELIST, 1); // i na koniec zwolnienie bufora g_VertexBuffer->Release();
Jak widać użycie naszej klasy znacznie uprościło nam sprawę ;)
Implementacja klasy symulującej glBegin i glEnd
Naszym celem będzie stworzenie odpowiedników tych funkcji w DirectX 9. Na początek
przypomnienie dla tych, którzy nie pamiętają jak te funkcje działają:
glBegin(typ_wielokatow);
typ_wielokatow może być np.: GL_TRIANGLES, GL_LINES, GL_QUADS,
GL_TRIANGLESTRIP... itp. Typ wielokątów tak naprawdę określa, w jaki sposób maja być
interpretowane wierzchołki znajdujące się w bloku glBegin - glEnd. glEnd() koniec
podawania wierzchołków Pomiędzy tymi funkcjami umieszcza się instrukcje, np. glVertex2f,
glColor3f& itp. Określające poszczególne parametry wierzchołków.
Dla tych, którzy nie rozumieją, co oznaczają poszczególne operacje odsyłam do artykułów
wyjaśniających podstawy operacji graficznych w OpenGL'u lub jeśli Cię to API nie interesuje
możesz je po prostu zignorować (nie powinno ci to przeszkodzić w zrozumienie pozostałej
części artykułu).
Zacznijmy od przedstawienie naszej implementacji w DirectX:
// klasa do obsługi dynamicznego bufora wierzchołków template class
DXDirectMode { public: // konstruktor i destruktor DXDirectMode() : vertex_count(0) {}
~DXDirectMode() {} // inicjalizacja dynamicznego bufora wierzchołków void
Init(LPDIRECT3DDEVICE9 _device, UINT format, int max_vertex = 32) { device = _device;
max_count = max_vertex; uFormat = format; data = new T[max_vertex]; device -
>CreateVertexBuffer(max_count * sizeof(T), 0, uFormat, D3DPOOL_DEFAULT, &vb, NULL );
} // zwalnia dane bufora wierzchołków void Release() { if(data) delete []data; vb-
>Release(); } // początek określania listy wierzchołków void Begin(D3DPRIMITIVETYPE
_type) { type = _type; } // wstawienie pojedynczego wierzchołka void Vertex(const T&
vertex) { data[vertex_count] = vertex; ++vertex_count; } // koniec listy wierzchołków,
rysowanie, trzeba niestety podać // ilość wielokątów do wyswietlenia(rzecz która można
poprawić) void End(int primitive_count) { // kopiujemy wierzcholki do bufora VOID*
pVertices; vb->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD) ; memcpy( pVertices,
data, sizeof(T) * vertex_count); vb->Unlock(); device ->SetStreamSource(0, vb, 0,
sizeof(T)); device ->SetFVF(uFormat); device ->DrawPrimitive(type, 0, primitive_count); //
zerujemy ilosc wierzchokow do narysowania vertex_count = 0; } private: int max_count; //
maksymalna liczba wierzcholkow int vertex_count; // obecna liczba wierzchołków T* data; //
dane wierzcholkow LPDIRECT3DDEVICE9 device; // urządzenie D3D, można usunąć
singletona LPDIRECT3DVERTEXBUFFER9 vb; // bufor wierzchołków D3DPRIMITIVETYPE
type; // typ wyświetlania wierzchołków UINT uFormat; // format wierzchołków };
Powyższa klasa jest dość prosta i z pewnością ma wiele niedoskonałości. Chociaż osobiście
nie widzę zbyt wielu możliwości na jej rozbudowę. Jej implementacja stawia jednak pewne
wymaganie odnośnie struktury przechowującej wierzchołki:
1. Klasa reprezentująca wierzchołek musi mieć publiczny konstruktor domyślny
2. Dla wygody przydałby się również konstruktor do inicjalizacji wszystkich składowych
klasy jakimiś wartościami
Dlaczego, jest tak a nie inaczej postaram się przedstawić na poniższym przykładzie:
// to ten sam przykład co powyżej tylko z małymi zmianami LPDIRECT3DDEVICE8
g_pd3dDevice = NULL; // struktura przykładowych wierzchołków struct CUSTOMVERTEX {
FLOAT x, y, z, rhw; DWORD color; // konstruktor domyślny CUSTOMVERTEX() {} //
konstrukor do inicjalizacji wartosciami wierzchołka CUSTOMVERTEX(FLOAT _x, FLOAT _y,
FLOAT _z, FLOAT _rhw, DWORD color) : x(_x), y(_y), z(_z), rhw(_rhw), color(_color) {} };
// nasz format wierzcholkow #define D3DFVF_CUSTOMVERTEX
(D3DFVF_XYZRHW|D3DFVF_DIFFUSE) DXDirectMode g_DirectMode; //
podczas inicjalizacji programu, korzystamy z domyślnej maksymalnej // ilości wierzchołków
bloku begin - end g_DirectMode.Init(g_pd3dDevice, D3DFVF_CUSTOMVERTEX); // w tym
miejscu rysujemy nasza scenę // do wykorzystanie tej klasy polecam napisać makro #define
VERTEX(x, y, z, rhw, color) g_DirectMode->Vertex(CUSTOMVERTEX(x, y, z, rhw, color)); // i
teraz podajemy wierzchołki g_DirectMode.Begin(); VERTEX(150.0f, 50.0f, 500.5f, 1.0f,
0x00ff0000); VERTEX(250.0f, 250.0f, 500.5f, 1.0f, 0x0000ff00); VERTEX( 50.0f, 250.0f,
500.5f, 1.0f, 0x0000ffff); // podajemy ilos wielokątów do narysowania g_DirectMode.End(1);
// i na koniec g_DirectMode.Release();
Powyższy przykład spowoduje narysowanie na ekranie takiego samego trójkącika jak ten
narysowany powyżej z zwykłego bufora wierzchołków.
Od Autora
Mam nadzieję, że powyższy artykuł okaże się dla kogoś przydatny. Wszelkie pytania i uwagi
proszę kierować na adres: gosuwachu@o2.pl
Wyszukiwarka
Podobne podstrony:
MSP430 Directives
director
Active Directory omówienie domyślnych jednostek organizacyjnych
barcelona 6 directory v1 m56577569830521452
Bufory
directorypaths
direct
LEIBNIZ DIRECTIONES wersja robocza XII 2012
Domena i Active Directory
WIERZCHOWSKA
Direct3D Wstep
directo3
Microsoft DirectX 10 Technical Brief
bufory
Active Directory
directions
SHSpec 189 6209C18 Directing PC s Attention
Direct3D Tekstury
więcej podobnych podstron