WARP 2.0 Digital 04.pdf

(9027 KB) Pobierz
Cover W04 - W2D.indd
DirectX Visual C++ 3ds max OpenGL
NR 04 (14) MARZEC 2006
56017949.012.png
WARP 2.0 Digital 04 03/2006
„Jeżeli jakiś technologiczny cud jest
możliwy, człowiek go dokona.
Tak, jakby leżało to w jego naturze…”
major Kusanagi
Ghost in the Shell
Witajcie!
w numerze:
WARP 2.0 Digital
Nr 04 (14) marzec 2006
Oto co zawiera
czwarty numer
cyfrowego
wydania WARP 2.0 Digital. Już w zasadzie
standardowo jest to sześć zróżnicowanych
artykułów. Wszyscy zainteresowani
programowaniem znajdą tym razem same
kontynuacje działów z poprzednich numerów.
Numer otwiera Direct3D, następnie kolejna
porcja informacji na temat STL i dalsza część
kursu programowania C++. Dla entuzjastów
przenośnych konsol zamieściliśmy kolejny
artykuł na temat programowania GameBoy
Advance.
03
Direct 3D
Zespół BCT
Piotr Besta (matsuoka)
Daniel Besta (rabban)
Marcin Chaszczewski (dark soul)
Adam Sawicki (regedit)
Karol Kuczmarski (xion)
Adres redakcji:
ul. Niepodległości 1c/1
66-440 Skwierzyna
12
STL - Standard Template Library
e-mail:
blackcatwarp@o2.pl
Internet:
www.warp.arx.pl
Z kolei dla grafików (i nie tylko) trafia się
niezła gratka. W numerze zamieszczamy
wywiad z Eoin’em Colgan’em, artystą
pracującym obecnie w Electronic Arts.
Oprócz tego trzecia część warsztatu w którym
pracujemy nad troopersem - tym razem
tworzymy teksturę. Pozdrawiamy!
20
C++ Zone
Redakcja nie odpowiada za treść ogłoszeń
i reklam. Materiałów nie zamówionych nie
zwracamy. W przypadku publikacji, zastrzegamy
sobie prawo do skrótów. Wszystkie użyte znaki
firmowe i towarowe są zastrzeżone przez ich
właścicieli i zostały użyte wyłącznie w celach
informacyjnych.
redakcja warp
30
GameBoy Advance
Wszelkie prawa zastrzeżone. Żaden fragment
niniejszej publikacji nie może być w całości lub
we fragmentach kopiowany i powielany bez
zgody wydawcy.
38 The Art of Eoin Colgan
Redakcja dołożyła wszelkich starań, aby
zawarte w czasopiśmie informacje były
rzetelne i kompletne. Nie bierze jednak żadnej
odpowiedzialności za ich wykorzystanie, ani
za związane z nimi ewentualne naruszenia
praw patentowych czy autorskich. Autorzy nie
ponoszą także żadnej odpowiedzialności za
potencjalne szkody wynikłe z wykorzystania
informacji zawartych w niniejszym czasopiśmie.
okładka WARP 2.0 Digital 04
Dr. No Soldier L1, autor: Eoin Colgan
GoldenEye: Rogue Agent, © Electronic Arts
43 Trooper - malowanie tekstury
▲Ghost in the Shell (1995), © Manga Entertainment Inc.
02
WARP 2.0 Digital 04 (14) 03/2006
56017949.013.png 56017949.014.png 56017949.015.png 56017949.001.png 56017949.002.png
Direct3D
N ajważniejszym obiektem w Direct3D jest urządzenie. To w nim są metody, które
pozwalają na zmianę różnych ustawień oraz rysowanie, jak również na tworzenie
innych potrzebnych obiektów. Tworząc urządzenie wybieramy tryb grafi czny, w jakim
będzie pracować nasza gra. Kiedy już mamy ten obiekt, możemy rozpocząć działanie
pętli powtarzając cykl obliczania i rysowania.
niepowodzenia
– makra
FAILED(hr). Jeśli
chciałbyś zrealizować
obsługę błędów w swoim
kodzie również poprzez zwracanie
przez funkcje wartości tego typu, do
sygnalizowania powodzenia zwracaj wartość
S_OK, a błędu – E_FAIL.
W itaj w drugiej części mojego kursu Direct3D.
Miesiąc temu rozpoczęliśmy omawianie
budowy szkieletu (ang. framework ) gry.
Większość czasu zajęła nam jednak nauka
Windows API konieczna, aby utworzyć puste
okno. Teraz, kiedy już mamy je utworzone,
możemy rozpocząć naukę samego Direct3D.
W tym numerze zobaczymy, jak utworzyć tzw.
urządzenie oraz jak działa gra nieustannie
przeliczając i odrysowując nowe klatki w pętli.
odrysować się na ekranie.
powodzenie lub niepowodzenie operacji,
którą funkcja miała wykonać. Do testowania
powodzenia takiej wartości możesz używać
makra SUCCEEDED(hr), a do testowania
Spójrz na listing 1a . Zadaniem
funkcji CreateDevice jest stworzenie
obiektu urządzenia, a zadaniem funkcji
DestroyDevice – jego usunięcie. Obiekt ten
przechowywany jest w zmiennej m_pDev
typu LPDIRECT3DDEVICE9 (lub inaczej
IDirect3DDevice9*). Utworzenia dokonuje
metoda CreateDevice wywoływana z obiektu
m_pD3D, który utworzyliśmy w pierwszej
części kursu.
Tak więc wszystkie funkcje składowe
obiektów DirectX będą zwracały wartość typu
Listing D3D-1.1a Tworzenie i usuwanie urządzenia.
01: void CFramework::CreateDevice ()
02: {
03: D3DPRESENT_PARAMETERS PresentParams;
04: GeneratePresentParams ( &PresentParams ) ;
05:
06: HRESULT hr;
07:
08: hr = m_pD3D->CreateDevice ( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hWnd,
09: D3DCREATE_HARDWARE_VERTEXPROCESSING, &PresentParams, &m_pDev ) ;
10: if ( SUCCEEDED ( hr )) return ;
11: hr = m_pD3D->CreateDevice ( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hWnd,
12: D3DCREATE_MIXED_VERTEXPROCESSING, &PresentParams, &m_pDev ) ;
13: if ( SUCCEEDED ( hr )) return ;
14: hr = m_pD3D->CreateDevice ( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hWnd,
15: D3DCREATE_SOFTWARE_VERTEXPROCESSING, &PresentParams, &m_pDev ) ;
16: if ( SUCCEEDED ( hr )) return ;
17: } // end CreateDevice
18:
19: void CFramework::DestroyDevice ()
20: {
21: m_pDev->Release () ;
22: m_pDev = 0;
23: } // end DestroyDevice
Urządzenie
Mamy stworzone okno Windows.
Możemy utworzyć w nim obiekt
zwany urządzeniem (ang. device ). To
najważniejszy obiekt w całym Direct3D.
Będzie nam potrzebny do wykonywania
praktycznie wszystkich operacji grafi cznych i
dostęp do niego będzie potrzebny każdemu
obiektowi naszej gry, który będzie potrafi ł
Urządzenie
Metoda ta zwraca wartość typu HRESULT.
Podobnie zresztą, jak wszystkie metody
wszystkich obiektów Direct3D. Taka jest
bowiem zasada w całej technologii COM,
a biblioteka DirectX łączy się z kodem
w C++ właśnie za jej pomocą. Typ ten
jest po prostu pewną liczbą oznaczającą
03
WARP 2.0 Digital 04 (14) 03/2006
56017949.003.png 56017949.004.png 56017949.005.png
 
Direct3D
HRESULT do sygnalizowania błędu. Jeśli
funkcja ma zwrócić jakąś wartość – tak jak
funkcja CreateDevice ma zwrócić wskaźnik
do utworzonego urządzenia – musi to zrobić
poprzez jeden ze swoich parametrów. Takie
zwrócenie wartości przez parametr nazywane
jest parametrem wyjściowym i realizowane
jest za pomocą przekazania parametru przez
wskaźnik. Ta technika powinna być Ci znana
z nauki samego programowania w języku
C++. Zwróć uwagę, że w tym konkretnym
przykładzie funkcja ma zwrócić wskaźnik.
Jako ostatni parametr, CreateDevice oczekuje
więc wskaźnika do wskaźnika – wartości typu
IDirect3DDevice9** .
dokładnym omówieniem ich wszystkich.
właśnie umożliwiał zmianę ustawień podczas
działania. Szczegóły poznamy później.
nieprzyjemnym i niezdrowym ustawieniem
najniższym – 60 Hz. Tymczasem najnowsze
normy mówią, że ergonomiczna praca z
komputerem możliwa jest dopiero powyżej 85
Hz, a dzisiejsze monitory „wyciągają” nawet
120 Hz i więcej. To prawda, że czym większa
częstotliwość odświeżania, tym szybciej
monitor się zużywa. Jednak zdrowie oczu jest
zdecydowanie ważniejsze i dlatego należy
zawsze ustawiać do pracy częstotliwość ok.
85-100 Hz, a we własnych grach najlepiej
dawać ją do wyboru razem z rozdzielczością.
Zanim jednak zaczniemy, chciałbym zwrócić
uwagę na jedną rzecz. Każde z tych ustawień
to w zasadzie wybór jednej z możliwości.
Wyboru tego można dokonać na sztywno
ustalając np., że gra ma pracować zawsze
w jednej rozdzielczości, ma mieć wyłączony
anti-aliasing itd. lub można dać wybór
użytkownikowi. Powstaje pytanie, kiedy warto
to robić i w przypadku których ustawień?
Odpowiedzi trzeba sobie udzielić samemu
stosownie do konkretnej gry oraz… naszego
zapału do tworzenia specjalnego okna
konfi guracji :)
Warto wspomnieć też, że jeśli będziemy
chcieli pracować na pełnym ekranie,
rozdzielczości i innych parametrów nie
możemy wybrać zupełnie dowolnie. Istnieje
konkretna lista trybów dostępnych na danej
karcie grafi cznej – zestawów rozdzielczości,
częstotliwości odświeżania oraz formatu
piksela. Jej pozycje można wyliczyć
stosując funkcje GetAdapterModeCount i
EnumAdapterModes interfejsu IDirect3D9.
Ustawienia wyświetlania
Pierwsze dwa pola struktury
D3DPRESENT_PARAMETERS
BackBufferWidth i BackBufferHeight typu
UINT, to odpowiednio szerokość i wysokość
podana w pikselach. Urządzenie będzie
posiadało bufor o takich wymiarach. Jeśli
wybierzemy tryb pełnoekranowy, te wartości
powinny stanowić poprawną rozdzielczość,
jaką chcemy ustawić (np. 800 na 600). Jeśli
program będzie działał w oknie, to mogą być
w zasadzie dowolne wartości. My ustawiamy
je zawsze na wymiary obszaru roboczego
(ang. client area) okna naszej aplikacji, ale
wcale nie musi tak być. Jeśli jednak wymiary
bufora nie będą zgodne z wymiarami okna,
wypełniający je obraz będzie rozciągnięty.
Pole BackBufferFormat typu D3DFORMAT
to format bufora, czyli sposób reprezentacji
pikseli w pamięci. Format ma bezpośredni
związek z głębią kolorów, czyli tym, co
ustawiamy we właściwościach ekranu jako
High Color czy True Color (16 bitów, 24 bity,
32 bity). Podobnie jak wyżej – jeśli będziemy
pracowali na pełnym ekranie, trzeba tu
wpisać poprawny format, który może być na
danej karcie grafi cznej, na danym monitorze
i przy danej rozdzielczości ustawiony. Jeśli
natomiast planujemy pracować w oknie,
dawniej musielibyśmy podać format zgodny
z formatem aktualnie ustawionym w systemie
Windows. W obecnej wersji DirectX nie jest
to już konieczne. Piksele będą automatycznie
konwertowane. Proponuję
wpisać tu po prostu
wartość
D3DFMT_
A8R8G8B8.
Oznacza
ona, że na
każdy piksel
przeznaczone
będą 4 bajty
– po
Listing D3D-1.1b Określenie parametrów prezentacji.
01: void CFramework::GeneratePresentParams ( D3DPRESENT_PARAMETERS *pp )
02: {
03: pp->BackBufferWidth = m_Settings.m_uiWidth;
04: pp->BackBufferHeight = m_Settings.m_uiHeight;
05: pp->BackBufferFormat = D3DFMT_A8R8G8B8;
06: pp->BackBufferCount = 1;
07: pp->MultiSampleType = D3DMULTISAMPLE_NONE;
08: pp->MultiSampleQuality = 0;
09: pp->SwapEffect = D3DSWAPEFFECT_DISCARD;
10: pp->hDeviceWindow = m_hWnd;
11: pp->Windowed = !m_Settings.m_bFullScreen;
12: pp->EnableAutoDepthStencil = false ;
13: pp->AutoDepthStencilFormat = D3DFMT_UNKNOWN;
14: pp->Flags = 0;
15: pp->FullScreen_RefreshRateInHz =
16: ( m_Settings.m_bFullScreen ? m_Settings.m_uiRefreshRate : 0 ) ;
17: pp->PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
18: } // end GeneratePresentParams
Do tej metody trzeba przekazać szereg
parametrów, które defi niują podstawowe
ustawienia wyświetlania. Będzie tam
miejsce na wybranie rozdzielczości ekranu
i wielu innych ustawień znanych z opcji
konfi guracyjnych występujących często
w grach. Kilka z nich przekazanych jest
bezpośrednio jako parametry tej funkcji.
Jednym z parametrów jest wskaźnik do
struktury D3DPRESENT_PARAMETERS.
Ta struktura opisuje pozostałe ustawienia i
do wypełnienia jej pól napisałem specjalną
funkcję – GeneratePresentParams ( listing
1b ). Jak widać, tych pól jest naprawdę sporo.
Nie martw się jednak – zajmiemy się teraz
Wybór ten może się bowiem odbywać
poza grą – w osobnym programie bądź
w osobnym oknie podczas uruchamiania
gry. Wtedy zmiana konfi guracji zmusza
użytkownika do zakończenia rozgrywki i
ponownego uruchomienia całej aplikacji.
Lepszym rozwiązaniem jest umożliwienie
konfi guracji w samej grze, bez konieczności
jej restartowania. Ponieważ ustawienia te
podajemy tworząc urządzenie, taka zmiana
zmusi nas do usunięcia i ponownego
utworzenia urządzenia. Jednak inne dane
gry – np. dźwięki czy kolekcja potworów
– może pozostać w pamięci. Dlatego warto
pisać gry w ten sposób. Nasz szkielet będzie
Pole o długiej nazwie FullScreen_
RefreshRateInHz typu UINT to częstotliwość
odświeżania ekranu. W przypadku pracy
w trybie okienkowym koniecznie trzeba
tu wpisać 0. Jeśli chcemy przełączyć
się na pełny ekran, należy podać pewną
częstotliwość. W tym miejscu warto
wspomnieć, że wiele gier pozwalając
nawet zmienić rozdzielczość, zupełnie
ignoruje przy tym zagadnienie wyboru
częstotliwości odświeżania i ustawia ją na
wartość domyślną. Owocuje to najczęściej
04
WARP 2.0 Digital 04 (14) 03/2006
Ustawienia wyświetlania
Pierwsze dwa pola struktury
56017949.006.png 56017949.007.png 56017949.008.png
Direct3D
(ang. front buffer ) – aktualnie pokazywany
i pewna liczba tylnich (ang. back buffer ). To
pole mówi o ilości buforów tylnich. Wszystkich
będzie więc w sumie zawsze o jeden więcej.
Powstaje teraz pytanie, ile buforów ustawić?
Wybór w zasadzie sprowadza się do decyzji,
czy warto stosować dwa bufory, czy też
wystarczy tylko jeden. Istnieje między tymi
ustawieniami drobna różnica. Jeśli włączymy
synchronizację pionową (będzie o niej mowa
niżej), a nasza gra skończy rysowanie klatki w
buforze tylnim, karta będzie musiała poczekać
na jego przełączenie aż do powrotu plamki
na monitorze. Jeśli gra w tym czasie nie
wykonuje jakiś swoich obliczeń, tylko chce
rysować już kolejną klatkę, musi poczekać.
D3DMULTISAMPLE_NONE oraz 0.
w tym przypadku z buforami obrazu. Nie
potrzebujemy dostępu do już namalowanych,
ponieważ w każdej klatce będziemy
odrysowywali cały ekran od początku.
Pole SwapEffect typu D3DSWAPEFFECT
określa sposób wymiany bufora
przeznaczonego do prezentowania na
ekranie. To może być wartość jednej z kilku
dopuszczalnych stałych. My wybierzmy po
prostu D3DSWAPEFFECT_DISCARD. Jest
to wybór najbardziej optymalny i najszybszy.
Angielskie słowo discard (porzucić) występuje
w DirectX często i oznacza, że chcemy
mieć dostęp do jakiegoś zasobu wyłącznie
do zapisu i pozwalamy mu na porzucenie
starej zawartości, gdyż nie potrzebujemy
jej nigdy odczytywać. Dzięki temu DirectX
może stosować bardziej optymalne metody
dostępu do tego zasobu. Tak też dzieje się
jednym
na każdą
składową, w tym na
kanał alfa.
Pole hDeviceWindow typu HWND to po
prostu uchwyt okna, w którym ma zostać
utworzone nasze urządzenie. My podajemy
główne i jedyne okno naszej aplikacji. Nic nie
stoi jednak na przeszkodzie, aby przy lepszej
znajomości Windows API utworzyć własną
kontrolkę (podokno) i w niej osadzić Direct3D,
pozostałą część okna aplikacji przeznaczając
na inne rzeczy, np. na kontrolki graficznego
interfejsu użytkownika. Dzięki temu można
napisać funkcjonalny edytor do świata gry.
Pole BackBufferCount typu UINT to
liczba tylnich buforów. Aby zrozumieć jego
znaczenie, trzeba w tym miejscu poznać
sposób wyświetlania grafiki w grach. Spójrz
na rysunek 1 . Zawsze podczas działania
komputera jest tak, że procesor w swoim
tempie rysuje kolejne klatki animacji, a
tymczasem karta graficzna, niezależnie
od tego, wysyła te 60, 85 czy 100 razy na
sekundę do monitora. Gdyby więc rysowanie
odbywało się w tej samej pamięci, która jest
pokazywana, użytkownik widziałby kolejno
rysujące się obiekty albo dostawał obraz
właśnie w tych momentach, w których nie jest
jeszcze do końca narysowany.
Jako programista nie musisz
jednak o tym pamiętać ani się tym
zajmować. To blokowanie dokonane
zostania automatyczne. Po prostu
jedna z funkcji Direct3D nie zwróci
sterowania aż do odpowiedniego
momentu. Ten szczegół nie jest
jednak aż tak istotny, dlatego nam
wystarczy tutaj ustawienie wartości
oznaczającej jeden bufor - 1.
Dlatego rysowanie w grach działa inaczej.
Buforów zawierających tablicę pikseli
obrazu jest w pamięci karty graficznej kilka.
Podczas gdy jeden z nich jest pokazywany
na monitorze (ten wskazywany czerwoną
strzałką), gra rysuje następną klatkę w
drugim buforze. Potem następuje szybka
zamiana. Nic jednak nie musi być kopiowane.
Wystarczy poinstruować kartę, żeby od tej
chwili na monitor wysyłała obraz z drugiego
bufora. Od tej chwili gra zacznie rysowanie
kolejnej ramki obrazu w tym pierwszym.
Tą operację przełączenia pokazywanego
aktualnie bufora stary DirectDraw nazywał
flip. Obecnie kryje się ona pod wywołaniem
funkcji Present , którą zobaczymy później.
Następne pola – MultiSampleType
typu D3DMULTISAMPLE_TYPE i
MultiSampleQuality typu DWORD
– odpowiedzialne są za anti-
aliasing. Jeśli zostanie włączony,
krawędzie rysowanych obiektów
będą wygładzane i nie będzie
widać „schodków” przy ukośnych
liniach. Niestety, jednocześnie
spada wydajność i to w stopniu
trudnym do zaakceptowania. Dlatego
moim zdaniem nie warto włączać
tej opcji. Oczywiście, można dać
użytkownikowi wybór. Ja jednak
proponuję, dla uproszczenia,
wybrać wartości odpowiednio
Tak więc istnieje zawsze jeden bufor przedni
Rys. 1 Sposób rysowania grafiki w grach, z wykorzystaniem przedniego i tylnego bufora. Tworzą one przełączany
łańcuch wyświetlania (ang. swap chain ).
05
WARP 2.0 Digital 04 (14) 03/2006
56017949.009.png 56017949.010.png 56017949.011.png
Zgłoś jeśli naruszono regulamin