Doteraz sme sa zaoberali iba 2D grafikou. Nemalo by pre vás byť problém urobiť jednoduchú 2D hru. Doterajšie vedomosti vám však poslúžia pri prechode k 3D. Preto pre tých, ktorí ešte nemajú žiadne skúsenosti s D3D odporúčam prečítať si predchádzajúce časti.

Teraz sa dostávame k zaujímavejšej časti grafiky DirectX, ktorou je 3D. Bohužiaľ, nie ľahšej. Treba si uvedomiť, že nám pribudol tretí rozmer Z, čo výpočty dosť značne sťažuje. Pre transformáciu objektov v 3D priestore budeme používať matice. Je to najrýchlejší a zároveň najprehľadnejší spôsob, ktorý využil aj Microsoft v Direct3D.

Teória

Matice (anglicky: Matrix, plural: Matrices) - Je to matematický postup, ktorý využíva Direct3D pri 3D operáciách. Pochopenie princípu počítania s maticami bude pre vás veľkou výhodou. I keď nie je to nutné, pretože Direct3D ponúka množstvo funkcií, ktoré všetko urobia za vás.

Poznáme 3 typy matíc: Priestoru, Pohľadu a Projekcie

Priestoru - (world matrix) určuje presnú polohu vertexov v priestore. Tu je potrebné si ujasniť pojem priestor: Je to množina všetkých bodov v scéne.

Na priestor môžeme aplikovať transformácie ako sú: presúvanie, zmena veľkosti a rotácia. Všetko sa to deje pomocou matíc. Tzn., že keď chceme rotovať všetky vertexy v priestore aplikujeme naň maticu rotácie. Červená kocka v obrázku predstavuje výrez z priestoru, ktorý obsahuje 1 vertex. Po použití matice rotácie sa otáčajú všetky body v priestore podľa osi y, o uhol alfa.

Je dôležité zachovať postupnosť transformácií. Je rozdiel rotovať okolo osi X a potom Z, ako keď najprv rotujete okolo Z a potom X.

Keď chcete použiť viacero typov transformácií naraz (rotáciu a zároveň presúvanie), dajú sa pomocou funkcie D3DXMatrixMultiply skombinovať. Je efektívnejšie skombinovať 5 transformácií (transformačných matíc) do jednej ako aplikovať každú samostatne.

Pohľadu - (view matrix) si môžete predstaviť ako kameru, ktorá sa nachádza na nejakom bode so súradnicami X,Y,Z a pozerá sa na iný bod v priestore. U hier typu Doom je to práve postava, za ktorú hráte a zameriavač vašej zbrane je miesto, kam sa kamera pozerá. Tu si treba uvedomiť jednu vec: pokiaľ sa vám na obrazovke nič nezobrazí može to byť tým, že ste urobili chybu v kóde alebo zle nastavili kameru (resp. v jej zornom poli nie je žiadny objekt). Takéto chyby sa dosť ťažko odlaďujú, preto buďte opatrný a svoj program priebežne
testujte.

Projekcie - (projection matrix alebo aj field of view (FOV)) určuje spôsob ako bude 3D priestor zobrazený na 2D obrazovku. Môžete si nadefinovať úplne iný tvar objektov ako v skutočnosti vyzerajú, t.j. roztiahnuť, predĺžiť. Môžete určiť do akej vzdialenosti od kamery budú objekty zobrazené. Ako keby ste sa hrali s objektívom kamery.

Použitie v praxi

Direct3D obsahuje mnoho vstavaných funkcií pre prácu s maticami. V závere tejto lekcie si podrobne ukážeme ako tieto funkcie pracujú.

Postup je rovnaký ako v predchádzajúcich lekciách: Deklarujeme vertex, naplníme štruktúru vertexu potrebnými údajmi. Nezabúdajte, že teraz zadávame aj Z súradnicu.

'Vypnutie osvetlenia
D3DDevice.SetRenderState D3DRS_LIGHTING, False
'Vypnutie vykreslovania objektov iba z jednej strany
D3DDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_NONE

Skôr než niečo zobrazíme, musíme si správne nakonfigurovať renderovacie zariadenie (D3DDevice) pomocou SetRenderState. Keďže v projekte ešte nepoužívame žiadne svetlá musíme vypnúť globálne svetlo v celom projekte. Defaultne je svetlo zapnuté, a tak by sa stalo, že na obrazovke nič neuvidíme (objekt by nemal byť čím osvetlený).

Taktiež je potrebné nastaviť resp. vypnúť CULLING. Každý trojuholník má dve strany, ale D3D štandardne zobrazuje iba jednu. Skúste zapoznámkovať tento riadok kódu a uvidíte, čo sa stane.

Poslednou fázou je nastavenie a použitie matíc:

Dim matWorld As D3DMATRIXDim matView As D3DMATRIX
Dim matProj As D3DMATRIX

Aj matice je potrebné deklarovať.

D3DXMatrixLookAtLH matView, _
VytvorVektor (0, 5, 9), _
VytvorVektor (0, 0, 0), _
VytvorVektor (0, 1, 0)
D3DDevice.SetTransform D3DTS_VIEW, matView

D3DXMatrixLookAtLH

Maticu pohľadu vytvoríme funkciou D3DXMatrixLookAtLH.

Mout = matView - matica pohľadu

VEye = VytvorVektor (0, 5, 9) - 3Dvektor (so súradnicami XYZ), ktorý udáva polohu kamery

VAt = VytvorVektor (0, 0, 0) - 3Dvektor, ktorý udáva kam sa kamera pozerá

VUp = VytvorVektor (0, 1, 0) - 3Dvektor, ktorý udáva ,,kde je hore”. Obyčajne býva (0,1,0)

Takto vytvorenú maticu použijeme príkazom SetTransform. Prvý parameter udáva o aký typ matice ide (priestoru, pohľadu, projekcie). Druhý je nami vytvorená matica.

D3DXMatrixPerspectiveFovLH matProj, pi / 4, 1, 0.1, 500
D3DDevice.SetTransform D3DTS_PROJECTION, matProj

Takto jednoducho sa dá vytvoriť matica projekcie:

D3DXMatrixPerspectiveFovLH

MOut = matProj - matica projekcie

fovy = pi / 4 - uhol pohľadu (v radiánoch)

aspect = 1 - pomer výšky a šírky obrazu. Jednotka znamená pomer 1:1

zn = 0.1 - najbližší bod

zf = 500 - najvzdialenejší bod

Zn a zf určujú rozsah viditeľnosti bodu. Ak je vzdialenosť bodu (alebo celého objektu) väčšia ako zf, potom nebude zobrazený. Obidva údaje sú v metroch . Nastavením malého Zf sa vzdialenejšie objekty nevykreslia, čím sa značne zníži zaťaženie systému. Toto využíva mnoho hier, kde na konci viditeľnosti sa použije hmla (fog).

Ako posledné som si nechal maticu priestoru, ktorá nám otáča objekt:

Dim matWorld2 As D3DMATRIX
uhol = uhol + 0.1
If uhol >= 360 Then uhol = 0
D3DXMatrixIdentity matWorld
D3DXMatrixIdentity matWorld2
D3DXMatrixRotationX matWorld, uhol * (pi / 180)
D3DXMatrixRotationZ matWorld2, uhol * (pi / 180)
D3DXMatrixMultiply matWorld, matWorld, matWorld2
D3DDevice.SetTransform D3DTS_WORLD, matWorld

Úlohou tohto kódu je rotovať objekt okolo osi X a Z. To zabezpečujú dve funkcie D3DXMatrixRotationX a D3DXMatrixRotationZ. Prvá nastaví maticu matWorld, tak aby rotovala objekt okolo osi X a druhá nastaví matWorld2, tak aby rotovala objekt okolo osi Z. Uhol rotácie sa pri každom novom frame mení o 0,1 stupňa. Avšak D3D nepracuje so stupňami, preto všetko treba konvertovať na radiány. Potom pomocou D3DXMatrixMultiply vynásobí tieto matice, a tak dostane 1 výslednú, ktorá bude robiť presne to, čo tie dve dohromady.

Syntax je nasledovná: D3DXMatrixMultiply (cieľová matica, zdroj1, zdroj2)

Príkaz D3DXMatrixIdentity je v tomto kóde zbytočný. Jeho úlohou je obnoviť maticu na základnú hodnotu, a tým nastaviť uhol otáčania na 0. Podrobnosti sa dozviete, keď budete čítať ďalej.

To je všetko k základom 3D. Pomocou vstavaných funkcií je to rýchle a jednoduché. Na záver ešte uvediem zoznam často používaných funkcií na transformáciu priestoru:

D3DXMatrixRotationX - rotácia okolo osi X

D3DXMatrixRotationY - rotácia okolo osi Y

D3DXMatrixRotationZ - rotácia okolo osi Z

D3DXMatrixRotationAxis - rotácia okolo definovanej osi

D3DXMatrixRotationYawPitchRoll - špeciálny typ rotácie. Efekt: podobný letu lietadla

D3DXMatrixScaling - zväčšovanie/zmenšovanie

D3DXMatrixTranslation - posúvanie v smere X,Y,Z

D3DXMatrixTranspose - prepis pomocou inej matice

Počítanie s maticami

Teraz si bližšie vysvetlíme ako to celé funguje. Na začiatku vás upozorňujem, že pochopenie nasledujúcich riadkov nie je nevyhnutné. Direct3D obsahuje množstvo pomocných funkcií (pozri vyššie), ktoré stačia na väčšinu transformácií. Každopádne trocha matematiky nikomu nezaškodí.

Direct3D používa matice 4×4. Všeobecný predpis matice sa nachádza medzi červenými hranatými zátvorkami. Pomocou nej môžeme transformovať každý bod v priestore so súradnicami X,Y,Z, čím dostaneme nový bod X’ Y’ Z’. Presnejšie povedané, keď vynásobíme akýkoľvek bod s maticou, dostaneme nový bod. Z pohľadu VB je matica dvojrozmerné pole.

Násobenie je trocha odlišné od bežného spôsobu. Tak ako ukazuje obrázok: transformovanú prvú súradnicu (X’)dostaneme tak, že najprv sa vynásobíme každú súradnicu príslušným členom matice v prvom stĺpci a následne tieto násobky spočítame. Podobne je to aj pre druhú a tretiu transformovanú súradnicu, pričom násobenie prebieha s členmi v druhom a treťom stĺpci. Posledný štvrtý prvok matice sa dopĺňa jednotkou.

Deklarácia matice vo VB je nasledovná:

Type D3DMATRIXm11 As Single
m12 As Single
m13 As Single
m14 As Single
m21 As Single
m22 As Single
m23 As Single
m24 As Single
m31 As Single
m32 As Single
m33 As Single
m34 As Single
m41 As Single
m42 As Single
m43 As Single
m44 As Single
End Type

Translácia

Najjednoduchší typ transformácie. Je to posúvanie bodov po priamke určenej vektorom. Na každý typ transformácie sa používa špecifická matica. Translačná matica má na spodku vektor daný Tx, Ty, Tz a jednotky po diagonále.

Výsledkom násobenia je rovnica: transformovaný bod (X’,Y’,Z’) = bod (X,Y,Z) + vektor (X,Y,Z). Príklad: Máme bod A (2,0,5) a chceme ho premiestniť do bodu B(0,3,2). Riešenie: Využijeme translačnú maticu s vektorom u(-2, 3, -3). Potom to už len stačí prepísať do VB kódu:

Sub TranslaciaMatice (matica As D3DMATRIX, v As D3DVECTOR)
D3DXMatrixIdentity matica  'reset matice
matica.m41 = v.x  'X komponent vektora
matica.m42 = v.y  'Y komponent
matica.m43 = v.z  'Z komponent
D3DDevice.SetTransform D3DTS_WORLD, matica
End Sub

Rotácia

Rotovať môžeme okolo osi X, Y alebo Z. Nezabudnite, že uhol rotácie je v radiánoch, nie v stupňoch. Prevod medzi nimi je nasledovný:
Rad = stupen * (pi /180). Aj tu použijeme špeciálny typ matice:

Uvediem už iba kód vo VB pre rotáciu okolo osi X:

Sub RotaciaMatice (matica As D3DMATRIX, uhol As Single)
D3DXMatrixIdentity matica  'reset matice
Dim Radian as Single
Radian = uhol * pi /180  'Prevod na radiány
matica.m22 = Cos (Radian)
matica.m23 = Sin (Radian)
matica.m32 = -Sin (Radian)
matica.m33 = Cos (Radian)
D3DDevice.SetTransform D3DTS_WORLD, matica
End Sub

Scaling

Predstavuje zmenu veľkosti objektu. Ten môžeme zväčšiť, zmenšiť alebo roztiahnuť v závislosti na tzv. scaling faktor s(X,Y,Z). Ak je rovný 1 (default), potom objekt si zachováva pôvodnú veľkosť.

Sub ScalingMatice (matica As D3DMATRIX, s As D3DVECTOR)
D3DXMatrixIdentity matica  'reset matice
matica.m11 = s.x
matica.m22 = s.y
matica.m33 = s.z
matica.m44 = 1
D3DDevice.SetTransform D3DTS_WORLD, matica
End Sub

Ide skôr o zhusťovanie alebo naopak rozptýlenie bodov k originu.

Príklad: Máme úsečku AB tvorenú bodom A(2,0,0) a B(5,0,0). Chceme ju dvojnásobne roztiahnuť v smere osi X. Potom scaling faktor bude s(2,1,1). Výsledok: Bod A bude mať súradnice A(4, 0, 0) a B(10,0,0). Úsečka s pôvodnou dĺžkou X = 5 - 2 = 3 teraz meria X’ = 10 - 4 = 6. Určite ste si všimli, že okrem toho, že zmenila veľkosť sa presunula o 2 doprava. Z toho vyplýva, že pri scalingu mení objekt veľkosť, ale aj polohu.

Kombinovanie matíc

Určite sa stretnete s prípadom, kedy budete potrebovať objekt najprv napríklad zväčšiť a zároveň rotovať. Z toho, čo sme si doteraz ukázali by ste najprv aplikovali maticu zväčšenia a potom rotácie. Efektívnejšie a rýchlejšie (hlavne pre procesor) je zlúčiť obidve matice dohromady a aplikovať už iba výslednú.

To, čo teraz nasleduje vyzerá dosť zložito. Preto nenechajte sa odradiť. Je to to isté, čo sme robili doteraz, len vo väčšom množstve.

Najprv zlúčime všetky matice do jednej. Budeme zlučovať 5 matíc: translácie, rotácieX, rotácie Y, rotácie Z a scalingu.

Nebudem to tu celé rozpisovať. Toto je výsledok. Dopracujete sa k nemu násobením všetkých 5 matíc. Ako násobíme (zlučujeme) dve matice ? Pozrite sa na obrázok: Prvý stĺpec druhej matice s každým riadkom prvej matice. Potom nasleduje druhý, tretí a štvrtý stĺpec.

Výsledný násobok vynásobíte s 3, 4 a nakoniec 5 maticou.

VB kód:

Private Function CreateMatrix(Rx As Single, Ry As Single, Rz As Single, Sx As Single, _
Sy As Single, Sz As Single, Tx As Single, Ty As Single, Tz As Single) As D3DMATRIX
Dim CosRx As Single, CosRy As Single, CosRz As Single
Dim SinRx As Single, SinRy As Single, SinRz As Single
CosRx = Cos(Rx)  'pouzite 6x
CosRy = Cos(Ry)  'pouzite 4x
CosRz = Cos(Rz)  'pouzite 4x
SinRx = Sin(Rx)    'pouzite 5x
SinRy = Sin(Ry)    'pouzite 5x
SinRz = Sin(Rz)    'pouzite 5x
With CreateMatrix
.m11 = (Sx * CosRy * CosRz)
.m12 = (Sx * CosRy * SinRz)
.m13 = -(Sx * SinRy)
.m21 = -(Sy * CosRx * SinRz) + (Sy * SinRx * SinRy * CosRz)
.m22 = (Sy * CosRx * CosRz) + (Sy * SinRx * SinRy * SinRz)
.m23 = (Sy * SinRx * CosRy)
.m31 = (Sz * SinRx * SinRz) + (Sz * CosRx * SinRy * CosRz)
.m32 = -(Sz * SinRx * CosRx) + (Sz * CosRx * SinRy * SinRz)
.m33 = (Sz * CosRx * CosRy)
.m41 = Tx
.m42 = Ty
.m43 = Tz
.m44 = 1#
End With
End Function

Táto funkcia je dokonca rýchlejšia ako vstavané D3Dx funkcie. Sínus a kosínus sú dosť náročné na procesorový čas, preto je výhodnejšie vypočítať ich iba raz a uložiť do premennej. Pri každom daľšom použití sa nemusí znova prepočítavať.

Matrix Identity

Doteraz som komentoval tieto dve slová ako reset matice. Čo to pre nás znamená ? Vychádzajme z matice pre zmenu veľkosti. Dosaďte za Sx, Sy a Sz jednotku. Znamená to, že veľkosť ostala identická (z toho ten názov). Potom matica bude vyzerať takto:

Keď ktorýkoľvek bod vynásobíme touto maticou dostaneme ten istý bod. Napriek tomu má veľký význam: Možno ste sa už počas čítania zamýšľali nad tým, ako dosiahnuť to, aby jeden objekt napr. rotoval a druhý ostal v pôvodnej polohe.

Postup:

1. Zobrazíte objekt1 a použijete rotačnú maticu.

2. Použijete Matrix Identity, čím resetujete maticu a potom zobrazíte objekt2.

Keby sme nepoužili Matrix Identity rotovali by obidva objekty.

Na záver už len dodám, že tieto poznatky môžete využiť aj v 2D prostredí. Samozrejme vynecháte Z rozmer a použijete maticu 3 x 3.

Priložené súbory: