DirectX: Labirynt 3D od podstaw

15.03.2010 - Adam Błaszkiewicz
TrudnośćTrudność

Czas na Direct3D!

Dotychczasowy projekt można pobrać z ramki po lewej.

Teraz, zamiast tekstu, chcielibyśmy wyświetlić w naszym oknie scenę Direct3D. W tym celu musimy utworzyć urządzenie Direct3D, za pomocą którego będziemy wysyłali do karty graficznej scenę, którą będziemy chcieli wyrenderować.

Dołączmy odpowiedni plik nagłówkowy:

1
#include <d3d9.h>

DirectX jest oparty na modelu COM (Component Object Model). COM to binarny standard opisu obiektów, jest więc niezależny od języka programowania. Pozwala na odwoływanie się do obiektów za pomocą interfejsów i zapewnia współdzielenie obiektów w taki sposób, że są one automatycznie niszczone po zwolnieniu ostatniego interfejsu, czyli wtedy, gdy obiekt nie jest już używany. Dla uproszczenia, wskaźniki do interfejsów DirectX zadeklarujemy jako zmienne globalne:

1
2
IDirect3D9              *pD3D = 0;
IDirect3DDevice9        *pD3DDevice = 0;

Aby utworzyć D3DDevice, musimy użyć obiektu D3D, który musimy utworzyć wcześniej. Potrzebujemy też struktury D3DPRESENT_PARAMETERS oraz uchwytu do okna, więc musimy mieć to okno wcześniej utworzone. Najlepiej napisać poniższy kod tuż przed ShowWindow, aby wyświetlić już gotowe okno z gotowym do pracy Direct3D. Zauważ, że używamy tutaj bardzo nieeleganckiej instrukcji goto. Robimy to, aby przed wyjściem z programu zwolnić odpowiednie obiekty. W prawdziwym programie użylibyśmy klas w stylu RAII, ale wtedy kolejność wykonywania instrukcji w programie mogłaby nie być oczywista. Moglibyśmy też załatwić ten problem poprzez zagnieżdżanie bloków if, ale byłoby to koszmarnie nieczytelne. Dlatego na potrzeby tego tutoriala, w celu dydaktycznym, użyjemy instrukcji goto, która przenosi wykonywanie programu do momentu zwalniania pamięci w funkcji WinMain.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pD3D = Direct3DCreate9( D3D_SDK_VERSION );
if(!pD3D) goto WinMain_end;
 
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof( d3dpp ) );
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
 
HRESULT hr;
hr = g_pD3D->CreateDevice(
    D3DADAPTER_DEFAULT,
    D3DDEVTYPE_HAL,
    hWnd,
    D3DCREATE_HARDWARE_VERTEXPROCESSING,
    &d3dpp,
    &pD3DDevice );
if(FAILED(hr)) goto WinMain_end;

Wersja z komentarzami:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// Uzyskujemy interfejs D3D
pD3D = Direct3DCreate9( D3D_SDK_VERSION );
 
// Jeśli funkcja zwróciła pusty wskaźnik,
// operacja nie powiodła się.
// Możemy jakoś obsłużyć błąd lub wyjść z WinMain:
if(!pD3D) goto WinMain_end;
 
// Struktura zawierająca informacje o formacie obrazu
D3DPRESENT_PARAMETERS d3dpp;
 
// Ustawimy wszystkie pola struktury na 0,
// aby potem wybrać tylko te, które nas interesują:
ZeroMemory( &d3dpp, sizeof( d3dpp ) );
 
// Tryb wyświetlania w oknie (nie na pełnym ekranie):
d3dpp.Windowed = TRUE;
 
// Sposób wyświetlania kolejnych buforów ekranu:
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
 
// D3DFMT_UNKNOWN wybiera taki sam format, jak ten
// aktualnie używany przez Windows:
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
 
 
HRESULT hr;
 
// Tworzymy D3DDevice:
hr = pD3D->CreateDevice(
 
    // Wybór urządzenia (zwykle po prostu
    // karty graficznej):
    D3DADAPTER_DEFAULT,
 
    // Tryb akceleracji sprzętowej
    // (przez kartę graficzną):
    D3DDEVTYPE_HAL,
 
    // Tu podajemy uchwyt do naszego okna
    hWnd,
 
    // Sposób przetwarzania wierzchołków,
    // wybieramy przetwarzanie sprzętowe
    // jako preferowane:
    D3DCREATE_HARDWARE_VERTEXPROCESSING,
 
    // Adres do struktury
    // D3DPRESENT_PARAMETERS
    &d3dpp,
 
    // Adres wskaźnika do IDirect3DDevice,
    // wskaźnik ten zostanie ustawiony
    // na odpowiedni obiekt:
    &pD3DDevice );
if(FAILED(hr)) goto WinMain_end;

HRESULT jest typem kodującym wynik wywołania funkcji - kod błędu lub "kod sukcesu". Jest to liczba 32-bitowa. Domyślny błąd to E_FAIL, a domyślny sukces to S_OK. Zdefiniowane są następujące makra:

SUCCEDED(hr) - TRUE (1), gdy hr jest kodem sukcesu, FALSE (0) w przeciwnym wypadku.

FAILED(hr) - TRUE (1), gdy hr jest kodem błędu, FALSE (0) w przeciwnym wypadku.

Powyższe makra są zdefiniowane w plikach nagłówkowych Windowsa, nie DirectX! Wartości typu HRESULT są także używane w COM.

Zajmijmy się teraz zwalnianiem pamięci na końcu WinMain:

1
2
3
4
5
6
7
8
9
10
11
12
if(0)
{
    WinMain_end:
    msg.wParam = 1;
}
 
if( pD3DDevice ) pD3DDevice->Release();
if( pD3D ) pD3D->Release();
 
UnregisterClass( _T("LabiryntWndClass"), hInstance );
 
return msg.wParam;

Wersja z komentarzami:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Do tego bloku można wejść tylko poprzez instrukcję goto:
if(0)
{
    // Etykieta dla goto:
    WinMain_end:
 
    // Jeśli doszliśmy do momentu
    // zwalniania pamięci przez goto,
    // ustawiamy msg.wParam na wartość różną od 0.
    // Wartość ta będzie zwrócona przez WinMain,
    // aby zasygnalizować błąd:
    msg.wParam = 1;
}
 
// Zwalniamy interfejsy Direct3D:
if( pD3DDevice ) pD3DDevice->Release();
if( pD3D ) pD3D->Release();
 
// Wyrejestrowanie klasy okna:
UnregisterClass( _T("LabiryntWndClass"), hInstance );
 
// Zwracamy wartość komunikatu WM_QUIT lub 1,
// jeśli wykonane zostało goto:
return msg.wParam;

Jeśli w wyniku niepowodzenia zostanie użyta instrukcja goto, WinMain zwraca 1:

(8.png)

Musimy jeszcze wyrenderować naszą pierwszą, pustą scenę. Możemy to zrobić w obsłudze komunikatu WM_PAINT. Zastąpmy więc kod, który tam napisaliśmy, tym:

1
2
3
4
5
6
7
case WM_PAINT:
    ValidateRect( hWnd, NULL );
                        
    pD3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET,
        D3DCOLOR_XRGB( 128, 100, 50 ), 1.0f, 0 );
    pD3DDevice->Present( NULL, NULL, NULL, NULL );
    return 0;

Funkcji ValidateRect używamy, aby oznaczyć cały obszar okna jako odmalowany i zapobiec zapętlonemu wysyłaniu komunikatu WM_PAINT przez system. Clear używamy, aby zamalować scenę na wybrany kolor - D3DCOLOR_XRGB( 128, 100, 50 ). Present wyświetla scenę. Możemy teraz skompilować i uruchomić nasz program. Aktualny projekt można pobrać z ramki po lewej.

Ćwiczenie dla czytelnika: sprawdzić, co się stanie, jeśli wywołamy ShowWindow od razu po utworzeniu okna, a dopiero potem utworzymy Direct3D.

4.666665
Twoja ocena: Brak Ocena: 4.7 (6 ocen)

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com