Sunteți pe pagina 1din 25

Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.

Pereteatcu

2. DirectX 9

Destinaţia DirectX 9

Biblioteca DirectX 9 SDK, întruneşte (include) în sine un şir de componente de a lucra cu


grafică, sunet, video, dispozitive de intrare/ieşire şi alte componente multimedia. Cercetăm
instalarea DirectX 9 SDK de la CD la computer.

DirectX — este bibliotecă multimedia, care permite lucrul direct cu dispozitivele periferice ale
computerului fără de a utiliza mijloacele tradiţionale ale platformei Win32. interacţiunea directă
a lui DirectX cu dispozitive dotate cu driverele elaborate de către producătorii ale acestor
dispozitive. Ca rezultat, tehnologia aceasta permite abstractizare completă de partea dispozitivă
şi concentrarea eforturilor la crearea nemijlocită a codului. Toată biblioteca DirectX 9 se
divizează în componente care răspund pentru partea respectivă a lucrului bibliotecii:

 DirectX Graphics;

 DirectInput;

 DirectMusic;

 DirectSound;

 DirectPlay;

 DirectShow;

 DirectSetup.

În fig. 2.1 este prezentată schema generală de interacţiune a DirectX 9 cu unităţile periferice ale
computerului.

DirectX Graphics
DirectInput
DirectMusic
Application DirectSound HAL Hardware
DiredPlay
DirectShow
DirectSetup

Fig. 2.1. Scema generală de interacţiune cu DirectX 9

18
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

Componente DirectX 9

Vom face cunoştinţă cu patru componente din DirectX 9. Acestea sunt Direct3D (întră în
componenţa DirectX Graphics), DirectInput, DirectMusic şi DirectSound. Toate patru se
folosesc la programarea jocurilor computerizate. Ceea ce se referă la sunet, în principiu va fi
îndeajuns numai DirectMusic, totuşi cu scopul de cunoştinţelor mai generale vor fi descrise şi
careva din componente ale DirectSound.

În versiunile precedente (inclusiv până la 7) DirectX Graphics a fost divizat în două părţi:
DirectDraw şi Direct3D. DirectDraw răspundea de grafica 2D, pe când Direct3D răspundea de
grafica 3D. În versiunea a 8 DirectX DirectDraw a fost inclusă în Direct3D. A devenit epoca
jocurilor 3D, ar grafica 2D deja nu este de mirare. Cu apariţia DirectX 9 practic nimic nu s-a
schimbat. Totuşi accesul la DirectDraw a rămas, însă cât el este de actual? În orice caz ne vom
ocupa numai cu Direct3D, care răspunde de lucru cu grafica 3D.

DirectInput — este componenta care răspunde de lucru cu toate dispozitive de introducere:


tastatură, mouse, joistic (engl. joystick), volan, cască"de realitate virtuală ", cu dispozitive de
vibroreturnare şi de legătură inversă. Ea asigură lucrul dispozitivelor şi viteza necesară de
accesare a aplicaţiei prin interacţiunea directă a programului cu echipamentul periferic al
computerului.

DirectMusic, — după cum se vede din denumire, răspunde de lucru cu muzică. În mare parte
asigură lucru cu MIDI (Musical Instrument Digital Interface). Există posibilitatea de a interpreta
şi fişierele WAV. Componenta DirectMusic este forte complicată. Ea nu este limitată numai cu
interpretarea fişierelor MIDI sau WAV.

DirectSound — este partea din DirectX 9 care răspunde de sunet şi permite de a utiliza buferr-ele
de bază (principale), suplimentare (ajutătoare), de statistică şi de flux, care asigură diferite efecte
de sunet.

DirectPlay asigură interacţiunea aplicaţiei noastre cu reţea locală sau cu reţea Internet pentru
modul multiuser de jocuri. Componenta DirectPlay este construită pe baza protocolului propriu,
care asigură lucrul cu toate protocoale de reţea.

DirectShow este necesară pentru lucru cu video torenţial, în particular, serveşte pentru capturarea
şi reprezentarea video de diferite formate: MPEG (Motion Picture Experts Group), MPEG Audio
Layer-3 (cunoscut pentru toţi ca formatul MP3) şi, fără îndoială Audio-Video Interleaved (AVI).

DirectSetup — este componenta care răspunde de instalarea fişierelor DirectX pe calculatorul la


care va rula aplicaţia. Aproape fiecare din noi, s-a întâlnit cu situaţia, când înainte de sau după
instalare a jocului, pe ecran apare un mesaj cu propunerea de a instala o versiune mai nouă de
DirectX. Anume pentru astfel de instalare şi răspunde componenta DirectSetup. În plus la asta,
DirectSetup porneşte automat discul compact cu programul necesar.

Deci, DirectX 9 — este o bibliotecă foarte mare, care răspunde de lucru cu componentele
multimedia ale calculatorului. Ea asigură productivitatea înaltă şi, cel mai important, permite
programatorului, care elaborează aplicaţia, să nu se gândească la partea hard a calculatorului.

19
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

HAL

DirectX include în sine nivelul de abstractizare — HAL (Hardware Abstraction Layer — nivelul
de abstractizare a echipamentului), care permite reglarea setărilor pentru orice echipament şi
dispozitive.

Cu ajutorul nivelului de abstractizare a echipamentului HAL, aplicaţia interacţionează cu


echipamentul computerului, independent de marcă şi de producăto al acestui echipament. Ca
rezultat, codul scris odată poate lucra cu orice dispozitive şi echipament.

СОМ

Toată biblioteca DirectX este construită pe baza СОМ (Component Object Model) — modelul
de obiecte compuse. Neintrând detaliat în esenţa tehnologiei СОМ, reţinem că lucrul cu СОМ se
bazează pe apelurile funcţiilor respective. Totuşi principiile generale ale СОМ vor fi descrise.

СОМ — este un fel de specificaţie, care nu depinde de platformă şi de limbajul de programare.


Ea defineşte standardul de utilizare şi de elaborare a componentelor de program. Specificaţia
СОМ stabileşte un anumit standard pentru arhitectura care se elaborează prin componente.
Componentele program se alcătuiesc din codul executabil, reprezentat în formă de biblioteci
dinamice (DLL) şi fişiere ЕХЕ pentru platforma Windows.

În componenţa СОМ există biblioteca API, care se numeşte СОМ-bibliotecă. Cu ajutorul aceste
biblioteci se dirijează toate componentele. Fiecare din componentele-program realizează un
număr concret de obiecte-COM, accesul la care se realizează prin interfeţe, care, la rândul său,
sunt compuse din funcţii. Cu ajutorul acestor funcţii şi se face interacţiunea cu obiectul-COM.
Interfeţe în СОМ — reprezintă o structură strict definită, care conţine un vector de pointeri la
funcţii. Fiecare di componentele DirectX, de exemplu Direct3D, include un şir de interfeţe cu
setul respectiv de funcţii, cu ajutorul cărora se accesează obiectul-COM. Toate interfeţe în
DirectX au notaţia proprie care începe cu litera "I". Apoi urmează numele obiectului-COM, de
exemplu Direct3D, apoi numărul versiunii utilizate a bibliotecii DirectX, de exemplu:
IDirect3D9. Toate interfeţe sunt strict definite şi nu pot fi modificate, ceea ce permite accesul
la versiunile precedente ale interfeţelor.

Obiecte-СОМ şi interfeţe sunt identificate în sistem cu ajutorul identificatorului global-unicul


compus din 128 biţi (GUID), îl numesc şi "ghid". El se atribuie la elaborarea componentei.
Prescurtarea pentru înscrierea identificatorului există în forme IID (Interface Identifier —
identificatorul interfeţei), iar pentru obiectul-СОМ — CLSID (Class Identifier — identificatorul
clasei).

Interfaţa IUnknown

Toate interfeţe-СОМ sunt derivate de la interfaţa IUnknown, se traduce ca "necunoscută"


Interfaţa IUnknown are trei funcţii: AddRef(), Release()şi QueryInterface(). Fiindcă
toate interfeţe-СОМ moştenesc de la IUnknown, fiecare din ele conţin aceste trei funcţii.

Obiectul СОМ are un contor, care duce evidenţa a numărului de interfeţe utilizate. Cu fiecare
interfaţa nouă activată contorul se măreşte cu unu. Cu ajutorul funcţiei AddRef() se măreşte
contorul obiectului-СОМ indicat. Noi nu vom folosi funcţia aceasta, fiindcă mărirea contorului
de referinţe se face automat.

20
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

După ce interfaţa şi-a terminat lucru, ea se eliberează, iar contorul intern a obiectelor-СОМ se
micşorează cu unu. Imediat după ce valoarea contorului devine zero, obiectul-СОМ îşi descarcă
pe sine din memoria, şi tot în momentul acela, toate obiectele create de el, vor fi descărcate de
către sistem. Pentru decrementarea contorului răspunde funcţia Release(). De funcţia această
ne vom folosi permanent. Operaţia, efectuată de către funcţia Release(), se mai numeşte
"eliberarea resurselor ocupate". În viitor, în fiecare din proiecte pe care vom crea, vom introduce
câte o funcţie specială, care va elibera resursele ocupate prin apelurile funcţiei Release().

După cum deja a fost spus, DirectX 9 nu are interfaţa DirectDraw, care răspunde de grafica 2D.
Totuşi, dacă apare necesitatea de a obţine accesul la interfaţa aceasta, de exemplu, la versiunea a
şaptea DirectDraw7, o astfel posibilitate există, datorită faptului de compatibilitate strictă a
interfeţelor cu versiunile precedente. Pentru asta este prevăzută funcţia QueryInterface().
Din antetul funcţiei totul devine clar:
HRESULT QueryInterface(
REFIID iid,
void** ppvDest)

Funcţia QueryInterface() are următorii parametri:

 iid — identificatorul interfeţei interogate (în corespondenţă cu prescurtarea IID);

 ppvDest — adresa pointerului spre interfaţa obţinută.

Pentru a obţine accesul la interfaţa DirectDraw7, trebuie să scriem următorul cod:


Pointer_la_interfata -> QueryInterface(IID_IDirectDraw7,
&Pointer_versiunii_necesare);

Aici nu mai ajung câteva operaţii, însă totul ser va clarifica pe parcursul studierii ulterioare a
materialului. În cazul în care funcţia QueryInterface() nu va eşua, interfaţa DirectDraw7
devine accesibilă.

Direct3D 9

Direct3D — este cea mai puternică şi cea mai principală componentă a DirectX 9. Ea răspunde
de desenare în grafică 3D, de suprapunerea texturilor pe obiecte, de vertex-shader şi pixeli-
shader, şi de multe altele. Cu ajutorul Direct3D Este posibil de a încorpora grafica 3D în aplicaţii
pentru platforma Windows. Având la dispoziţie un set mare de diferite funcţii, structuri,
macrouri, flaguri, programatorul este practic ferit de partea hard a computerului, fiind concentrat
asupra codului de program. Ultimul uşor se încorporează în aplicaţia şi interacţionează cu
aplicaţia.

Cea mai principală componentă a Direct3D este procesul de randare (rendering). Randarea —
este vizualizarea obiectului 3D pe ecran al monitorului. Este important de înţeles, că orice obiect
se păstrează în memoria video ori operativă a computerului ca un constructor descompus în părţi.
Fiecare parte răspunde de careva componentă a obiectului. De exemplu, robotul este construit
din cap, picioare, mâni, corp. Tot aşa şi obiectul este compus din părţi cum sunt texturi, lumină,
material, poligoane şi multe altele. Pentru a vedea în constructorul un robot întreg, ultimul
trebuie să asamblat din părţi, să treacă prin procesul de asamblare. Tot aşa, pentru a vedea pe
ecran al monitorului un obiect, ultimul trebuie să treacă prin procesul de randare, care şi va face
asamblarea obiectului. În conceptul general, prin obiect se subînţelege nu numai un singur model
3D luat aparte, dar şi mediul care o înconjoară. În Direct3D asta se numeşte scenă.

21
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

În întregime procesul de randare constă din câteva mecanisme. Principalele sunt: mozaicare
(tessellation), transformare, iluminare, rasterizare. Cu ajutorul acestor mecanisme se obţine
vizualizarea pe ecran al monitorului a scenei întregi.

Mozaicare (tessellation) — este mecanismul de descompunere a suprafeţei în poligoane.


Poligonul — este o suprafaţă în spaţiul 3D limitată de colţuri. În 3D poligonul de obicei are
formă triunghiulară.

Transformare (transformation) — este transformarea coordonatelor obiectului cu ajutorul


matricelor. Cele mai uzuale din transformări sunt: rotire, mutare, scalare.

Iluminare (lighting) — este o analogie directă cu mediul înconjurător, în care toate obiectele au
culoarea, care la rândul său depinde de surse de lumină.

Rasterizare (rasterizer) — este cel mai complicat mecanism din cele numite mai sus. El
desenează pe ecran al monitorului toată scena, reprezentând-o la nivelul de pixeli. Pentru asta se
face texturarea, Z-bufferizare, calcularea umbrelor, alfa-amestecarea (alfa-blending), eliminarea
suprafeţelor ascunse şi multe altele.

Crearea scenei în Direct3D

Înainte de a începe programarea graficii cu Direct3D, trebuie să urmărim principiul de creare a


scenei, care poate fi divizat în câteva părţi.

Crearea aplicaţiei tip fereastră. Aceasta este ceea ce a fost făcut în primul proiect, anume, a fost
creată fereastra în regimul selectat şi aspectul dorit. Prin asta s-a creat suprafaţa de lucru. Mai
departe vom integra în acest proiect codul de dezvoltare şi funcţiile DirectX.

Iniţializarea Direct3D. Aici vom crea pointeri spre interfeţe necesare, vom iniţializa Direct3D 9,
vom seta parametrii de prezentare şi vom crea dispozitivul Direct3D. Dispozitivul Direct3D —
este, aşa numite, partea hard şi partea soft ale computerului. Adică noi setăm rezoluţia ecranului,
frecvenţa de regenerare a cadrelor, aflăm informaţii despre placa video, stabilim cum
videoadapterul va prelucra informaţiile propuse, setăm formatul buffer-ului din spate, setăm
parametrii bufferului de adâncime, deci, totul ce se referă la reglarea părţilor hard şi soft ale
computerului.

Crearea obiectului. Fiecare obiect, oricât de mare sau mic ar fi el, se compune din poligoane. De
obicei acestea sunt triunghiuri. Orice triunghi constă din trei colţuri, sau vertexuri. În Direct3D
se operează cu noţiunea de vertex. Un vertex este definit prin trei coordonate în spaţiu, respectiv
după axele X, Y şi Z. Ştiind toate acestea, plus la asta având cunoştinţe despre trigonometrie, se
poate de construit practic orice obiect. Prin setarea formatului vertexurilor, în dependenţă de
utilizare a transformărilor matriciale pentru randarea obiectului sau utilizare a formatului
transformat al vertexurilor pentru reprezentare a obiectului 2D pe ecran, noi creăm prin asta
obiectul.

Iluminare, material şi textură. O scenă reală poate fi prezentată pe ecran, numai dacă vom utiliza
iluminarea şi materiale. Numai creând diferite surse de lumină, de exemplu, bec, soare, foc, şi
setând materiale pentru obiecte, materiale care vor defini reflectarea luminii, vom adăuga
realitatea la scena care se desenează. Şi dacă vom aplica textura, de exemplu, în formă de parchet
de podea, vom atinge identitatea absolută cu mediul înconjurător.

Randarea obiectului. Redesenează scena pe ecran, luând în consideraţie diferite valori pentru
toate componente ale conveierului de randare.
22
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

Etapele susnumite, pentru claritate, au fost divizate condiţional în părţile componente. Totuşi
principiul general a fost descris. Mai departe vom analiza interfeţele din care constă Direct3D 9
şi cum să creează pointeri spre interfeţe necesare. În sfârşit, vom instala DirectX 9 SDK la
calculator.

Interfeţe Direct3D 9

Direct3D 9 conţine cel mai mare număr de diferite interfeţe. Ele răspund de texturare, de buffer-
ul vertexurilor, de buffer-ul indicilor, de vertex- şi pixeli-shader, de interfaţa cu dispozitive
Direct3D 9, etc. Să descriem pe scurt numai acele din interfeţe, pe care vom folosi nemijlocit în
proiectele viitoare:

 IDirect3D9;

 IDirect3DDevice9;

 IDirect3DVertexBuffer9;

 IDirect3DIndexBuffer9;

 IDirect3DTexture9;

 IDirect3DVertexShader9.

IDirect3D9 — este cea mai principală interfaţa, de la ea moştenesc toate celelalte interfeţe.
Interfaţa aceasta trebuie să fie instanţiată în primul rând. Şi numai după asta vom avea acces la
celelalte interfeţe şi funcţii.

Obiectul interfeţei se creează cu funcţia Direct3DCreate9().

IDirect3DDevice9 — se foloseşte după ce s-a creat obiectul de interfaţă principală


IDirect3D9. Se creează obiectul interfeţei a dispozitivului Direct3D. După cum s-a spus, ea
reprezintă un set concret de setări şi opţiuni care sunt necesare pentru crearea şi utilizarea
dispozitivului Direct3D, pe baza căruia se desenează toată scena. Pentru aplicaţii grafice
dispozitivul Direct3D reprezintă monitorul. Obiectul al interfeţei acestea întotdeauna se creează
în al doilea rând şi cu funcţia IDirect3D9::CreateDevice(). Plus la asta interfaţa dată mai
conţine un şir de funcţii pentru a efectua operaţii cu dispozitivul Direct3D. Asta se referă şi la
alte interfeţe.

IDirect3DVertexBuffer9 — interfaţa cu ajutorul căreia se creează buffer-ul vertexurilor.


Fiecare obiect se compune din primitive (feţe triunghiulare, sau mai simplu triunghiuri), descrise
prin puncte (vârfuri, vertexuri) în spaţiu. Fiecare obiect se compune dintr-un set respectiv de
vertexuri. Pentru păstrare comodă a acestor vertexuri, se creează buffer-ul vertexurilor, fiind
realizat în general prin matrice de date. Buffer-ul de vertexuri se creează cu funcţia
IDirect3DVertexBuffer9::CreateVertexBuffer().

IDirect3DIndexBuffer9 — utilizând interfaţa aceasta putem crea buffer-ul indicilor, care ne


permite să indexăm toate vertexurile ale obiectului, pentru a optimiza descrierea obiectului. De
exemplu, avem un pătrat, compus din două triunghiuri. Patru vertexuri în pereche (câte două la
fiecare din două colţuri) au aceleaşi coordonate, adică se repetă. Cu ajutorul buffer-ului de indici
putem evita repetările. Şi dacă pentru un pătrat nu este atât de important, pentru un cub indexarea

23
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

vertexurilor deja este potrivită. În cazul de necesitate, buffer-ul de indici poate fi creat cu funcţia
IDirect3DIndexBuffer9::CreateIndexBuffer().

IDirect3DTexture9 — după cum se vede din nume, interfaţa aceasta răspunde de texturi.
Interfaţa conţine multe funcţii cu ajutorul cărora avem posibilitatea să suprapunem texturi pe
obiect, una peste alta, etc.

IDirect3DVertexShader9 — interfaţa aceasta asigură lucrul cu vertexuri-shader.


Mecanismul de transformare şi de iluminare în aplicaţii mai serioase aduce la restricţii în
utilizare. În locul mecanismului acesta se folosesc vertexuri-shader, care îmbunătăţesc calitatea
graficii. Vertex-shaider — este un program scris în limbajul asemănător cu assembler, şi integrat
în aplicaţia care se creează. Ca rezultat, în timpul real esenţial se ridică calitatea graficii.

Din lista destul de mare au fost descrise doar acele interfeţe pe care vom utiliza mai departe. În
componenţa Direct3D 9 întră aşa numită biblioteca de utilite Direct3DX 9. Utilizând biblioteca
de utilite, avem posibilitatea să încărcăm obiecte create în 3DS МАХ, sau să efectuăm cu obiecte
transformări matriciale. Biblioteca aceasta, la rândul său, conţine multe interfeţe, careva din care
vor fi cercetate ulterior.

Crearea pointerului spre interfaţă

Pentru a crea un pointer la o interfaţă, trebuie să declarăm o variabilă de interfaţă necesară, în


care se va păstra adresa. Sunt, cel puţin, două metode de a crea un pointer spre interfaţă.

Prima metodă:
IDirect3D9* pDirect3D;
IDirect3DDevice9* pDirect3DDevice;

A doua metodă:
LPDIRECT3D9 pDirect3D;
LPDIRECT3DDEVICE pDirect3DDevice;

Amândouă metode aduc la unul şi acelaşi rezultat. Pentru a fi clar, de ce acesta este posibil,
demonstrăm specificarea pentru interfaţa IDirect3D9, care este asemănătoare cu specificări
pentru celelalte interfeţe:
typedef struct IDirect3D9 *LPDIRECT3D9 *PDIRECT3D9

Mai departe, peste tot vom folosi a doua metodă.

După ce a fost declarat pointerul la interfaţa, putem crea şi însăşi obiect. Fiindcă am declarat câte
un pointer la interfeţe, respectiv IDirect3D9 şi IDirect3DDevice9, creăm şi câte un obiect
de fiecare interfaţă:
// pointerul la obiectul-interfaţa IDirect3D9
pDirect3D = Direct3DCreate9(D3D_SDK_VERSI0N);
// pointerul la obiectul-interfaţa IDirect3DDevice9
pDirect3D -> CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSWG,
&Direct3DParametr, &pDirect3DDevice);

În detalii, acţiunile acestea vor fi cercetate în secţiunile ulterioare. Deocamdată vom înţelege
sensul general de toate acţiunile, pentru a uşura înţelegerea materialului de mai departe. De la
început, se declară un pointer la interfaţă, mai precis, variabila în care se va păstra adresa unui
24
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

obiect-interfaţă. Apoi, se creează obiectul-interfaţă şi adresa lui se introduce în variabila, care va


fi numită pointer la interfaţă.

Instalarea DirectX 9 SDK

În mapa DirectX 9 SDK este fişierul dx90bsdk.de dimensiune 218 М. Acesta este versiunea
produsului DirectX 9.0b, care conţine bibliotecile pentru trei limbaje de programare: C++, С#,
Visual Basic.

Instalarea se face în felul următor:

Click dublu pe numele fişierului dx90bsdk în mapa DirectX 9 SDK. Aceasta va aduce la
despachetarea întregului SDK la calculator.

După despachetarea pe ecran al monitorului apare fereastra Microsoft DirectX 9.0 SDK -
InstallShield Wizard, cu posibilitatea alegerii a componentelor necesare. Opţiunea
DirectX Samples and Source Code propune la alegere documentaţia, exemple, coduri-
surse pentru trei limbaje de programare. Pe noi ne interesează nemijlocit C++, celelalte limbaje
după necesitate.

In fereastra de dialog a instalatorului ”InstallShield Wizard” în caseta ”Install to”


indicăm directorul pentru instalarea DirectX 9, de obicei acesta este directorul de rădăcină
C:\DXSDK.

Să apăsăm butonul Next, ca rezultat se va deschide caseta de dialog, în care se va propune tipul
de instalare. Să setăm opţiunea Debug în regiunea DirectX Runtime Support. Să apăsăm
butonul Install Now pentru a continua instalarea.

După ce instalarea se va termina, sistemul ne va propune reîncărcarea computerului. Cu asta


instalarea DirectX 9 SDK se va termina. Putem să începem programarea.

După instalarea descrisă, în Visual C++ .NET, în locul de creare a unui proiect nou, la wizard-ii
standard (AppWizard) se va adăuga încă un wizard — DirectX 9 Visual C++ Wizard. Cu
ajutorul acestuia putem să creăm aplicaţii minimale de utilizare a DirectX 9. Cu astfel de
"carcasă" putem lucra mai departe. Totuşi, mai bine să creăm o "carcasă" proprie, care va
satisface necesităţilor noastre. Carcasa proprie poate fi adăugată la lista wizard-elor. Pentru
exemplu să creăm cu ajutorul wizard-ului o aplicaţie. Vom analiza ce rezultat vom obţine după
compilare.

Să deschidem Visual C++ .NET. Să executăm comanda meniului File/New/Project. Ca


rezultat, va apărea caseta de dialog New Project. În secţiunea Templates să evidenţiem cu
clicul stâng linia DirectX 9 C++ AppWizard. În câmpul Name să scriem numele proiectului,
de exemplu, Demo. În câmpul Location să indicăm unde se va păstra proiectul de creare, de
exemplu, c:\code. Să apăsăm butonul ОК. Pe ecran va apărea fereastra DirectX 9 C++
AppWizard - Demo.

Pe pagina activă Overview vor fi enumerate componentele, care pot fi instalate (incluse) în
aplicaţia. Mai jos, pe fiecare din trei pagini accesibile: Project Settings, Direct3D
Options, DirectInput Options putem să activăm (conectăm) sau să dezactivăm
(deconectăm) un şir de componente, enumerate pe pagina Overview. Să lăsăm totul cum este şi
să apăsăm butonul Finish. Astfel se va crea un proiect, completamente alcătuit de către wizard-
ul DirectX.
25
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

În Visual C++ .NET, pe pagina Solution Explorer din partea stânga va apărea mai mul de
20 de fişiere cu diferite extensii. Să compilăm acest proiect şi să analizăm rezultatul obţinut. Pe
ecran va apărea fereastra a unei aplicaţii simple. Pe suprafaţa de lucru a aplicaţiei va fi desenat
un ceainic frumos de culoare roşie. Cu ajutorul tastelor <Up>, <Left>, <Right>, <Down>
ceainicul poate fi rotit şi privit în detalii din toate părţile.

A fost cercetată în detalii biblioteca de multimedia DirectX 9, care reprezintă standardul la


programarea jocurilor pentru Windows. A fost descrisă noţiunea de modelul СОМ. A fost
analizată diferenţa dintre obiecte-СОМ şi iterfeţe-СОМ, şi cum se efectuează interacţionarea
între ele. A fost descris principiul de construire a scenei în Direct3D. Au fost create doi pointeri
la două interfeţe principale ale Direct3D. A fost descris procesul de instalare a pachetului
DirectX 9 SDK. Acum putem să trecem la programarea graficii.

26
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

3. Iniţializarea Direct3D

Noţiune de iniţializare

Cunoaştem deja cum se creează o aplicaţie simplă de tip fereastră, care apare pe ecran al
monitorului în poziţia indicată, care are dimensiunile indicate, fundalul la care este umplut cu
culoarea indicată. Putem să schimbăm dimensiunile, stilul, culoarea fundalului şi punctul de
apariţie. Pentru asta trebuie să schimbăm valorile ale câmpurilor respective în structura creată
windowsclass.

Să trecem la iniţializarea Direct3D 9. Pentru a iniţializa Direct3D 9 trebuie să trecem prin


anumiţi paşi, altfel nimic nu va lucra. Trebuie să declarăm pointeri la interfeţe, că creăm obiecte
ale interfeţelor, să stabilim parametrii de reprezentare, să aflăm informaţii despre adaptorul, să
curăţim buffer-ul din spate, să eliberăm resursele ocupate de către Direct3D 9 etc. Toţi paşii
aceştia sunt standard şi sunt ordonaţi strict. Este clar că, trecând prin paşii necesari, putem seta
diferite valori pentru parametrii funcţiilor utilizate, adică să reglam Direct3D 9 după necesitate.

Crearea funcţiei de iniţializare Direct3D

Vom crea o funcţie care va răspunde pentru iniţializarea Direct3D 9. Vom numi-o
InitialDirect3D(). Mai întâi de toate vom pregăti un proiect nou.

Creăm în Visual C++ .NET un proiect gol nou de tip Windows application, cu numele
InitDirect3D. Să adăugăm în acest proiect un fişier gol cu acelaşi nume
InitDirect3D.cpp şi să copiem în acest fişier codul sursă din proiectul precedent.

În locul unde am conectat macroul şi fişierul antet Windows.h, inserăm linia:


#include "d3d9.h"

conectând astfel fişierul antet din biblioteca DirectX 9:

Fişierul acesta conţine un număr mare de clase pentru Direct3D 9. Mai trebuie să conectăm şi
biblioteca d3d9.lib. Sunt două metode de a conecta o bibliotecă la proect.

Să cercetăm prima metodă. În fereastra de lucru Visual C++ .NET, pe pagina Solution
Explorer, executăm un click drept pe proiectul InitDirect3D, apare un meniu de context.
Alegem opţiunea Properties, după ce se va deschide caseta de dialog InitDirect3D Property
Pages (fig. 3.1). Apoi selectăm Linker/Input şi în caseta Additional Dependencies
scriem d3d9.1ib. Apăsăm ОК. Astfel biblioteca va fi adăugată în proiectul nostru. Adăugarea
bibliotecii este arătată în fig. 3.2.

27
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

Fig. 3.1. Caseta de dialog InitDirect3D Property Pages

Fig. 3.2. Adăugarea bibliotecii

Toate bibliotecile de care vom avea nevoie în viitor, de exemplu, dinput.lib pentru lucru cu
dispozitive de intrare, vor fi adăugate la fel ca şi d3d9.1ib, nume fiind separate prin spaţiu.
Ordinea de scriere nu contează.

A doua metodă de conectare a bibliotecii necesare, constă în conectare implicită direct în fişierul
codului sursă, cu ajutorul directivei a preprocesorului pragma:
#pragma comment(lib, "d3d9.1ib")

Ambele metode sunt echivalente.


28
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

Mai departe vom declara pointeri spre interfeţe necesare. Vom declara pointeri prin variabile
globale. Imediat după conectarea fişierelor antet, scriem:
LPDIRECT3D9 pDirect3D=NULL;

Acesta va fi pointerul spre interfaţa principală IDirect3D9, care ne va asigura lucrul cu grafica
3D. Am declarat o variabilă în care vom păstra adresa obiectului de interfaţă, vom numi-o
pointerul la interfaţă. La toţi pointeri trebuie să atribuim valoarea iniţială NULL.

Declarăm un pointer la interfaţa de dispozitiv Direct3D 9 pentru a lucra cu grafica 3D:


LPDIRECT3DDEVICE9 pDirect3DDevice=NULL;

În general, etapa de iniţializare a Direct3D 9 poate fi divizată în trei părţi. Mai întâi creăm un
obiect Direct3D 9, apoi setăm parametrii de reprezentare pentru Direct3D 9 şi în sfârşit, pe baza
setărilor efectuate, creăm un dispozitiv Direct3D 9.

Codul funcţiei InitialDirect3D() în întregime va arăta astfel:


HRESULT InitialDirect3D(HWND hvnd)
{
if((pDirect3D=Direct3DCreate9(D3D_SDK_VERSION))==NULL)
return E_FAIL;
D3DDISPLAYMODE Display;

if(FAILED(pDirect3D->
GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &Display)))
return E_FAIL;

D3DPRESENT_PARAMETERS Direct3DParameter;
ZeroMemory(&Direct3DParameter, sizeof Direct3DParameter);

Direct3DParameter.Windowed=TRUE;
Direct3DParameter.SwapEffect=D3DSWAPEFFECT_DISCARD;
Direct3DParameter.BackBufferFormat=Display.Format;

if(FAILED(pDirect3D->
CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hvnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING,
&Direct3DParameter, &pDirect3DDevice)))
return E_FAIL;
/* или
if(FAILED(pDirect3D->
CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hvnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&Direct3DParameter, &pDirect3DDevice)))
return E_FAIL;
*/

return S_OK;
}

29
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

Acum vom cerceta conţinutul funcţiei InitialDirect3D() pe părţi.

În linia:
if((pDirect3D=Direct3DCreate9(D3D_SDK_VERSION))==NULL)

cu ajutorul funcţiei Direct3DCreate9() se creează pointerul principal spre interfaţa


IDirect3D9. Însăşi funcţia foloseşte întotdeauna un singur macrou D3D_SDK_VERSION, care
este definit în fişierul antet d3d9.h, şi care indică la versiunea curentă a SDK. Funcţia trebuie să
întoarcă o valoare diferită de NULL, care este pointerul spre interfaţa IDirect3D9. Dacă asta nu
are loc, este evident că biblioteca Direct3D 9 nu este instalată pe calculatorul dat.

Apoi, trebuie să obţinem informaţii despre modul curent de vizualizare al monitorului. Anume,
rezoluţie a ecranului, colorare, formatul suprafeţei de vizualizare. Informaţiile acestea vor fi
folosite pentru a copia setările curente în buffer-ul din spate (back buffer). În Direct3D tot ceea
ce se vizualizează pe ecran, mai întâi se desenează în buffer-ul din spate, după ce se copie în
buffer-ul primar (front buffer), adică nemijlocit pe ecran. Procesul acesta se numeşte bufferizare
dublă. Permanent se face redesenarea buffer-ului din spate cu copierea ulterioară pe ecran al
monitorului, ceea ce permite crearea animaţia reală a obiectelor. În linia
D3DDISPLAYMODE Display;

declarăm o structură de tipul D3DDISPLAYMODE. Ea va fi folosită la creare a buffer-ului din


spate pentru corespondenţa setărilor curente ale formatului monitorului. Cercetăm declararea
tipului structurat D3DDISPLAYMODE:
typedef struct _D3DDISPLAYMODE {
UINT Width;
UINT Height;
UINT RefreshRate;
D3DFORMAT Format;
} D3DDISPLAYMODE;

Tipul D3DDISPLAYMODE conţine următoarele câmpuri:

 Width — specifică lăţimea suprafeţei de lucru în pixeli;

 Height — specifică înălţimea suprafeţei de lucru în pixeli;

 RefreshRate — frecventă de regenerare. Valoarea 0 înseamnă – valoarea implicită;

 Format — formatul modului de vizualizare. Este membrul enumerării D3DFORMAT, care


defineşte diferite tipuri de formate de suprafeţe. Conţine un număr mare de valori posibile, pe
care vom descrie parţial.

Declararea tipului D3DFORMAT este următoarea:


typedef enum _D3DFORMAT {
D3DFMT_UNKNOWN = 0,

D3DFMT_R8G8B8 = 20,
D3DFMT_A8R8G8B8 = 21,
D3DFMT_X8R8G8B8 = 22,
D3DFMT_R5G6B5 = 23,
D3DFMT_X1R5G5B5 = 24,

30
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

D3DFMT_A1R5G5B5 = 25,
D3DFMT_A4R4G4B4 = 26,
D3DFMT_R3G3B2 = 27,
D3DFMT_A8 = 28,
D3DFMT_A8R3G3B2 = 29,
D3DFMT_X4R4G4B4 = 30,
D3DFMT_A2B10G10R10 = 31,
D3DFMT_A8B8G8R8 = 32,
D3DFMT_X8B8G8R8 = 33,
D3DFMT_G16R16 = 34,
D3DFMT_A2R10G10B10 = 35,
D3DFMT_A16B16G16R16 = 36,

D3DFMT_A8P8 = 40,
D3DFMT_P8 = 41,

D3DFMT_L8 = 50,
D3DFMT_A8L8 = 51,
D3DFMT_A4L4 = 52,

D3DFMT_V8U8 = 60,
D3DFMT_L6V5U5 = 61,
D3DFMT_X8L8V8U8 = 62,
D3DFMT_Q8W8V8U8 = 63,
D3DFMT_V16U16 = 64,
D3DFMT_A2W10V10U10 = 67,

D3DFMT_UYVY = MAKEFOURCC('U', 'Y', 'V', 'Y'),


D3DFMT_R8G8_B8G8 = MAKEFOURCC('R', 'G', 'B', 'G'),
D3DFMT_YUY2 = MAKEFOURCC('Y', 'U', 'Y', '2'),
D3DFMT_G8R8_G8B8 = MAKEFOURCC('G', 'R', 'G', 'B'),
D3DFMT_DXT1 = MAKEFOURCC('D', 'X', 'T', '1'),
D3DFMT_DXT2 = MAKEFOURCC('D', 'X', 'T', '2'),
D3DFMT_DXT3 = MAKEFOURCC('D', 'X', 'T', '3'),
D3DFMT_DXT4 = MAKEFOURCC('D', 'X', 'T', '4'),
D3DFMT_DXT5 = MAKEFOURCC('D', 'X', 'T', '5'),

D3DFMT_D16_LOCKABLE = 70,
D3DFMT_D32 = 71,
D3DFMT_D15S1 = 73,
D3DFMT_D24S8 = 75,
D3DFMT_D24X8 = 77,
D3DFMT_D24X4S4 = 79,
D3DFMT_D16 = 80,

D3DFMT_D32F_LOCKABLE = 82,
D3DFMT_D24FS8 = 83,

31
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

#if !defined(D3D_DISABLE_9EX)
D3DFMT_D32_LOCKABLE = 84,
D3DFMT_S8_LOCKABLE = 85,
#endif // !D3D_DISABLE_9EX

D3DFMT_L16 = 81,

D3DFMT_VERTEXDATA =100,
D3DFMT_INDEX16 =101,
D3DFMT_INDEX32 =102,

D3DFMT_Q16W16V16U16 =110,

D3DFMT_MULTI2_ARGB8 = MAKEFOURCC('M','E','T','1'),

D3DFMT_R16F = 111,
D3DFMT_G16R16F = 112,
D3DFMT_A16B16G16R16F = 113,

D3DFMT_R32F = 114,
D3DFMT_G32R32F = 115,
D3DFMT_A32B32G32R32F = 116,

D3DFMT_CxV8U8 = 117,

#if !defined(D3D_DISABLE_9EX)
D3DFMT_A1 = 118,
D3DFMT_A2B10G10R10_XR_BIAS = 119,
D3DFMT_BINARYBUFFER = 199,
#endif // !D3D_DISABLE_9EX

D3DFMT_FORCE_DWORD =0x7fffffff
} D3DFORMAT;

Cercetăm careva din valorile posibile ale tipului D3DFORMAT:

 D3DFMT_UNKNOWN — valoarea implicită pentru formatul suprafeţei. Vom folosi anume


această valoare;

 D3DFMT_R8G8B8 — 24-biţi de formatul RGB;

 D3DFMT_A8R8G8B8 — 32- biţi de formatul ARGB;

 D3DFMT_D16_LOCKABLE — 16-biţi de formatul buffer-ului de adâncime;

 D3DFMT_D32 — 32-biţi de formatul buffer-ului de adâncime;

 D3DFMT_D15S1 — 16-biţi de formatul buffer-ului de adâncime, din care 15 biţi sunt


rezervaţi pentru canalul de adâncime şi 1 bit pentru canalul şablonului;
32
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

 D3DFMT_D24S8 — 32-biţi de formatul buffer-ului de adâncime, din care 24 biţi sunt


rezervaţi pentru canalul de adâncime şi 8 biţi pentru canalul şablonului;

 D3DFMT_D16 — 16- biţi de formatul buffer-ului de adâncime;

 D3DFMT_D24X8 — 32-biţi de formatul buffer-ului de adâncime, din care 24 biţi sunt


rezervaţi pentru canalul de adâncime;

 D3DFMT_D24X4S4 — 32-biţi de formatul buffer-ului de adâncime, din care 24 biţi sunt


rezervaţi pentru canalul de adâncime şi 4 biţi pentru canalul şablonului;

 D3DFMT_VERTEXDATA —formatul buffer-ului vertexurilor;

 D3DFMT_INDEX16 — 16- formatul buffer-ului indicilor;

 D3DFMT_INDEX32 —32- formatul buffer-ului indicilor;

 D3DFMT_FORCE_DWORD — valoarea nu se foloseşte.

Apoi cu ajutorul funcţiei IDirect3D9::GetAdapterDisplayMode(), obţinem informaţii


despre formatul curent al monitorului. Antetul acestei funcţii arată astfel:
HRESULT GetAdapterDisplayMode(
UINT Adapter,
DISPLAYMODE* Adapter)

Funcţia GetAdapterDisplayMode() are urmîtorii parametri:

 Adapter — videoadapter al monitorului, valoarea D3DADAPTER_DEFAULT indică, că


implicit se va folosi adapterul primar;

 pMode — pointerul la structura de tipul DISPLAYMODE, în cazul nostru este Display, care se
umple cu informaţii ce descriu modul curent al adapterului.

Să scriem următorul cod:


if(FAILED(pDirect3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,
&Display)))
return E_FAIL;

În fragmentul acesta am folosit macroul FAILED() şi codul de returnare E_FAIL.

Apoi se creează un obiect al parametrilor de reprezentare Direct3DParameter. Completând


câmpurile ale acestui obiect, vom descrie comportarea viitoare a aplicaţiei noastre 3D:
D3DPRESENT_PARAMETERS Direct3DParameter;

Următoarea linie:
ZeroMemory(&Direct3DParameter, sizeof Direct3DParameter);

curăţă de diferite "gunoaie" structura care se creează. Aici am folosit funcţia ZeroMemory().
Prim parametru (&Direct3DParameter) reprezintă o referinţă la structura parametrilor de
prezentare, care se curăţă. Al doilea parametru (sizeof Direct3DParameter) descrie
dimensiunea în octeţi a zonei care se curăţă, în cazul nostru toate câmpurile ale structurii. Antetul
funcţiei ZeroMemory()arată astfel:
33
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

VOID ZeroMemory(
PVOID Destination;
SIZE_T Length);

Funcţia ZeroMemory() are următorii parametri:

 Destination — un pointer la începutul blocului de memorie cre trebuie umplut cu zerouri;

 Length — dimensiunea în octeţi a blocului de memorie care se curăţă.

Acum trebuie să iniţializăm câmpurile structurii Direct3DParameter. Să ne amintim că tipul


structurii este D3DPRESENT_PARAMETERS. Deci, cercetăm descrierea tipului
D3DPRESENT_PARAMETERS, totodată vom completa acelea câmpuri de care vom avea nevoie în
viitorul apropiat:
typedef struct _D3DPRESENT_PARAMETERS_ {
UINT BackBufferWidth;
UINT BackBufferHeight;
D3DFORMAT BackBufferFormat;
UINT BackBufferCount;
D3DMULTISAMPLE_TYPE MultiSampleType;
D3DSWAPEFFECT SwapEffect;
HWND hDeviceWindow;
BOOL Windowed;
BOOL EnableAutoDepthStencil;
D3DFORMAT AutoDepthStencilFormat;
DWORD Flags;
UINT FullScreen_RefreshRateInHz;
UINT FullScreen_PresentationInterval;
} D3DPRESENT_PARAMETERS;

Deocamdată vom folosi numai trei câmpuri:


Direct3DParameter.Windowed=TRUE;
Direct3DParameter.SwapEffect=D3DSWAPEFFECT_DISCARD;
Direct3DParameter.BackBufferFormat=Display.Format;

Apoi, în procesul de dezvoltare a proiectului, vom iniţializa câmpuri noi, mărind posibilităţile
aplicaţiei.

Tipul structurat D3DPRESENT_PARAMETERS are următoarele câmpuri:

 Windowed — caracterizează modul video, folosit de aplicaţia noastră. Valoarea TRUE indică
modul fereastră. La momentul de faţă folosim anume acest mod, în proiectul precedent au
fost setate dimensiunile ferestrei — 500x400 pixeli. FALSE — este modul fullscreen, adică pe
ecranul întreg;

 BackBufferFormat — formatul suprafeţei a buffer-ului din spate. Buffer-ul din spate


trebuie să fie în concordanţă cu setările curente ale modului video. Am folosit valoarea din
câmpul Display.Format, care conţine formatul curent al monitorului;

 SwapEffect — are tipul enumerare D3DSWAPEFFECT, şi serveşte pentru a descrie modul


de interschimbare a buffer-ilor. Astfel, dacă windowed are valoarea TRUE şi câmpul
34
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

SwapEffect este pus pe D3DSWAPEFFECT_FLIP, se creează doar un singur buffer din


spate;

 BackBufferWidth — lăţimea buffer-ului din spate în pixeli;

 BackBufferHeight — înălţimea buffer-ului din spate în pixeli;

 BackBufferCount — numărul buffer-ilor din spate, poate fi 0, 1, 2 sau 3. Valoarea


minimă (0) — înseamnă un singur buffer din spate;

 MultiSampleType — câmpul de tipul enumerare D3DMULTISAMPLE_TYPE, şi trebuie să


fie D3DMULTISAMPLE_NONE, dacă câmpul SwapEffect nu a fost pus pe
D3DSWAPEFFECT_DISCARD;

 hDeviceWindow — în cazul în care aplicaţia lucrează în modul fullscreen, va fi utilizată


suprafaţa întreagă a ecranului. În cazul în care parametrul Windowed are valoarea TRUE,
asta va însemna modul fereastră, dacă parametrul are valoarea FALSE, atunci aplicaţia va
lucra în modul fullscreen;

 EnableAutoDepthStencil — dacă parametrul acesta are valoarea TRUE, pentru


Direct3D 9 devine disponibil buffer-ul de adîncime a ferestrei, iar dispozitivul Direct3D 9
va crea bufferul de şablon;

 AutoDepthStencilFormat — are tipul enumerare D3DFORMAT, care descrie formatul de


calculare automată a adâncimii şablonului pentru dispozitivul creat. Parametrul acesta va fi
ignorat în cazul în care parametrul EnableAutoDepthStencil nu este pus pe TRUE;

 Flags — câmpul acesta poate fi pus pe 0, sau să utilizeze flagul


D3DPRESENTFLAG_LOCKABLE_BACKBUFFER. Flagul se setează în cazul în care aplicaţia
cere blocarea buffer-ului din spate;

 FullScreen_RefreshRateInHz — frecvenţa de regenerare a ecranului;

 FullScreen_PresentationInterval — indică intervalul maxim necesar pentru


trecerea la alt buffer din spate. Pentru modul de lucru fullscreen poate fi folosită valoarea
D3DPRESENT_INTERVAL_DEFAULT sau o valoare care corespunde a unui din flaguri
enumerate în structura de tipul D3DCAPS9. Ultima este foarte mare şi deocamdată nu va fi
discutată.

La primă vedere tipul structurat D3DPRESENT_PARAMETERS se pare destul de complicat, totuşi


totul se reduce la utilizarea substituţiilor de valori deja cunoscute. Numărul de valori nu este atât
de mare, şi ele toate sunt legate între ele.

Mai departe vom crea un obiect de interfaţă cu dispozitivul de vizualizare. Pentru asta vom folosi
funcţia CreateDevice(). Ne amintim că interfaţa principală IDirect3D9 se creează cu
funcţia Direct3DCreate9(), iar interfaţă a dispozitivului Direct3D 9 — cu funcţia
IDirect3DDevice9::CreateDevice(). Acestea două funcţii sunt absolut. Funcţia
CreateDevice()are următorul antet:
HRESULT CreateDevice(
UINT Adapter,
D3DDEVTYPE DeviceType,

35
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

HWND hFocusWindow,
DWORD BehaviorFlags,
D3DPRESENT_PARAMETERS* pPresentationParameters,
IDirect3DDevice9** ppReturnedDeviceInterface
)

Descriem parametrii funcţiei CreateDevice():

 Adapter — este placa video a calculatorului nostru. De obicei se foloseşte un singur


videoadapter, de aceea se foloseşte valoarea D3DADAPTER_DEFAULT, adică implicit
videoadapter primar;

 DeviceType — defineşte tipul dorit al dispozitivului. De valoarea parametrului acesta


depinde va fi sau nu folosită accelerare a plăcii video. Parametrul are tipul de enumerare
D3DDEVTYPE cu câteva valori predefinite. Declararea tipului D3DDEVTYPE este următoarea:
typedef enum _D3DDEVTYPE {
D3DDEVTYPE_HAL = 1,
D3DDEVTYPE_REF = 2,
D3DDEVTYPE_SW = 3,
D3DDEVTYPE_FORCE_DWORD = Oxffffffff
} D3DDEVTYPE;

Iată care sunt valori ale tipului D3DDEVTYPE:

 D3DDEVTYPE_HAL — se folosesc posibilităţile hardului. Vom alege valoarea această,


pentru a accelera lucrul cu grafică, prin utilizarea videoadapterulul;

 D3DDEVTYPE_REF — emulare prin program;

 D3DDEVTYPE_SW — se foloseşte numai împreună cu funcţia


IDirect3D9::RegisterSoftwareDevice();

 D3DDEVTYPE_FORCE_DWORD — nu se foloseşte;

 hFocusWindow — în parametrul acesta trebuie să stabilim descriptorul ferestrei principale, în


cazul nostru este hwnd. Parametrul stabileşte pentru care din ferestre se creează interfaţa
dispozitivului;

 BehaviorFlags — indică cum va fi procedată prelucrarea vertexurilor. Valoarea se combină


printr-o combinaţie de flaguri. Iată care sunt cele mai principale din ele:

 D3DCREATE_HARDWARE_VERTEXPROESSING — în cazul acesta se foloseşte placa


video şi prelucrare a vertexurilor se face de către dispozitiv. Vom folosi anume flagul
acesta, fiindcă la data de azi o placă vidio destul de performantă nu costă atât de mult
cum era pe vremurile trecute;

 D3DCREATE_SOFTWARE_VERTEXPROCESSING — flagul acesta se foloseşte doar


atunci când obligatoriu dorim prelucrarea vertexurilor prin program;

36
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

 pPresentationParameters — reprezintă un pointer la structura


D3DPRESENT_PARAMETERS. În cazul nostru este Direct3DParameter. Astfel folosim
toate câmpurile setate preventiv;

 ppReturnedDeviceInterface — prin parametrul acesta se indică adresa pointerului (în


cazul nostru este &pDirect3DDevice). În pointerul acesta funcţia CreateDevice() va
înscrie rezultatul lucrului propriu, adică adresa obiectului-interfaţa IDirect3DDevice9
creat.

Ca rezultat, toată operaţia de creare a obiectului-interfaţă cu dispozitivul (sau pur şi simplu


interfaţă) va arăta astfel:
if(FAILED(pDirect3D->
CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hvnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING,
&Direct3DParameter, &pDirect3DDevice)))
return E_FAIL;

După creare a funcţiei InitialDirect3D(HWND hvnd) apelul ei trebuie să plasăm în funcţia


WinMain(), nemijlocit după apel al funcţiei CreateWindowEx():
if(SUCCEEDED(InitialDirect3D(hwnd)))
{
ShowWindow(hwnd, SW_SHOWDEFAULT); // desenam fereastra
UpdateWindow(hwnd); // reinoim fereastra
ZeroMemory(&msg, sizeof msg);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
)

Prelucrarea erorilor în DirectX

În timpul lucrului cu DirectX, şi nu numai, este necesar să utilizăm sistemul de prelucrare a


erorilor, de exemplu, când creăm sau iniţializăm Direct3D 9. Pe toate acestea trebuie să le
urmărească programatorul însăşi. Pentru asta în Direct3D 9 sunt definite macrouri speciale:

 FAILED() — verifică dacă codul a eşuat;

 SUCCEEDED() — verifică dacă codul s-a executat cu succes.

În principiul, ambele macrouri lucrează după o schemă asemănătoare. Totuşi este şi diferenţa.
Cercetăm aceste macrouri.

Primul macrou are următoarea structură:


if(FAILED(...))
Eşuare
else
Totul este bine, mergem mai departe

Al doilea macrou:
37
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

if(SUCCEEDED(...))
Totul este bine, mergem mai departe
else
Eşuare

Pentru a specifica rezultatul de returnare în DirectX 9 sunt definite coduri de returnare.


Analizând codul de returnare, în cazul erorii oarecare, obţinem informaţii despre natura erorii.
Setul de astfel de coduri este foarte mare, aparte ele nu vor fi discutate aici. De aceea în toate
exemple vom utiliza coduri universale de returnare E_FAIL şi S_OK. Informaţii suplimentare
despre coduri de returnare pot fi obţinute din documentaţie pentru DirectX 9 SDK.

Rendering, sau desenarea în cadrul ferestrei a aplicaţiei

Rendering-ul reprezintă procesul de desenare a obiectelor 3D. Pentru început, vom colora buffer-
ul din urmă cu o culoare oarecare, doar uniform.

Cu acest scop vom crea funcţia RenderingDirect3D(), care va răspunde de desenare în


cadrul ferestrei aplicaţiei noastre:
HRESULT RenderingDirect3D()
{
if(pDirect3DDevice==NULL)
return E_FAIL;
...;
return S_OK;
}

În primul rând vom curăţa buffer-ul din urmă. Pentru asta vom folosi funcţia
IDirect3DDevice9::Clear()care are următorul antet:
HRESULT Clear(
DWORD Count,
const D3DRECT *pRects,
DWORD Flags,
D3DCOLOR Color,
float Z,
DWORD Stencil
)

Descriem parametrii funcţiei Clear():

 Count — numărul de regiuni dreptunghiulare, care trebuie să fie curăţate. Regiunile sunt date
printr-un vector de dreptunghiuri (parametrul 2). Dacă se indică valoarea 0, se va curăţa toată
suprafaţa, adică toată regiunea de randare;

 pRects — adresa vectorului de dreptunghiuri. Fiecare element al acestui vector este structura
de tipul D3DRECT, care descrie dimensiunile unei zone dreptunghiulare. Declararea tipului
structurat D3DRECT este următoarea:
typedef struct _D3DRECT {
LONG x1;
LONG y1;
LONG x2;

38
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

LONG y2;
} D3DRECT;

Descriem câmpurile structurii de tipul D3DRECT:

 x1 şi у1 — coordonatele colţului stânga-sus al dreptunghiului;

 х2 şi у2 — coordonatele colţului dreapta-jos al dreptunghiului;

 Flags — indică care din suprafeţele trebuie să fie curăţate. Valoarea parametrului reprezintă
o combinaţie arbitrară a următoarelor flaguri:

 D3DCLEAR_STENCIL — de a înlocui conţinutul buffer-ului şablonului cu valori ale


parametrului Stencil;

 D3DCLEAR_TARGET — de a curăţa buffer-ul suprafeţei cu culoarea indicată prin


parametrul Color. Vom utiliza anume acest flag pentru a curăţa buffer-ul suprafeţei cu
culoarea dată;

 D3DCLEAR_ZBUFFER — de a curăţa buffer-ul de adâncime cu valoarea parametrului Z;

 Color — culoare. Pentru a seta valoarea parametrului acesta putem utiliza macroul
D3DCOLOR_XRGB(). De exemplu, D3DCOLOR_XRGB(255, 255, 50) va colora regiunea
de randare cu galben deschis;

 Z — indică valoarea pentru curăţare a Z-bufferului. Valoarea trebuie luată din intervalul
[0.0f — 1.0f]. Valoarea 0.0f — reprezintă cea mai apropiată distanţă de la observator.
Valoarea 1.0f — reprezintă cea mai îndepărtată distanţă de la observator. Deocamdată vom
folosi valoarea 1.0f. Sensul parametrul mai detaliat va fi descris în compartimenul
consacrat bufferului de adâncime;

 Stencil — valoarea pentru curăţare a bufferului de şablon. Trebuie luată din intervalul de la
0 până la 2n-1, unde n este adâncime în biţi a bufferului de şablon. Deocamdată vom pune
parametrul acesta pe 0. Mai detaliat parametrul acesta va fi descris ulterior.

Notă. Deocamdată vom folosi numai flagul D3DCLEAR_TARGET, de aceea valorile Z şi


Stencil vor fi ignorate. În general, funcţia Clear() ignoră valorile ale acelor din parametrii
Color, Z, Stencil, flagurile cărora nu participă la formarea parametrului Flags.

Deci, ştiind valorile necesare ale argumentelor funcţiei Clear(), putem să curăţim buffer-ul din
urmă:
pDirect3DDevice->Clear(0, NULL, D3DCLEAR_TARGET,
D3DCOLOR_XRGB(255, 255, 0), 1.f, 0);

În Direct3D se foloseşte noţiunea de scenă (engl. scene). În scenă întră toate obiectele 3D cu
toate şi diferite proprietăţile sale. Înainte de chemarea scenei se apelează funcţia BeginScene()
din interfaţa IDirect3DDevice9. Funcţia BeginScene()semnalează că poate fi începută
randarea obiectelor scenei, adică elaborează comanda de desenare a scenei:
pDirect3DDevice->BeginScene(); // începutul scenei

Scena se termină cu apelul funcţiei EndScene() din interfaţa IDirect3DDevice9:


pDirect3DDevice->EndScene(); // sfârşitul scenei
39
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

Ambele funcţii nu au parametri. Fiindcă, deocamdată nu avem scenă (pur şi simplu scena este
goală), între aceste două apeluri nu este nimic, totuşi după fiecare apel al funcţiei
BeginScene() neapărat trebuie să fie apelul EndScene(), iar între aceste două apeluri are loc
randarea scenei.

Acum totul ce a fost desenat în buffer-ul din urmă (back buffer), vom afişa pe ecran cu ajutorul
funcţiei IDirect3DDevice9::Present(). Ea va copia ceia ce a fost desenat din buffer-ul din
urmă în bufferul frontal (front buffer), adică pe ecran. Antetul funcţiei de prezentare este
următorul:
HRESULT Present(
const RECT *pSourceRect,
const RECT *pDestRect,
HWND hDestWindowOverride,
const RGNDATA *pDirtyRegion
)

Urmează descrierea parametrilor funcţiei Present():

 pSourceRect — un pointer la structura RECT a suprafeţei surse de desenare. Valoarea NULL,


indică că va fi folosită întreaga suprafaţa de desenare (toată suprafaţa pe care o reprezintă
buffer-ul din urmă);

 pDestRect — un pointer la structura RECT ce reprezintă suprafaţa de destinaţie (suprafaţa pe


care o reprezintă buffer-ul frontal). Pentru a utiliza pe toată suprafaţa frontală trebuie să
punem valoarea NULL. Astfel se va copia conţinutul buferr-ului din urmă „unu la unu” în
buffer-ul frontal;

 hDestWindowOverride — un pointer la fereastra destinatarului, zona de client a căreia a


fost luată ca adresa parametrului de reprezentare. Valoarea NULL, va indica că vom folosi
câmpul hWndDeviceWindow din structura de tipul D3DPRESENT_PARAMETERS. Ne
amintim că anterior pentru câmpului hWndDeviceWindow am folosit valoarea hwnd —
adică descriptorul ferestrei pe care am creat;

 pDirtyRegion — ultimul parametru s-a păstrat de la versiunile precedente ale DirectX şi


deja nu se foloseşte. El a fost lăsat pentru compatibilitate şi întotdeauna are valoarea NULL.

Deci, în cazul nostru apelul funcţiei Present() va fi simplificat la maximum:


pDirect3DDevice->Present(NULL, NULL, NULL, NULL);

Textul complet al funcţiei RenderingDirect3D() arată astfel:


HRESULT RenderingDirect3D()
{
if(pDirect3DDevice==NULL)
return E_FAIL;
pDirect3DDevice->Clear(0, NULL, D3DCLEAR_TARGET,
D3DCOLOR_XRGB(255, 255, 0), 1.f, 0);

pDirect3DDevice->BeginScene();
// Aici va urma desenarea scenei

pDirect3DDevice->EndScene();
40
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

pDirect3DDevice->Present(NULL, NULL, NULL, NULL);

return S_OK;
}

După crearea funcţiei care va face renderingul, apelul ei plasăm în prelucrarea cozii în funcţia
MainWinProc() în locul unde se face redesenarea ferestrei:
case WM_PAINT:
RenderingDirect3D();
ValidateRect(hwnd, NULL);
return 0;

Funcţia ValidateRect() se apelează pentru desenarea (reînnoirea) suprafeţei de lucru a


ferestrei aplicaţiei. Parametrul — hwnd — descriptorul ferestrei. Al doilea parametru (cu
valoarea NULL) — indică redesenarea implicită, adică redesenare a ferestrei în întregime. Cu
asta procedura a renderengului se termină. Deocamdată, am realizat ce mai simplă metodă de
lansare a renderingului, anume prin utilizare a funcţiei MainWinProc() şi a mesajului
WM_PAINT. În viitor vom cerceta şi alte metode de apelare a funcţiei RenderingDirect3D().

Eliberarea resurselor, ocupate de Direct3D

În procesul de închidere a aplicaţiei, ultima trebuie să întoarcă toate resursele ale sistemului
ocupate de către Direct3D 9. Conform regulilor de utilizare a СОМ-interfeţelor, toate resursele
trebuie descărcate din memorie iar referinţele respective trebuie puse pe zero. Pentru asta avem
la dispoziţie funcţia Release(), care deja a fost descrisă. Trebuie să ţinem cont de ordine de
eliberare a resurselor (obiectelor). Eliberarea se face în ordinea inversă cu ocupare a lor. Ultima a
fost declarată interfaţa pDirect3DDevice9, deci ea va fi eliberată prima. Etc., în ordinea
inversă.

Pentru asta scriem funcţia, pe care vom numi-o DeleteDirect3D(), şi care va elibera
resursele ocupate:
void DeleteDirect3D()
{
if(pDirect3DDevice)
pDirect3DDevice->Release();
if(pDirect3D)
pDirect3D->Release();
}

Amplasăm apelul acestei funcţii în MainWinProc() ca reacţie la mesajul WM_DESTROY:


case WM_DESTROY:
DeleteDirect3D();
PostQuitMessage(0);
return 0;

Textul programului actual este dat în anexa 1, ia rezultatul programului este arătat în fig. 3.3.

41
Grafica 3D (Suport de curs în baza Direct3D), S.Pereteatcu, A.Pereteatcu

Fig. 3.3. Fereastra de bază colorată cu galben

Am terminat pregătirea necesară şi putem să trecem la crearea şi desenarea obiectelor.

42

S-ar putea să vă placă și