Direct 3D - Animácia
V predchádzajúcich častiach ste sa naučili ako poskladať objekt z trojuholníkov, otextúrovať ich alebo priamo nahrať z X súboru. Ďalším krokom k realite je “oživenie” postáv. Ako na to si ukážeme v tejto časti.
Typy a spôsoby animácií
Animácia je založená na nedokonalosti ľudského zmyslu. Ak vidíme postupne sériu statických obrázkov v rýchlom slede, vnímame ich ako pohyb. Každý takýto obrázok nazývame frame (snímok). Animovať objekt znamená meniť jeho parametre. V zmysle tejto definície môžeme animovať nielen pozíciu, ale aj tvar, farbu, textúru atď.
S animáciami sme sa už stretli v predchádzajúcich kapitolách, kde sme presúvali a rotovali celé objekty za pomoci matíc. Aj to je jeden z možných spôsobov ako animovať, málokedy však postačí pri zložitejších scénach. Z ďalších alternatív si spomeňme napr. lineárnu interpoláciu, sférickú interpoláciu a inverznú kinematiku. Pri konkrétnej animácii sa zvyčajne používa jeden spôsob alebo sa spôsoby navzájom kombinujú (napr. lineárna so sférickou interpoláciou).
Animácie by som potom rozdelil do dvoch základných kategórií:
- frame by frame (snímok za snímkom)
- tweened animation
- kľúčová animácia (keyframe)
- animácia pomocou kostí (bones)
Do kategórie tweened animation som zaradil iba 2 najčastejšie používané typy. Okrem nich existuje samozrejme ešte množstvo iných typov, obvykle odvodených od základných (napr. 3DStudio používa upravenú kľúčovú animáciu). V tejto časti si povieme zopár informácií iba o kľúčovej animácii. Animáciu pomocou kostí zatiaľ odložíme na neskôr.
Podľa toho ako sa spáva animácia v čase rozdeľujeme animácie na:
- časové - dĺžka animácie a udalosti v nej sú závislé na čase.
- framové - dĺžka animácie a udalosti v nej sú závislé od framov.
Časová animácia skončí v presne stanovenom čase (napr. po 20 sekundách), kdežto framová až po zobrazení daného počtu framov. Z uvedeného vyplýva, že u framovej animácii záleží na rýchlosti akou je schopná grafická karta vyrenderovať jednu scénu (frame). Čím kratšie renderovanie trvá, tým rýchlejšie animácia pobeží. V praxi sa moc často nepoužíva, pretože na rôznych počítačoch sa prehráva rôzne rýchlo. Môžete ich nájsť v niektorých starých hrách, ktoré s príchodom rýchlejších počítačov boli kvôli veľkej rýchlosti nehratelné. Ďalej existujú zmiešané typy, ktoré majú vlastnosti z časovej aj framovej animácie. Napr. animácie v Macromedia Flash skončia až po prehraní určitého počtu framov. Každý ďalší frame je však zobrazený až po uplynutí určitého času.
Snímok za snímkom
Je najjednoduchším typom animácie. Pre každý stav objektu sa vytvorí jeden snímok. Tie sa potom rýchle za sebou prehrávajú, tak aby vytvorili dojem plynulej animácie. Takýto typ sa využíva napr. vo filmoch, kde sa každú sekundu premietne približne 24 obrázkov. V hernom priemysle našla uplatnenie pri 2D hrách (plošinovky,2D stratégie) ako jediná možná animácia (pokiaľ nerátam základné transformácie celého objektu). Tak napr. pre animáciu bojovníka sa vytvoria sady dvadsiatich obrázkov pre pohyb, útok, obrana a pre stav nečinnosti objektu. Potom podľa toho, čo postavička práve robí, sa prehráva príslušná sada animácii. V 3D priestore je takmer nepoužiteľná. Ak by sme používali podobné sady modelov (1 snímok = X súbor) s veľkosťou 50 kb pre 200 snímkov dostaneme 10 MB dát na animovanie jednej postavičky. Hlavným nedostatkom je preto obrovská náročnosť na pamäť. Na druhej strane je najrýchlejšou animáciou. Ak by sme pri spustení programu nahrali všetky X súbory do pamäte, potom stačí renderovať jeden po druhom bez akýchkoľvek výpočtov. Pozrite si zjednodušenú schému takej animácie:
'Nahráme všetky snímky Private Sub Load() Dim Model(99) as D3DXMesh, I as Integer For i = 0 To 98 Set Model(i) = LoadMeshFromX (i-ty X subor) Next i End Sub Private Sub Render() Model(AktualFrame).Render AktualFrame = AktualFrame + 1 'Previnieme animáciu na začiatok if AktualFrame > 98 then AktualFrame = 0 End Sub
Na prvý pohľad je jasné, že ide o framovú animáciu. Hneď ako je vyrenderovaný jeden model, začína sa renderovať ďalší.
Kľúčová animácia
Kľúčová animácia patrí do kategórie tweened animation, ktoré majú jednu spoločnú vlastnosť: Vytvoríte začiatočný a koncový frame a zvyšné framy (medziframy - tweens) sa počas animácie dopočítajú. Takéto framy, ktoré sú oporou pre výpočet zvyšku animácie, sa nazývajú klúčové. Spravidla sú to tie snímky, na ktorých sa objekt nachádza vo “vrcholovom” stave. Aby sme si ujasnili o aký stav ide, predstavte si spomalenú animáciu rozsvietenia žiarovky. Táto animácia má dva vrcholové stavy: 1. tma a 2. svetlo. Celý proces rozžhavovania vlákna medzi dvoma vrcholovými stavmi je možné jednoducho dopočítať. Uvedomte si, že pri výpočte framov len predpokladáme v akom stave by sa mohol objekt v danom čase nachádzať. Ak na začiatku svietila žiarovka intenzitou i = 0 a na konci i = 100, potom je pravdepodobné že v strede animácie je intenzita rovná 50 (graf č.1). Ale čo ak práve v tom čase vypneme prúd a po chvíli ho opäť zapneme? Počiatočný a koncový stav sa neznení, ale už stred nezodpovedá nami vypočítanej hodnote. V takom prípade pridáme ďalšie klúčové snímky tak, ako ukazuje graf č.2.

Správny počet a umiestnenie keyframov je veľmi dôležité, aby sa vypočítaná animácia čo najviac podobala želanej. Medziframi je možné vypočítať iba z najbližších keyframov, medzi ktorými ležia. To znamená, že stred animácie sa odvodzuje od keyframov 3 a 4 a nie napr. 3 a 5. Spôsobov ako vypočítať medziframi je hneď niekoľko. Ten najjednoduchší má názov lineárna interpolácia. Vychádza veľmi jednoduchého vzorca:
Vysl = A + t.(B-A)
A - počiatočná pozícia
B - koncová pozícia
t - interpolant
Prechádzajúce dva grafy sú tiež výsledkom lineárnej interpolácie. Počiatočný keyframe lineárne (po priamke) interpoluje k nasledujúcemu. S touto technikou sme sa už stretli v lekcii Point sprites, kde sa interpolovali 2 farby. Interpolant t je z intervalu <0,1> Čím je interpolant vyšší, tým viac sa medziframe podobá stavu B. Naopak, pri malom interpolante sa medzistav blíži k stavu A. Keď sa vrátime k prvému príkladu so žiarovkou môžeme intenzitu v strede animácie vypočítať nasledovne:
Vysl = 0 + 0,5 . (100-0) = 50
Prenesme sa teraz do DirectX. Budeme animovať postavu ženy pri cvičení :).
Klúčovými snímkami budú X súbory, v ktorých sú uložené rôzne vrcholové polohy pri cvičení. Našou úlohou je vytvoriť lineárny prechod medzi tymito polohami. Takýchto polôh som vytvoril päť. Pri docielení poslednej sa animácia previnie na začiatok.

Pravdepodobne vás v tejto chvíli napadá otázka, ako použiť model nahratý z X súboru vo vzorci lineárne interpolácie. Asi vás sklamem, ale nie je možné interpolovať 2 modely. Neexistuje dokonca ani žiadna funkcia v DX8. Vieme však interpolovať vertexy, z ktorých je model zložený. Uveďme si príklad:
Vertex s pozíciou A[0,2,2] chceme interpolovať do polohy B[10,4,6].Kde sa bude vertex nachádzať v strede animácie?
Vertex.x = A.x + t*(B.x - A.x) = 0 + 0.5 * (10-0) = 5
Vertex.y = A.y + t*(B.y - A.y) = 2 + 0.5 * (4-2) = 3
Vertex.z = A.z + t*(B.z - A.z) = 2 + 0.5 * (6-2) = 4
Výsledná pozícia je [5,3,4]. Podobne môžeme vypočítať aj RBG zložky farby, normálové vektory a tu,tv súradnice.
'Linearna interpolacia medzi zdrojovym a cielovym vertexom LinearInterp.X = (Source.X) + Interpolant * (Dest.x - Source.X) LinearInterp.Y = (Source.Y) + Interpolant * (Dest.y - Source.Y) LinearInterp.Z = (Source.Z) + Interpolant * (Dest.z - Source.Z)
DirectX v mnohých veciach uľahčuje programátorovi prácu. Nie je tomu inak ani pri práci s modelmi. Pomocná knižnica D3DX obsahuje funkcie D3DXMeshVertexBuffer8GetData a D3DXMeshVertexBuffer8SetData na extrahovanie a opätovné vloženie vertexov z a do modelu (D3DXMesh).
Call D3DXMeshVertexBuffer8SetData(kModel, 0, Len(pole(0)) * VertexPocet, 0, pole(0)) call D3DXMeshVertexBuffer8GetData(kModel, 0, Len(pole(0)) * VertexPocet, 0, pole(0)))
D3DXMeshVertexBuffer8GetData
D3DXMeshVertexBuffer8GetData skopíruje vertexy z modelu do poľa
D3DXMeshobj - štruktúra D3DXMesh, z ktorej sa extrahujú vertexy
Offset - bajt v štruktúre, od ktoré sa má začať čítať
Size - veľkosť extrahovaných vertexov v bajtoch
Flags - vlajky pre uzamykanie bufferu
Data - prvý prvok poľa, ktoré bude naplnené vertexami
D3DXMeshVertexBuffer8SetData
D3DXMeshVertexBuffer8SetData skopíruje vertexy z poľa do modelu
D3DXMeshobj - štruktúra D3DXMesh, do ktorej sa skopírujú vertexy
Offset - bajt v štruktúre, od ktoré sa má začať zapisovať
Size - veľkosť kopírovaných vertexov v bajtoch
Flags - vlajky pre uzamykanie bufferu
Data - prvý prvok poľa, z ktorého sa bude čítať
Tymito funkciami potom vydolujeme vertexy z každého keyframu a uložíme ich do samostatných polí. Odtiaľ sa už dá ľahko interpolovať medzi keyframami. Zoberieme si dve polia s vertexami susedných modelov a každý jeden vertex interpolujeme. Pod susednými rozumieme keyframi vedľa seba na časovej osi. Ako sa na začiatku spomínal nemôžeme interpolovať napr. 3 a 5 keyframe. Podmienkou celého tohto procesu je, aby bolo v oboch poliach rovnako veľa prvkov. Mohlo by sa nám stať, že niektorý z vertexov prvého poľa nebudeme mať “partnera” z druhého poľa a program nám zhavaruje.
Poslednou úlohou je vypočítať interpolant. Pri framovej animácii je to veľmi jednoduché. Interpolant budeme zvyšovať o určitú konštantu pri každom vyrenderovanom frame. Ak chceme napr., aby postava zdvíhala ruku počas 30 framov, potom je konštanta rovná 1/30. Na začiatku sa interpolant rovná 0/30 a na konci 30/30. Pri časovej je to trochu zložitejšie. Interpolant sa mení v závislosti na čase.

Ak chceme vypočítať interpolant prvého a druhého kľúčového snímku v čase t1, musíme vydeliť čas, ktorý ubehol od začiatku prvého framu celkovým časom medzi nimi.
'vypocet interpolantu Dim CelkFrameCas As Long, AktFrameCas As Long 'dlzka casu medzi dvoma susednymi KeyFramami CelkFrameCas = kluc(DalsiKluc).Cas - kluc(AktualKluc).Cas 'cas od zaciatku aktualneho KeyFramu AktFrameCas = AktCas - kluc(AktualKluc).Cas Interpolant = AktFrameCas / CelkFrameCas
V niektorých prípadoch je však lineárnej interpolácia nepoužitelná. Asi ťažko by ste mohli animovať pohyb veľkej ručičky na hodinkách takýmto spôsobom. V takom prípade je lepšie použiť sférickú interpoláciu. Jej základom sú quaterniony, o ktorých sa dozviete viac až v ďalšej lekcii.
Na záver si povedzme, ako by sme mohli zvýšiť výkon našej animácie. Pre tých, ktorí majú lepšie grafické karty s jednotkou T&L (Transform & Lighting) si môžu zapnúť hardwarovú transformáciu vertexov jednoduchým nastavením parametru D3DCREATE_HARDWARE_VERTEXPROCESSING pri vytváraní D3DDevice. Rozdiel v mojom prípade je očividný - 658 fps oproti 380 fps. Druhá možnosť je vytvoriť DLL knižnicu v C-čku, ktorá by robila výpočty pri interpolovaní, keďže VB nie je zrovna moc rýchly programovací jazyk.