Mendelova zemědělská a lesnická univerzita v Brně Provozně ekonomická fakulta
Práce s grafickými 3D formáty v prostředí virtuální reality Bakalářská práce
Vedoucí práce: Ing. David Procházka
Pavel Žák
Brno 2008
Na tomto místě bych rád poděkoval vedoucímu mé práce Ing. Davidu Procházkovi za jeho zájem a konstruktivní připomínky.
Prohlašuji, že jsem tuto bakalářskou práci vyřešil samostatně s použitím literatury, kterou uvádím v seznamu.
V Brně dne 22. května 2008
....................................................
Abstract Žák, P. Working with graphics 3D formats in virtual reality space. Bachelor thesis. Brno, 2008. This thesis describes a process of development of an application capable of 3D models loading, visualization and manipulation. It is focused on settings of stereoscopic projection and connection with tracking devices. Samples of C++ source code using the OpenGL API are included.
Abstrakt Práce s grafickými 3D formáty v prostředí virtuální reality. Bakalářské práce. Brno, 2008. Práce popisuje postup při tvorbě aplikace schopné načítání a zobrazení grafických 3D modelů a manipulace s nimi. Zabývá se nastavením kamer pro stereoskopickou projekci a přístupem k údajům z trackovacích zařízení. Obsahuje ukázky zdrojového kódu v jazyce C++ s využitím OpenGL.
4
Obsah 1 Úvod a cíl práce 1.1 Cíl práce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7 7
2 Technologické řešení virtuální reality 9 2.1 Stereoskopická projekce . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.2 Snímání pohybu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 3 OpenGL 3.1 Alternativní API . . . 3.2 Nadstavbové knihovny 3.3 Kamera . . . . . . . . 3.4 Double-buffering . . . 3.5 Vstupní zařízení . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
4 Popis 3D scény 4.1 Grafické formáty . . . . . . . . . . . . 4.1.1 Quake 3 modely . . . . . . . . . 4.2 VRML . . . . . . . . . . . . . . . . . . 4.3 Přehled nejpoužívanějších 3D formátů
. . . . .
. . . .
. . . . .
. . . .
. . . . .
. . . .
. . . . .
. . . .
. . . . .
. . . .
. . . . .
. . . .
. . . . .
. . . .
. . . . .
. . . .
. . . . .
. . . .
. . . . .
. . . .
. . . . .
. . . .
. . . . .
. . . .
. . . . .
. . . .
. . . . .
. . . .
. . . . .
. . . .
. . . . .
. . . .
. . . . .
11 11 12 13 13 14
. . . .
15 15 16 17 17
5 Metodika 18 5.1 Nastavení kamer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 5.2 Zpracování trackerů . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 6 Implementace 6.1 Inicializace knihovny GLUT . . . . . . . . . . 6.2 Práce s buffery . . . . . . . . . . . . . . . . . 6.3 Správa objektů ve scéně . . . . . . . . . . . . 6.4 Interní datové struktury . . . . . . . . . . . . 6.4.1 Vektory . . . . . . . . . . . . . . . . . 6.4.2 Statické 3D modely . . . . . . . . . . . 6.4.3 Animované 3D modely . . . . . . . . . 6.4.4 Uzly . . . . . . . . . . . . . . . . . . . 6.5 Pohyb 3D prostorem . . . . . . . . . . . . . . 6.6 Nastavení kamer pro stereoskopickou projekci 6.7 Výpis textu . . . . . . . . . . . . . . . . . . . 6.7.1 Zobrazení FPS . . . . . . . . . . . . . 6.8 Import modelů . . . . . . . . . . . . . . . . . 6.9 Keyframe animace . . . . . . . . . . . . . . . 6.10 Trackery . . . . . . . . . . . . . . . . . . . . . 7 Závěr
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
20 20 20 21 22 22 22 23 24 25 26 27 27 28 29 30 32
5
8 Literatura
33
Přílohy
34
A Diagram tříd
35
B Screenshoty aplikace
37
6
1
Úvod a cíl práce
Věrné napodobení reality je jedním z největších cílů grafiků a programátorů posledních let. S narůstajícím výkonem grafických akcelerátorů úměrně rostou i počty polygonů v zobrazovaných scénách, vícejádrové procesory zase umožňují aplikaci pokročilých simulací fyzikálních jevů. Stále jsme však spoutáni omezenými možnostmi periferních zařízení. Pojem virtuální realita v sobě zahrnuje daleko víc než jen obraz na monitoru. Jde o navození pocitu prostoru, skutečného vnímání hloubky a zejména pak interakci s umělým prostředím. Je zřejmé, že běžná obrazovka a šipky na klávesnici nebudou při hledání prostředků k dosažení zmíněného cíle ideálním řešením. Problém simulace reality je nutné rozložit na dvě části – prostorové vnímání a manipulaci s virtuálním prostorem. Lidské oko vnímá svět dvourozměrně, plastický pohled získává mozek vyhodnocením rozdílů v obrazech z obou očí. Nejjednodušším způsobem jak simulovat prostorový dojem je tedy zajistit, aby byla ke každému oku doručena nezávislá informace. Interakcí s prostředím se rozumí možnost uživatele ovlivňovat dění ve virtuálním světě. Aby měl ovšem aktér z celé scény pocit autentičnosti, musí být tato schopnost naprosto intuitivní. Běžné ovládací prvky počítají s pohybem pouze ve dvou osách, jsou proto pro tento účel nevhodné. V současnosti se k zadávání vnějších podnětů používají polohové senzory, tzv. trackery. Jedná se o specializovaná zařízení, která odesílají počítači údaje o svém náklonu ve všech třech osách a vzdálenosti od kalibračního zařízení. Příslušná aplikace z daných údajů vyhodnotí polohu vůči středu promítací plochy a místo, na které tracker ukazuje. Pomocí senzoru v brýlích tak můžeme například pozorovateli umožnit sledovaný objekt prohlédnout z více stran prostým natáčením modelu podle pozice hlavy.
1.1
Cíl práce
Jak je vidět, technologická omezení nejsou již dnes nepřekonatelným problémem. Na většinu otázek máme uspokojivé odpovědi, zbývá neméně zajímavá část – implementace navrženého řešení. Cílem této práce je tedy vytvoření aplikace, která umožní přirozenou interakci s virtuálním prostředím. Celé řešení lze dekomponovat do dvou subsystémů. Jádrem aplikace se stane upravený 3D engine, který bude mít na starosti načítání modelů, práci s texturami a vykreslování. Od klasických systémů se bude lišit právě vykreslováním, neboť každou scénu musí zachytit dvakrát a z různých úhlů. Druhou součást bude tvořit rozhraní pro komunikaci s uživatelem. V praxi to znamená zpracování podnětů z trackerů a vyhodnocení adekvátní reakce. Podněty můžeme rozdělit na aktivní a pasivní. Aktivní vyvolává uživatel vědomě ovládacím zařízením v ruce. Pasivní se vztahují k trackeru na 3D brýlích a dále upravují celou scénu na základě polohy hlavy. 7
Cílem tohoto textu je popsat na funkční modelové aplikaci možnosti a úskalí současných systémů na simulaci reality. Není účelem vytvořit plnohodnotný herní engine, ale pochopit princip fungování výše zmíněných moderních technologií.
8
2 2.1
Technologické řešení virtuální reality Stereoskopická projekce
Samotný princip trojrozměrné stereoskopie není nijak složitý. Oproti klasické projekci, kdy divák vnímá jeden obraz oběma očima zároveň, stereoskopický systém generuje dvojici nezávislých obrazů. Pozorovaný objekt je zachycen párem kamer, jejichž rozteč přibližně odpovídá vzdálenosti lidských očí. Celá scéna tedy vzniká podobně, jako by se na ni díval člověk. Při následné projekci je nezbytné, aby se ke každému oku diváka dostal pouze příslušný obraz. Pokud je tato podmínka splněna, mozek vyhodnotí výsledek plně trojrozměrně a vyvolává v pozorovateli pocit přítomnosti přímo v místě dění. Technologické problémy spojené s distribucí obrazu lze řešit hned několika způsoby. Obvykle je třeba zvážit, pro jaký účel a počet pozorovatelů bude prezentace určena, a na základě toho vybrat nejvhodnější metodu. Například nejjednodušší anaglyfové zobrazení, tedy zobrazení pomocí brýlí s barevnými očnicemi, se díky nízkým pořizovacím nákladům hodí pro projekce s velkým počtem diváků. Na druhou stranu ztráta barevné informace omezuje účel použití snad jen na reklamu či zábavní průmysl. Aktivní zobrazení je metoda nepoměrně sofistikovanější, kvalita zobrazení je však vykoupena vyšší cenou brýlí a tím i omezením počtu diváků. Obraz je zde promítán na plátno či CRT monitor s dvojnásobnou snímkovací frekvencí, přičemž na filmovém pásu jsou střídavě proložené obrazy pro levé a pravé oko. Elektronické brýle diváka se pomocí infračerveného paprsku synchronizují se zdrojem vysílání a střídavě zakrývají oči. Je zřejmé, že snímkovací frekvence klesne na polovinu, nedojde však ke ztrátě barevné informace. Pasivní 3D projekce je založena na systému polarizačních filtrů. Dvojice projektorů promítá snímky současně na jednu plochu přes dvojici filtrů, z nichž jeden je otočený o devadesát stupňů. Nastavení filtrů na projektorech koresponduje s nastavením filtrů na brýlích. Do každého oka se tak dostane pouze obraz z příslušného projektoru. Toto řešení nabízí velmi kvalitní obraz při zachování plné barevné informace a zároveň je vhodné i pro větší počet diváků. Poslední v praxi využívanou metodou je zobrazení pomocí autostereoskopického monitoru. Zásadním rozdílem oproti předchozím metodám je, že není potřeba sledovat scénu žádnými brýlemi. Před LCD monitor se umístí speciální fólie, která láme světlo každého pixelového sloupce jiným směrem. V základní variantě je světlo lichých sloupců vychylováno vždy na jednu stranu a světlo sudých na druhou. V tomto případě vnímá člověk celou scénu korektně jen v případě, že drží hlavu ve správné poloze. Pokročilejší verze lámou světlo až devíti směry a scénu tudíž může pozorovat i více diváků současně (Autostereoskopické monitory, 2005). Při tvorbě modelové aplikace pro tento text bylo využito pasivní stereoskopické projekce.
9
2.2
Snímání pohybu
Podobně jako u stereoskopické projekce, i při sledování pohybu existuje více cest ke stejnému výsledku. Níže uvedené metody se liší zejména přesností, latencí, pořizovacími náklady a citlivostí na rušivé vlivy (Durlach, Mavor, 1995, s. 188). Mechanické senzory jsou dostupnou a přitom přesnou metodou, jejich použití se však omezuje zejména na snímání úhlů, které svírají končetiny. Nasazení v praxi tak najdou zejména v exoskeletonech, tedy zařízeních na získávání biometrických údajů celého těla. Reálné použití je poměrně složité, zejména kvůli fyziologické odlišnosti každého jedince. Zdaleka nejrozšířenější jsou trackery magnetické. Důvodem jsou nízké náklady na pořízení, dostačující přesnost a jednoduchost použití. Jsou také poměrně odolné proti rušivým vlivům prostředí, přestože se mohou vyskytnout problémy s magnetickými poli a feromagnetickými meteriály v okolí. Komerční trackery firmy Ascension Technology, použité při tvorbě modelové aplikace, jsou právě tohoto typu. Jejich rozlišovací schopnost je přibližně 2,5 mm a 0,5◦ , obnovovací frekvence 120 Hz, přičemž reálné latence se pohybují kolem 30 ms. Přesnost se může lišit v závislosti na vzdálenosti od kalibrační jednotky, nicméně při nasazení v běžné místnosti se jeví jako dostatečná. Dalším způsobem snímání polohy jsou optické senzory. V této oblasti existuje mnoho různých řešení, od triangulace – vyhodnocení pohybu objektu sledovaného dvojicí CCD kamer, přes sledování vzdálenosti staticky umístěným laserem, až po příjem infračerveného světla, vyzařovaného diodami na těle sledovaného objektu. Společným rysem všech těchto metod je bohužel zjevná technická náročnost a s ní spojené vyšší náklady. V neposlední řadě jsou stále ještě využívány trackery akustické. Byly vyvinuty pro potřeby robotiky v osmdesátých letech a představují levnější alternativu k magnetickým senzorům. Trojice mikrofonů měří dobu letu ultrazvukových vln z emitoru umístěného na sledovaném objektu. Touto metodou ovšem nelze měřit vzdálenosti kratší, než je vlnová délka použitého signálu (což je při 40 kHz kolem 7 mm). Při nasazení vyšších frekvencí se už silně projevují atmosférické ruchy. Tento způsob je tedy vhodný spíše pro nenáročné nasazení v zábavním průmyslu.
10
3
OpenGL
Knihovna OpenGL (Open Graphics Library) byla navržena jako aplikační programové rozhraní (API) pro tvorbu počítačové grafiky. Mezi její hlavní výhody patří nezávislost na použitém hardwaru či platformě, široká podpora ze strany výrobců grafických karet a použitelnost v mnoha programovacích jazycích (Tišnovský, 2003). Z programátorského hlediska se OpenGL chová jako stavový automat. Nastavení celé scény lze tedy měnit kdykoli v průběhu vykreslování a nové hodnoty zůstávají platné až do další explicitně volané změny. Díky tomuto přístupu mají definované funkce menší počet parametrů a celou scénu lze globálně změnit jediným příkazem. Každé vykreslované těleso se skládá z tzv. grafických primitiv. Do této kategorie patří bod, úsečka, trojúhelník, čtyřúhelník, konvexní polygon a černobílý a barevný rastrový obraz. Na jednotlivé vrcholy i celá primitiva lze aplikovat různé transformace (otočení, posun, změna měřítka) a snadno tak docílit změny objektu či animace. Zobrazovaný snímek se nejdříve vykresluje do obrazového rámce (framebufferu). Jedná se o mřížku se zvoleným rozlišením a barevnou hloubkou. Ve chvíli, kdy je celá scéna kompletní, dojde k odeslání barevné informace na obrazovku.
3.1
Alternativní API
Snaha o maximální nezávislost na použitém operačním systému přinesla OpenGL jeden nepříjemný vedlejší efekt, a sice absenci veškerých funkcí pro práci s okny, tvorbu grafického uživatelského rozhraní GUI, či zpracování událostí. Tyto funkce je třeba zajistit voláním systémového správce oken, případně použít některou z nadstaveb1 . OpenGL ovšem není jediné rozhraní pro tvorbu grafických aplikací. Za nejznámější konkurenční produkt lze bezpochyby označit Direct3D od firmy Microsoft. Je součástí API kolekce DirectX, která je vyvíjena pro platformy Microsoftu, v současné době tedy pro Windows, Xbox a Xbox 360. O jeho kvalitách svědčí široké nasazení, zejména v oblasti počítačových her. Při srovnání s OpenGL je vidět zcela odlišný přístup jeho tvůrců. DirectX se může pochlubit překotným vývojem a množstvím verzí. Výrobci grafických akcelerátorů proto deklarují vždy poslední plně podporovanou verzi (aktuálně 10.1). Programátor aplikace tedy určením verze hned ví, které funkce může použít. U OpenGL je situace složitější. Vývoj vlastního API prozatím stagnuje na verzi 2.1, ale moderní akcelerátory podporují množství rozšíření (extensions). Ta hardwarově implementují funkce, které by jinak bylo nutné programovat. Tvůrce aplikace ovšem neví, která rozšíření budou hardwarově podporována a je tak posta1
Při tvorbě modelové aplikace pro tuto práci byla použita nadstavbová knihovna GLUT (GL Utility Toolkit).
11
ven do složitější situace. Na druhou stranu OpenGL je rychlejší a zpravidla vyžaduje méně kódu pro dosažení stejného výstupu (OpenGL vedle DirectX stále žije, 2007). Obecně lze prohlásit, že Direct3D je úzce specializované, herní API. OpenGL oproti tomu vždy nacházelo využití spíše v profesionální sféře. Důvody jsou historické – specializované aplikace byly provozovány hlavně na stanicích Silicon Graphics, tvůrce standardu OpenGL. Microsoft s herním Direct3D nastoupil na místo 3Dfx a jejich zastarávajícího rozhraní Glide.
3.2
Nadstavbové knihovny
Nevýhodou obou zmíněných API je fakt, že je kvůli triviálním záležitostem potřeba napsat velké množství kódu. Toto řeší nadstavbové balíky, kterých existuje velké množství a obvykle značně usnadňují práci na úkor univerzálnosti. Jedním z nejpopulárnějších rozšíření pro DirectX je XNA přímo od Microsoftu (Keller, 2006). Jedná se o vývojový framework, který obsahuje množství tříd pro jednoduchou obsluhu textur, animací či zvuků. Po zvolení typu projektu (2D, 3D, Windows, Xbox) automaticky vytvoří jednoduchou šablonu, aby se programátor nemusel zabývat rutinními záležitostmi. Je dostupný jako plugin do Microsoft Visual Studia, nedávné uvolnění verze Express umožňuje využívat XNA zdarma v prostředí Microsoft C# Express Studia. U OpenGL je situace obdobná s tím rozdílem, že nadstavby bývají obvykle dostupné zcela volně. V oblasti profesionální grafiky se stalo standardem rozhraní Open Inventor. Jeho vývoj zahájila společnost Silicon Graphics počátkem devadesátých let a oficiálně ukončila v roce 1998 (Pečiva, 2003). Přesto ještě o dva roky později vydala open-source balík tohoto API ve verzi 2.1. Aktuálně se pracuje se systémy s Open Inventorem kompatibilními, mezi nejnovější patří například knihovna Coin od norské firmy Systems in motion, která je dostupná pod licencí GPL. Celá knihovna Open Inventor je psaná v C++ a její hlavní výhodou je rychlost. Většina implementovaných funkcí je totiž rychlejší než přímý kód v OpenGL. Toho bylo dosaženo pomocí kvalitních optimalizací, které jsou prováděny nad daty scény. Programátor má navíc k dispozici rozsáhlou sadu nástrojů pro zjednodušení práce. V poněkud odlišné situaci se nachází doplněk GLUT, zmíněný v předchozí kapitole. Jeho základem je totiž pouze podpora pro práci s okny, menu či písmem. Obsahuje i jednoduché implementace dalších funkcí, například časovače, ale už svým určením v žádném případě nemá konkurovat výše zmíněným nadstavbám. Mezi jeho výhody však patří miniaturní velikost a dostupnost pro většinu platforem a programovacích jazyků. Neposunuje programátora výše nad úroveň OpenGL, veškeré renderování a práci s texturami je třeba implementovat na úrovni grafických primitiv (GLUT - The OpenGL Utility Toolkit, 1997). Nevýhodou může být uzavřená hlavní smyčka programu, do které nemá tvůrce aplikace možnost zasahovat.
12
3.3
Kamera
Vykreslování v OpenGL je založeno na systému skládání transformačních matic. Modelová matice se používá pro manipulaci s objekty ve scéně. Projekční matice slouží k přepočítání perspektivy, aby se vzdálenější objekty jevily menší. Viewport matice slouží k převedení abstraktních souřadnic do souřadnic okna. A konečně texturová matice se používá při mapování textur na povrch objektů. Součinem těchto matic vznikne lineární transformace, která se aplikuje na všechny body v rovině nebo prostoru. Nastavení pozice pozorovatele se tedy s pomocí klasických OpenGL funkcí nedá provést přímo, je zapotřebí měnit některou z transformačních matic, nejčastěji modelovou. Výhodou tohoto uspořádání je absolutní kontrola programátora nad tím, co se se scénou děje. Nevýhodou je malý komfort při práci s kamerou. Z tohoto důvodu byly do OpenGL implementovány nové funkce, které měly tuto činnost usnadnit. Funkce glOrtho slouží k nastavení ortogonálního pohledu. Při tomto zobrazení není brán ohled na vzdálenost objektu od kamery. Tato funkce proto najde uplatnění spíše v technických aplikacích. Pro zobrazení perspektivy existuje funkce glFrustrum. Aby však bylo zobrazení korektní, je třeba nastavit perspektivu způsobem, který odpovídá optickým vlastnostem lidského oka. Obě uvedené funkce naneštěstí vycházejí z interních potřeb grafických akcelerátorů a jejich parametry jsou zvoleny bez ohledu na potřeby programátora. Řešení se objevilo s nadstavbou GLU (GL Utility Library), která primárně slouží k vytváření textur z bitmapových obrázků. Tato knihovnička je zřejmě nejčastěji používaným doplňkem k OpenGL a kromě jiného implementuje funkci gluLookAt, díky níž lze pomocí souřadnic kamery, souřadnic cílového bodu a normálového vektoru velmi snadno nastavit pozici a orientaci pozorovatele.
3.4
Double-buffering
Při každé změně polohy kamery, a tedy i transformačních matic, je třeba scénu překreslit. Překreslení zpravidla znamená přemazání obrazovky barvou pozadí a následné vykreslování grafických primitiv, na která byly aplikovány nové lineární transformace. Tato operace zabere určitý čas a během tohoto času by mohl uživatel na obrazovce sledovat problikávání způsobené postupnou změnou barev jednotlivých pixelů. Tomuto nepříjemnému jevu lze zabránit použitím druhého, tzv. zadního, bufferu. Zatímco přední buffer je zobrazen uživateli, do zadního se vykresluje. V okamžiku, kdy je celá scéna kompletní, dojde k záměně obou bufferů. Není přitom využita žádná obrazová paměť, jedná se o běžnou schopnost dnešních grafických akcelerátorů. Tato technika se jmenuje double-buffering.
13
3.5
Vstupní zařízení
Samotné OpenGL rozhraní nezahrnuje podporu pro běžná vstupní zařízení typu klávesnice nebo myš. Použitá nadstavba GLUT ovšem obsahuje vlastní smyčku pro zpracování systémových zpráv. Ta je pro programátora neviditelná a spouští se společně s hlavní smyčkou programu voláním funkce glutMainLoop. Pro zpracování událostí se používají tzv. callback funkce, což jsou vlastní funkce, předem definované, ale volané až momentě výskytu události. Typickým příkladem může být reakce na stisk klávesy. GLUT asynchronně (nezávisle na běhu programu) zavolá funkci určenou k obsluze klávesnice a parametrem předá ASCII hodnotu stisknuté klávesy. Obsluha myši probíhá obdobným způsobem, místo klávesy se parametrem předává index stisknutého tlačítka a pozice kurzoru v okně aplikace. GLUT dále nabízí funkce pro změnu či skrytí kurzoru myši a volání pro pasivní pohyb myši, tedy pohyb bez stisknutých tlačítek. Přestože je GLUT nezávislý na platformě a sám zvolí smyčku odpovídající použitému systému, některé jeho funkce mohou být systémem ovlivněny. Například při zvolení kurzoru znázorňujícího déle probíhající operaci se na platformě Windows objeví známé přesýpací hodiny. Stejný příkaz na počítačích Apple zobrazí hodiny ručičkové.
14
4
Popis 3D scény
Aby byl grafický engine použitelný v praxi, musí být schopen načíst informace o zobrazované scéně z externího souboru. Pro ukládání trojrozměrných modelů existuje celá řada standardů, proto širokou podporou formátů získává systém na univerzálnosti. Jednotlivé formáty zpravidla uchovávají obdobná data, liší se však způsobem zápisu. Obecně lze říci, že pro konstrukci modelu jsou nezbytné 3D souřadnice všech jeho vrcholů, tzv. vertexů, a seznam plošek, nejčastěji trojúhelníků, které jsou vertexy tvořeny. Tyto plošky (angl. face) jsou obvykle definovány pomocí indexů do pole vrcholů. Tento způsob zápisu zajišťuje, že je každý vertex uveden pouze jednou, čímž je redundance snížena na minimum. Pokročilé grafické formáty obsahují navíc informace o materiálech, tedy názvy textur, barvu, průhlednost či reakci na dopad světla, a člení celý model na podobjekty. Každému podobjektu je přiřazen právě jeden materiál.
4.1
Grafické formáty
Pravděpodobně nejjednodušším formátem pro zápis prostorových objektů je RAW. Jedná se o běžný textový soubor, ve kterém se na každý řádek ukládají souřadnice třech vrcholů formujících jednu plošku a její normálový vektor. Pokud není normála uvedena, dopočítává se automaticky. Výhodou tohoto formátu je možnost úprav v obyčejném textovém editoru a velmi jednoduchý import do grafického enginu. Nevýhodou je absence veškerých doplňkových informací. RAW se proto v praxi používá spíše pro testovací účely, případně pro implementaci vlastních grafických primitiv. Nepoměrně komplexnější je formát MilkShape 3D (MS3D) vytvořený švýcarskou společností chUmbaLum sOft pro herní engine Half-Life (Milkshape 3D, 2008). Kromě informací o materiálech obsahuje i údaje nezbytné pro skeletální animaci či keyframe animaci. Poměrně snadnou implementaci importu umožňuje otevřená specifikace tohoto formátu, což z něj činí mocný nástroj pro ukládání 3D modelů. Alternativou k MS3D může být 3DS, formát pro ukládání výstupu z aplikace 3D Studio (později 3D Studio MAX ). Přestože je patentovaný společností AutoDesk a nikdy nebyla uvolněna jeho kompletní specifikace, používá jej mnoho aplikací třetích stran. Široká škála informací, které může 3DS obsáhnout, znesnadňuje import do grafického enginu. Tato nevýhoda je však vyvážena snadnou dostupností volných modelů, která pramení z všeobecné obliby tohoto formátu. Dalším úspěšným formátem patřícím pod AutoDesk je MA (výstup z aplikace Maya). AutoDesk ho koupil společně s tvůrci Mayi, společností Alias Systems Corporation v říjnu 2005. O kvalitách a širokých možnostech tohoto standardu svědčí i udělené ocenění za dosažené výsledky na poli vědy a techniky za „využití prakticky všech vlastností počítačové 3D grafikyÿ z roku 2003 (Maya (software), 2008). 15
MA soubory mají na rozdíl od binárních 3DS podobu textovou. Dají se tedy přečíst a upravit v libovolném textovém editoru. Skutečně zajímavým počinem je COLLADA (Collaborative Design Activity). Tato vývojářská sekce měla původně definovat oficiální formát pro Sony PlayStation 3, řízení však převzalo průmyslové konsorcium Khronos Group s cílem vytvořit otevřený XML meziformát pro konverze 3D modelů (COLLADA overview, 2008). Pro nový standard DAE (Digital Asset Exchange) tak vznikla řada portovacích nástrojů, mezi jinými i pro MA a 3DS soubory. Někteří vývojáři ovšem fázi exportu z DAE přeskočili a implementovali do svých aplikací přímou podporu. Často se přitom jednalo o významné projekty – Google Earth, Adobe Photoshop či Unreal engine. DAE se tak neplánovaně stal jedním z nejvýznamnějších formátů dneška. 4.1.1
Quake 3 modely
Herní Quake 3 engine přinesl krom několika revolučních postupů i poměrně kvalitní formáty pro uchovávání 3D modelů. Jejich specifikace jsou volně dostupné, ostatně i zdrojový kód celého enginu známého též jako id Tech 3 byl zveřejněn pod licencí GPL. Značných změn oproti předchozí verzi se dostalo zejména animovaným MD3 modelům postav. Animace jsou opět řešeny pomocí tzv. keyframů, tedy krajních poloh modelu, mezi kterými se interpoluje v čase. Na rozdíl od pevných 10 keyframů za vteřinu u MD2 verze je zde však možné využít libovolný počet snímků, čímž je docíleno plynulejší animace s relativně malými nároky na paměť. Každý model je navíc rozdělen do několika nezávislých částí. Zpravidla se jedná o nohy, trup a hlavu, přičemž je možné pro každou z nich přiřadit nezávislou animaci. Spojování těchto částí probíhá v reálném čase. Druhým, a neméně významným, formátem id Tech 3 enginu je BSP. Tento typ souborů slouží pro ukládání levelů, tedy rozsáhlých lokací s množstvím textur a polygonů. Klíčové geometrické informace jsou řešeny standardní metodou jednorozměrného pole vertexů a trojúhelníků, tvořených indexy do pole vrcholů. Navíc se uchovávají názvy textur pro jednotlivé materiály a tzv. lightmapy. Jedná se o rastr nesoucí RGB informace o osvětlení dané lokace, aby ho nebylo třeba počítat dynamicky. Textura, vzniklá z tohoto rastru, je aplikována přes původní texturu pomocí techniky zvané multitexturing. Dojde tak ke značnému urychlení vlastního renderingu při zachování kvalitního vizuálního vjemu. V původní hře Quake 3 byly oba zmíněné druhy modelů z důvodu přehlednosti navíc archivovány včetně všech textur a dodatečných informací v souborech typu ZIP.
16
4.2
VRML
Z jazyků pro popis virtuální reality je nejrozšířenější standard VRML (Virtual Reality Modeling Language). Na rozdíl od výše zmíněných grafických formátů se hodí i pro ukládání rozsáhlých scén, ne pouze jednotlivých modelů. VRML definuje širší spektrum grafických primitiv, jednotlivé objekty tedy není nutné rozkládat na trojúhelníky. Běžně se pracuje i s objekty typu krychle, koule či kužel (Tišnovský, 2007). Toto uspořádání umožňuje úsporný zápis komplexních scén za cenu složitější implementace. VRML bylo ovšem původně zamýšleno jako otevřený standard pro přenos 3D modelů po internetu, kdy je datový objem primární. V současnosti VRML nahrazuje jeho mladší následovník X3D. Oproti svému předchůdci, který je založený na stromové struktuře Open Inventoru, přidává X3D podporu XML a nabízí pokročilejší programátorské rozhraní. Jak VRML, tak i X3D byly přijaty jako mezinárodní standardy ISO.
4.3
Přehled nejpoužívanějších 3D formátů
Tab. 1: Přehled nejpoužívanějších 3D formátů
Formát RAW MS3D 3DS MA COLLADA MD3 BSP VRML X3D
Aplikace – Milkshape 3D 3D Studio MAX Maya – Quake 3 Quake 3 – –
Hierarchie ukládání pole vrcholů v prostém textu soustava jednorozměrných polí vnořené oddíly – chunky soustava jednorozměrných polí XML soustava jednorozměrných polí vnořené oddíly – lumpy stromová struktura stromová struktura, XML
17
5 5.1
Metodika Nastavení kamer
Jak již bylo vysvětleno dříve, stereoskopická projekce vyžaduje pro každý snímek záběr ze dvou kamer. Principielně jde o jednoduchou záležitost, grafický akcelerátor však potřebuje aktivovat další obrazový buffer pro druhou kameru, podobně jako při výstupu na dva monitory. Při použití double-bufferingu tak získáváme celkem čtyři buffery. Označují se levý a pravý zadní pro vykreslování a levý a pravý přední pro zobrazenou scénu. Tato konfigurace je oficiálně podporována pouze u grafických akcelerátorů nVidia Quatro. Nová verze ovladačů slibuje podporu pro GeForce řady 6 a vyšší, při nasazení v praxi je ovšem silně nestabilní (testováno na GF7600GS). Před vlastním vykreslováním grafických primitiv je třeba kameru posunout po ose x o vzdálenost, která by ve virtuálním světě odpovídala polovině rozteče očí. Následuje vykreslení celé scény do levého zadního bufferu, posun kamery do pozice druhého oka a vykreslení stejné scény do pravého zadního bufferu. Rozdíl mezi obrazy je tak dán pouze posunem modelové matice. Je zřejmé, že výkon grafického akcelerátoru klesne ve stereo režimu přibližně na polovinu. Tomuto nepříjemnému jevu bohužel nelze nijak zabránit. Přestože teoreticky jde o tutéž scénu, posunem kamery dojde k přepočítání lineárních transformací, které se aplikují na každý vykreslovaný bod. Z hlediska OpenGL se tedy jedná o dva naprosto odlišné obrazy.
5.2
Zpracování trackerů
Při tvorbě aplikace popisované v této práci bylo využito dvojice magnetických trackerů od společnosti Ascension Technology. Jeden slouží ke snímání pozice pozorovatele a je upevněn na 3D brýlích. V tomto textu bude dále nazýván jednoduše tracker. Je nezbytný k tomu, aby mohl systém scénu přizpůsobit podle pozice hlavy pozorovatele. Každá výchylka z původní polohy znamená pohyb, který vyvolá posun kamery v příslušném směru. Cílem je, aby mohl pozorovatel vyobrazený objekt obcházet. Druhý plní funkci ovladače, nazývá se též wanda nebo controller. Namísto pozice v prostoru u něj zkoumáme bod, na který ukazuje. Jeho součástí je navíc trackball a trojice tlačítek. Jedná se o velmi intuitivní ovládací prvek, integrovaný trackball pak plní funkci kurzorových kláves. Vlastní trackovací systém zjišťuje pouze polohu trackeru a náklon wandy vzhledem ke kalibračnímu zařízení. Skutečně relevantní údaje, tedy poloha a orientace vzhledem k promítacímu plátnu, se dopočítávají softwarově. Na tento úkol byl vyhrazen samostatný počítač. Získaná data jsou po síti v reálném čase posílána na stroj, na kterém běží grafický engine.
18
Všechny zmíněné programové činnosti, tedy přepočet, odeslání, příjem a následné sdílení dat řeší aplikace trackd od společnosti Mechdyne. Jedná se o tzv. daemony, tedy aplikace běžící na pozadí systému. Serverová část vypočítá žádaná data a odesílá je přes registrované porty rychlým protokolem UDP. Klient údaje přijímá a ukládá je do paměti, odkud jsou dostupné prostřednictvím sdílených paměťových klíčů. Aby bylo možné získat hodnotu paměťového klíče, bylo nutné nejprve od společnosti Mechdyne získat programátorské rozhraní trackd API, které poskytuje sadu funkcí pro komunikaci s klientskou částí trackovacího systému. Tím je umožněno načítání aktuálních souřadnic trackerů do interních datových struktur. V modelové aplikaci bude implementována třída pro práci s kamerou. Tato třída bude implicitně zpracovávat podněty z myši a klávesnice, musí však rovněž poskytovat metody pro podporu alternativních vstupních zařízení. Při korekci souřadnic kamery pomocí klávesnice a myši je vhodné zvolit synchronní přístup. Kontrola vstupních zařízení je prováděna před zobrazením každého snímku a je tak dosaženo plynulého pohybu. V případě trackerů je však situace jiná, neboť je třeba vzít v úvahu nižší obnovovací frekvenci příslušných periferií. Zmíněné funkce pro kontrolu aktuálních souřadnic budou proto volány nezávisle na hlavní smyčce, tedy asynchronně pomocí časovačů. Konkrétní příklady práce s časovači se nacházejí v kapitole Implementace. Z pohledu třídy nastavující polohu kamery nezáleží na tom, jestli změnu souřadnic či orientace vyvolala klávesnice nebo tracker. Je tedy možné kameru ovládat myší i wandou současně, změny polohy vstupních zařízení pak budou mít kumulativní efekt.
19
6 6.1
Implementace Inicializace knihovny GLUT
Samotná nadstavba GLUT se postará o vytvoření okna pro vykreslování poté, co dojde k předání nezbytných parametrů. Klíčovou funkcí je v tomto případě glutInitDisplayMode, která aktivuje double-buffering, depth buffer a umožňuje použití dalších dvou bufferů pro stereoskopickou projekci. glutInitDisplayMode(GLUT DOUBLE|GLUT DEPTH|GLUT RGBA|GLUT STEREO); V modelové aplikaci je inicializace knihovny GLUT zajištěna konstruktorem hlavní třídy Engine. Parametry lze ovlivnit počáteční rozměry a titulek GL okna, nastavením stereoView na hodnotu true navíc dojde k aktivaci čtyř bufferů. Engine::Engine ( int* argc, char** argv, char windowTitle[]="GLUT 3D engine", int xSize=1024, int ySize=768, bool fullScreen=false, bool stereoView=false ); Dále je nezbytné předat GLUTu funkci, která je zodpovědná za vykreslování. V případě modelové aplikace je automaticky zvolena funkce display pro klasickou a displayStereo pro stereoskopickou projekci. Existence samostatné funkce pro stereoskopii je výhodná zejména z hlediska pozdější optimalizace výkonu. Třída engine také registruje implicitní funkci pro výpočet správné perspektivy, čímž nastaví projekční matici, a funkci idleFunc, která je volána při nečinnosti. Jejím obsahem je pouze příkaz pro novou iteraci hlavní smyčky programu. Pokud by tato funkce registrována nebyla, počet snímků za sekundu by v době nečinnosti klesal na nulu.
6.2
Práce s buffery
OpenGL, a tedy i celý GLUT, se chová jako stavový automat. Veškeré vykreslování, které probíhá, se odehrává v zadním bufferu až do volání funkce glutSwapBuffers. V případě stereoskopické projekce je však navíc nutné specifikovat, zda se jedná o levý či pravý zadní buffer, jinak by se neustále kreslilo do implicitního pravého bufferu. glDrawBuffer(GL BACK LEFT); Po odstranění neaktuálního obsahu lze vykreslovat. Pokud jsou využívány čtyři buffery, voláním funkce glutSwapBuffers dojde k záměně všech bufferů, tedy levých i pravých. Proto je v obou vykreslovacích podprogramech, tedy display i displayStereo, tato funkce volána pouze jednou, a to v samém závěru.
20
Obsáhlejší příklad práce s buffery se nachází v kapitole 6.6 – Nastavení kamer pro stereoskopickou projekci.
6.3
Správa objektů ve scéně
Nedílnou součástí třídy Engine je SceneManager. Uchovává informace o všech modelech ve 3D světě a obstarává vlastní vykreslování. Objekty jsou ukládány v dynamických polích pomocí šablony std::vector. Z každého modelu se po načtení do interních datových struktur stává uzel – instance některého z potomků třídy Node. Každý uzel má svou polohu, orientaci v prostoru, barvu a průhlednost. Výjimkou jsou instance třídy TextNode, která slouží pro výpis textu na obrazovku. V tomto případě se využívá ortogonální projekce a textové modely proto nemají orientaci a jejich poloha je vyjádřena dvojicí celočíselných hodnot oproti vektoru tří desetinných hodnot ostatních uzlů. Pro přídání konkrétního typu uzlu slouží metody typu addNode, které vrací ukazatel na nově přidaný uzel, v případě neúspěchu pak hodnotu NULL. Pomocí tohoto ukazatele je možné dále ovlivnit uvedené atributy třídy Node či pracovat s vlastním modelem. BSPLevelNode* level = engine->sceneManager->addBSPLevelNode("a.bsp"); if (level) { level -> position.y = -300.0f; } Při vykreslování je pak každý model nejprve přesunut do zadané polohy a natočen podle zvolené orientace. Tyto transformace je však nutno kombinovat s pozicí kamery, aby bylo možné se ve 3D světě pohybovat. První verze průvodní aplikace se před vlastním vykreslováním každého modelu vracela na počátek soustavy souřadnic voláním funkce glLoadIdentity. Následovala kumulativní aplikace dvou transformací – poloha a orientace kamery a tytéž vektory pro právě vykreslovaný model. Toto řešení, ač funkční, se ukázalo být nevhodné pro stereoskopickou projekci, kdy dochází k další komplikaci související s posunem kamery do pozice levého či pravého „okaÿ. V tomto případě je výhodnější provést transformaci na základě pozice kamery pouze jednou a takto vzniklou matici použít jako výchozí stav pro každý z modelů. OpenGL má pro tento postup připraven zásobník matic. K práci s touto datovou strukturou jsou určeny funkce glPushMatrix a glPopMatrix. Ve vykreslovacím cyklu je tak nejdříve modelová matice uložena do zásobníku, poté jsou aplikovány transformace související s aktuálním modelem a po vykreslení je výchozí matice obnovena. Následující ukázka je součástí metody SceneManager::drawAll().
21
for (i = 0; i < numModels; i++) { glPushMatrix(); glTranslatef( pModels[i]->position.x, pModels[i]->position.y, pModels[i]->position.z); pModels[i]->mesh.draw(); glPopMatrix(); }
6.4 6.4.1
Interní datové struktury Vektory
V zájmu zjednodušení práce programátora je výhodné používat v celé aplikaci jednotné datové struktury. Vektory jsou uspořádané množiny hodnot, v rámci této práce byly implementovány následující typy: Tab. 2: Implementované typy vektorů
Název Vector2f Vector3f Color4f Vector2i Vector3i
Význam 2 desetinné hodnoty 3 desetinné hodnoty 4 desetinné hodnoty 2 celočíselné hodnoty 3 celočíselné hodnoty
Použití texturové koordináty 3D souřadnice a orientace v prostoru RGBA – barva a průhlednost 2D souřadnice bez využití
Pro strukturu Vector3f jsou rovněž dostupné základní vektorové operace jako skládání, úprava velikosti, výpočet normály apod. 6.4.2
Statické 3D modely
Třída Model slouží jako základní šablona pro 3D modely bez animací. Sdružuje v sobě podobjekty a informace o materiálech. Každý podobjekt pak obsahuje právě jednu celočíselnou hodnotu materialIndex, která slouží jako odkaz do pole materiálů. class Model { protected: int numMeshes; int numMaterials; std::vector<Mesh> pMeshes; std::vector<Material> pMaterials;
22
public: Model(); ~Model(); bool loadModel(const char *filename); void draw(); }; Celočíselné proměnné numMeshes a numMaterials udávají počet podobjektů a počet materiálů a odpovídají velikosti vektorů pMeshes, resp. pMaterials (pole podobjektů a pole materiálů). Konstruktor zajišťuje inicializaci struktur při vytváření modelu a destruktor zase korektní uvolnění paměti. Metoda draw je univerzální vykreslovací funkcí, vykresluje objekt po trojúhelnících a obsahuje podporu textur. Funkce loadModel je importér pro načtení modelu ze souboru a v šabloně Model implementována není, neboť je unikátní pro každý typ modelu. Díky tomuto systému může programátor snadno přidávat podporu pro další typy modelů prostým definováním vlastní importovací metody. Modul pro import 3D souboru pak vypadá například takto: class MS3DModel : public Model { public: bool loadModel(const char *filename); }; Funkcí importéru je tedy převést data uložená v souboru na interní datové struktury odpovídající šabloně. Poté je možné vykreslovat model standardní metodou draw. 6.4.3
Animované 3D modely
Šablona pro animované modely AnimatedModel rovněž vychází z třídy Model, ale obsahuje navíc informace týkající se keyframe animací. class AnimatedModel : public Model { protected: int numAnimations; // pocet animaci int currentAnim; // odkaz do pole animaci int currentFrame; // aktualni frame animace int nextFrame; // frame do ktereho interpolujeme float t; // prubeh interpolace 0 <= t <= 1 float lastTime; // uplynuly cas std::vector
pAnimations; };
23
Tato šablona přibližně odpovídá návrhu Quake modelů MD2. V modelové aplikaci je použita jako předloha pro části modelů MD3 (nohy, trup a hlava). MD3 modely však vyžadují komplexnější přístup při vykreslování, a proto nejsou v této třídě obsaženy žádné metody. Veškeré obslužné funkce je třeba volit individuálně. Pomocná struktura Animation obsahuje pouze název animace, první a poslední frame a rychlost přehrávání udanou ve snímcích za sekundu. Veškeré geometrické informace – souřadnice vertexů – jsou uloženy v jednorozměrném poli. U animovaných modelů je tedy v každém snímku vykreslena pouze část pole vrcholů. V případě, že animovaný model vykreslujeme poděděnou metodou draw, model se zobrazí staticky, resp. zobrazuje se stále pouze první snímek animace. 6.4.4
Uzly
Uzlem se stává každý objekt zahrnutý ve SceneManageru, který je zobrazován ve scéně. Vzniká zpravidla explicitním voláním metod typu addNode a zaniká ukončením hlavní smyčky aplikace. Každá addNode metoda v případě úspěšného přidání objektu do scény vrací ukazatel na tento objekt. Pomocí něj je možné s objektem dále pracovat, není však nutné jej uchovávat až do konce hlavní smyčky. Systém destruktorů je navržen tak, aby při skončení aplikace nedocházelo k paměťovým únikům. Předlohou každého uzlu (kromě textového) je třída Node. class Node { public: Vector3f position; Vector3f orientation; Color4f color; Node(); }; Jedná se o základní šablonu, změnou parametrů je možné ovlivnit barvu či pozici ve virtuálním prostoru. Potomci této třídy navíc obsahují ukazatel na specifický objekt – statický model, animovaný model či celý BSP level. Textové uzly jsou specifické v tom, že nemají vektor určující orientaci ani hloubku v prostoru. Namísto ukazatele na objekt obsahují řetězec, který lze v průběhu vykreslování měnit a aktualizovat tak vypisované informace. class TextNode { public: Vector2i position; Color4f color; char* text; TextNode(char *txt); };
24
Tabulka 3 slouží jako přehled všech implementovaných uzlů společně s typem objektu, na který mohou ukazovat. Tab. 3: Přehled implementovaných uzlů
Název TextNode MS3DModelNode DSModelNode MD3ModelNode BSPLevelNode
6.5
Objekt char MS3DModel DSModel MD3Model BSPLevel
Význam prostý text bez diakritiky statické Milshape modely statické 3DS modely animované MD3 modely Quake BSP levely
Pohyb 3D prostorem
Jak již bylo zmíněno v kapitole Kamera, nastavení pozice pozorovatele lze v OpenGL provádět výhradně změnou transformačních matic. Posun a orientaci kamery lze nejjednodušeji řešit pomocí funkce gluLookAt, která vypočítá transformace ze souřadnic kamery, cílového bodu a tzv. ypsilon vektoru, což je osa směřující kolmo vzhůru. Všechny metody provádějící posun kamery ke své funkci využívají pomocný vektor direction, který směřuje z bodu position do bodu target. Před každým pohybem kamery se tento vektor normalizuje, aby jeho velikost neměla vliv na rychlost pohybu kamery. Pokud nyní neuvažujeme stereoskopickou projekci, posun cílového bodu po polopřímce udané vektorem direction nemá vliv na polohu ani orientaci kamery. Ke zpracování podnětů z klávesnice a myši slouží třída Camera, konkrétně metody setView a checkMovement. Jedna instance třídy Camera je zahrnuta ve SceneManageru, který volá zmíněné metody před zobrazením každého snímku a vyhodnotí případné posuny. Metoda checkMovement kontroluje stisk šipek na klávesnici a v případě kladného výsledku volá další funkce zajišťující pohyb vpřed a vzad (move), resp. úkroky do stran (strafe). V zásadě jde o jednoduchý posun ze souřadnic aktuální polohy směrem k souřadnicím cíle, resp. po kolmici na tento vektor. Následující ukázka je součástí metody Camera::move(float speed). Vector3f vVector = normalize(direction); position.x += vVector.x * speed; position.y += vVector.y * speed; position.z += vVector.z * speed; Komplikovanější je funkce setView, která nastavuje orientaci kamery podle pohybů ukazatele myši. Funguje na stejném principu jako běžné first-person kamery. Nejdříve vyhodnotí posun kurzoru od středu GL okna, pomocí goniometrických funkcí vypočítá nové souřadnice cíle a přesune kurzor zpět na střed okna. 25
K určení odpovídající rotace je třeba nejprve zjistit osy. Kamera se otáčí vždy podle ypsilon vektoru a kolmice na rovinu udanou vektory ypsilon a direction. Vector3f vAxis = cross(direction, upVector); vAxis = normalize(vAxis); rotateView(angleZ, vAxis.x, vAxis.y, vAxis.z); rotateView(angleY, 0, 1, 0); Hodnoty angleY a angleZ udávají velikost horizontální, resp. vertikální změny orientace. Pomocná funkce rotateView z těchto hodnot vypočítá nový směrový vektor direction. vNewView.x = (cosTheta + (1 - cosTheta) * x * x) * direction.x; vNewView.x += ((1 - cosTheta) * x * y - z * sinTheta) * direction.y; vNewView.x += ((1 - cosTheta) * x * z + y * sinTheta) * direction.z; Výpočet pro zbývající dvě osy probíhá analogicky. Proměnné sinTheta a cosTheta obsahují sinus, resp. cosinus úhlu předaného parametrem, hodnoty x, y a z definují osu otáčení. Pro provedení výpočtu pro všechny osy se z vektoru vNewView stává nový vektor direction. Jeho přičtením k pozici kamery lze získat nový cílový bod, který je nezbytný pro nastavení funkce gluLookAt.
6.6
Nastavení kamer pro stereoskopickou projekci
K navození pocitu prostoru je potřeba celou scénu zachytit ze dvou úhlů. Z hlediska grafického enginu to ovšem neznamená nutnost uchovávání několika instancí třídy Camera. Vykreslování do levého a pravého bufferu totiž neprobíhá současně, je proto výhodnější jedinou kameru vždy před vykreslováním přesunout do pozice levého, resp. pravého oka. void displayStereo() { glDrawBuffer(GL BACK LEFT); glClear(GL COLOR BUFFER BIT | GL DEPTH BUFFER BIT); sceneMgr->camera->setStereoView(CAMERA LEFT); sceneMgr->drawAll(); glDrawBuffer(GL BACK RIGHT); glClear(GL COLOR BUFFER BIT | GL DEPTH BUFFER BIT); sceneMgr->camera->setStereoView(CAMERA RIGHT); sceneMgr->drawAll(); glutSwapBuffers(); }
26
Souřadnice cílového bodu (target) jsou pro obě oči stejné, posunem cíle po polopřímce splývající s vektorem direction ovšem tentokrát dojde ke změně paralaxy. Paralaxa je úhel, jenž svírají přímky vedené z obou očí do pozice cílového bodu. Čím dále je pozorovaný bod od kamery, tím je paralaxa menší. Ve světě virtuální reality lze s její pomocí „zaostřovatÿ na různě vzdálené předměty. Modelová aplikace umožňuje změnu paralaxy dvěma způsoby. Je možné pomocí metody Camera::setDirection ovlivnit velikost směrového vektoru nebo přímo nastavit pozici cílového bodu metodou Camera::setTarget.
6.7
Výpis textu
Textové uzly jsou užitečné k výpisu informací přímo do okna s 3D grafikou. Vykreslují se obdobně jako ostatní modely, jediným podstatným rozdílem je využití ortogonální projekce. Díky ní lze udávat souřadnice textových uzlů v celočíselných hodnotách s počátkem soustavy souřadnic v levém horním rohu okna. Změna typu projekce s sebou nevyhnutelně přináší zásah do projekční matice. Z tohoto důvodu je vykreslování textových uzlů uzavřeno ve vlastním cyklu a probíhá ještě před vykreslováním 3D modelů. Před začátkem textové smyčky aplikace ukládá do zásobníku nejen podobu modelové matice, ale rovněž matici projekční. Po skončení cyklu tedy není třeba znovu přepočítávat nastavení projekce. Příslušnou matici lze jednoduše obnovit voláním funkce glPopMatrix. glMatrixMode(GL PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0, width, 0, height); // ortogonalni projekce glScalef(1, -1, 1); // y osa smeruje dolu glTranslatef(0, -height, 0); // pocatek v levem hornim rohu V průvodní aplikaci změnu projekce provádí metoda SceneManager::enable2D. Proměnné width a height udávají aktuální šířku a výšku okna v pixelech. Obnovení matic, a tedy i přepnutí zpět na zobrazení perspektivy, obsluhuje funkce SceneManager::disable2D. 6.7.1
Zobrazení FPS
Příkladem práce s textem je dynamické vypisování současného počtu snímků za sekundu. Modelová aplikace framerate počítá každý snímek, aby zajistila konstantní rychlost keyframe animací a pohybu kamery. Výpočet se provádí z času potřebného na zobrazení snímku a výsledek je dostupný pomocí metody int SceneManager::getFPS(). Vlastní výpis lze poté zajistit přidáním uzlu typu TextNode. Programátor má možnost definovat polohu uzlu a barvu písma pomocí získaného ukazatele.
27
TextNode* FPSNode = engine->sceneManager->addTextNode(""); if (FPSNode) { FPSNode->position = vector2i(40, 40); } Funkce Vector2i vector2i(int x, int y) slouží pro převod hodnot typu int na interní strukturu Vector2i, čímž usnadňuje zadávání polohy. Pokud by tato funkce použita nebyla, k pozicování by byly třeba dva příkazy.2 Provedení uvedených příkazů způsobí vytvoření prázdného textového uzlu. Aby byla celá konstrukce funkční, je třeba v pravidelných intervalech zjišťovat současný framerate a pomocí získaného ukazatele FPSNode aktualizovat textovou informaci. K periodickému provádění vlastních operací lze využít časovač, který je obsažen v nadstavbě GLUT. glutTimerFunc(1000, displayFPS, 1); První parametr udává dobu v milisekundách. Za tento časový interval se provede funkce displayFPS. Nadefinovaná callback funkce nesmí být metodou žádné třídy. Posledním parametrem je identifikátor časovače. GLUT umožňuje současné použití několika časovačů, definováním časovače se stejným identifikátorem však dojde ke zrušení předchozího. Obsahem callback funkce jsou příkazy k aktualizaci uzlu FPSNode. void displayFPS(int value) { itoa (engine->sceneManager->getFPS(), fps, 10); if (FPSNode) FPSNode->text = fps; glutTimerFunc(1000, displayFPS, 1); } Funkce itoa slouží k převodu čísla na řetězec, poslední parametr udává maximální délku řetězce. Následuje vlastní přepis textové informace a obnovení časovače. GLUT časovače voláním callback funkce zanikají, proto je nezbytné pro periodické volání časovač ve volané funkci obnovit. Parametr value obsahuje identifikátor časovače, který funkci spustil. V tomto případě zůstane nevyužit.
6.8
Import modelů
Průvodní aplikace k tomuto projektu počítá s importem modelů ve formátech MS3D, 3DS, animovaných MD3 a levelů BSP. Importem se rozumí načtení ze souboru, převod na interní datové struktury a následná schopnost zobrazení. 2
Obdobně funguje funkce Vector3f vector3f(float, float, float).
28
Načítání vertexů a trojúhelníků probíhá u všech podporovaných formátů velmi podobně. V hlavičce souboru se nachází celočíselná hodnota udávající celkový počet vrcholů a plošek, ze specifikací formátu lze zjistit strukturu uložených dat. Každý vrchol bývá specifikován třemi desetinnými hodnotami udávajícími souřadnice v prostoru, které jsou při importu převedeny na vlastní strukturu Vector3f(x,y,z: float), dvěma desetinnými čísly, které slouží jako texturové koordináty a jsou převedeny na Vector2f(x,y: float), čtyřmi hodnotami pro barvu a průhlednost RGBA a případně vertexovou normálou. Jednotlivé formáty mají samozřejmě svá další specifika, u BSP levelů se jedná například o koordináty lightmap, což je ekvivalent texturových koordinátů, MS3D zase ukládá bone identifikátor pro skeletální animaci. Zvláštním případem jsou soubory 3DS, které jsou členěny do oddílů (tzv. chunků), do kterých se importér rekurzivně zanořuje. Každý chunk má vlastní hlavičku, která udává typ vnořených informací a délku chunku v bytech.
6.9
Keyframe animace
U souborů formátu MD3 podporuje modelová aplikace keyframe animaci. Vertexové pole v tomto případě obsahuje několik poloh modelu. Tyto polohy udávají krajní stavy objektu při animovaném pohybu. Pro dosažení plynulého pohybu bylo nezbytné implementovat třídu MD3Quaternion, která mezi těmito stavy interpoluje. Matematické podklady pro operace s quaterniony byly převzaty z přednášek Petra Toboly (Quaterniony, 2004). Součástí MD3 modelu je i CFG soubor obsahující informaci o délce trvání jednotlivých animací. Tento údaj je v reálném čase porovnáván s počtem snímků za sekundu a pomocí takto získané hodnoty lze vypočítat aktuální polohu jednotlivých vrcholů. Pro skutečně precizní výsledek by bylo třeba zjišťovat interpolaci sférickou. Tento výpočet je však poměrně náročný a při větším počtu animovaných modelů by mohl představovat nadměrnou zátěž pro procesor. Proto se v praxi přistupuje ke zjednodušení v podobě interpolace lineární. Náročnější výpočet se počítá pouze v případě, že dochází k větší změně polohy a případná deformace, vzniklá lineární interpolací, by tak mohla být vidět pouhým okem. Uvedeným postupem lze snadno obsluhovat například animované modely MD2. V případě složených modelů MD3 je třeba navíc skládat transformace jednotlivých podobjektů. Rendering začíná zásadně od noh. Pokud dojde ke změně polohy či orientace, změna transformačních matic se uchová i pro trup. Při vykreslování hlavy jsou již uvažovány kumulativní transformace noh, trupu i hlavy. V aplikaci lze toto nejjednodušeji řešit rekurzivním zanořováním vykreslovacího algoritmu. Uvedený postup je však třeba bezpodmínečně zachovat, v opačné situaci může docházet k situacím, kdy se například nohy v souladu se svou animací nachází v horizontální poloze, zatímco trup s hlavou setrvávají v poloze vertikální.
29
6.10
Trackery
K získání pozičních údajů ze sdílené paměti je třeba programátorské rozhraní trackd API. Pro nekomerční využití je od společnosti Mechdyne (dříve VRCO) dostupné zdarma. Jedná se o jednoduchou knihovnu v jazyce C, která poskytuje funkce pro zjištění všech potřebných dat. GLUT podporu nestandardních vstupních zařízení neobsahuje, za pomoci časovače lze ovšem nadefinovat vlastní callback funkci pro kontrolu polohy. Interval kontrol lze zvolit libovolně, podle typu aplikace. V žádném případě by však frekvence volání této funkce neměla být vyšší než aktuální framerate. V praxi se osvědčil interval 50 ms, což odpovídá dvaceti snímkům za sekundu. V modelové aplikaci pro komunikaci s trackdAPI slouží třídy Tracker a Controller, jejich použití však není implicitní a programátor je musí aktivovat v hlavním programu. K navázání spojení s klientskou částí programu trackd je třeba zadat tzv. sdílený paměťový klíč, který lze zjistit ze souboru trackd.conf. Namísto konstruktoru se uvedené třídy inicializují metodou init, která zajistí komunikaci se systémem trackd a uloží základní údaje nezbytné pro získávání pozičních dat – počet senzorů u trackeru a počet tlačítek a trackballů u controlleru. bool Tracker::init (int daemonKey) { tracker = NULL; cout << "Inicializuji tracker..."; if (daemonKey != 0) tracker = trackdInitTrackerReader(daemonKey); if (tracker == NULL) return false; else { numSensors = trackdGetNumberOfSensors(tracker); cout << "OK -> verze API: " << getTrackdAPIVersion() << endl; } return true; } Inicializace controlleru probíhá velmi obdobně. Veškeré funkce začínající slovem trackd zprostředkovává aplikační rozhraní knihovny trackdAPI c.h. Získávání dat z trackovacích zařízení obsluhuje funkce loadData. Jejím úkolem je načítat dostupná data do struktur přístupných aplikaci.
30
void Tracker::loadData (int sensor) { if (tracker) { trackdGetPosition(tracker, sensor, pos); trackdGetEulerAngles(tracker, sensor, orn); trackdGetMatrix(tracker, sensor, mat); } } Jak je vidět, načítání pozičních údajů probíhá pro každý senzor zvlášť. Zpravidla není třeba zpracovávat všechny údaje, programátor má možnost parametrem sensor volit libovolný dostupný snímač. Proměnné pos a orn jsou polem 3 desetinných hodnot, lze je tedy snadno převést na strukturu Vector3f. Proměnná mat je čtyřřádkovou čtvercovou maticí odpovídající matici transformace. V aplikaci je dále nutné inicializovat časovač, jehož úkolem bude periodicky získávat nové poziční údaje a adekvátním způsobem pohybovat kamerou. Pro tento úkol je vhodné opět využít klasického časovače GLUT a již existujících metod třídy Camera.
31
7
Závěr
Cílem této práce bylo otestovat na funkční modelové aplikaci možnosti práce s grafickými modely v prostředí virtuální reality. Hlavní součástí se tak stala zejména kapitola Implementace, která se zabývá konkrétním řešením problému a obsahuje ukázky zdrojového kódu v jazyce C++. Při programování bylo využito několika aplikačních programových rozhraní API, především pak rozvinutý průmyslový standard pro práci s grafikou OpenGL a jeho nadstavba GLUT, dále pomocná knihovna GLaux pro načítání bitmapových textur a rozhraní pro přístup k trackovacím zařízením trackd. Projekt byl koncipován na principu editovatelných modulů, díky čemuž skýtá další možnosti pro případná rozšíření či vylepšení. Nabízí se kupříkladu implementace podpory komprimovaných textur či skeletální animace. I v současném stavu však vytvořená aplikace tvoří podstatnou část základu pro rozsáhlejší 3D projekty. Shrnuje většinu nástrojů potřebných pro vytvoření plnohodnotného enginu, jako jsou vektorové datové struktury, metody pro lineární a sférickou interpolaci, podporu textur a mipmap a importéry pro nejběžnější 3D modely. Navíc nabízí možnost stereoskopické projekce a podporu trackovacích zařízení.
32
8
Literatura
Autostereoskopické monitory [on-line]. 2005 [cit. 5. dubna 2008]. Dostupné na Internetu: http://www.gali-3d.com/cz/techno-a-stereo-m/ techno-a-stereo-m.php. COLLADA overview [on-line]. 2008 [cit. 14. dubna 2008]. Dostupné na Internetu: http://www.khronos.org/collada/. Durlach, N. I., Mavor, A. S. Virtual Reality: Scientific and Technological Challenges [on-line]. 1995 [cit. 8. dubna 2008]. Dostupné na Internetu: http://books.nap.edu/openbook.php?record_id=4761&page=188. GLUT - The OpenGL Utility Toolkit [on-line]. 1997 [cit. 15. května 2008]. Dostupné na Internetu: http://www.opengl.org/resources/libraries/glut/. Keller, B. Will XNA tools be able to help reduce game sizes? [on-line]. 2006 [cit. 15. května 2008]. Dostupné na Internetu: http://blogs.msdn.com/briankel/ archive/2006/01/24/517071.aspx. Maya (software) [on-line]. [cit. 14. dubna 2008]. Dostupné na Internetu: http:// en.wikipedia.org/wiki/Maya_\%28software\%29. Milkshape 3D [on-line]. [cit. 14. dubna 2008]. Dostupné na Internetu: http://en. wikipedia.org/wiki/MilkShape_3D. OpenGL vedle DirectX stále žije – vychází OpenGL 3 [on-line]. 2007 [cit. 2. května 2008]. Dostupné na Internetu: http://www.ddworld.cz/linux/ opengl-vedle-directx-stale-zije-vychazi-opengl-3-10.html. Pečiva, J. Open Inventor: Knihovna pro realtimovou 3D grafiku [on-line]. 2003 [cit. 15. května 2008]. Dostupné na Internetu: http://www.root.cz/clanky/ open-inventor/. Tišnovský, P. Grafická knihovna OpenGL [on-line]. 2003 [cit. 2. května 2008]. Dostupné na Internetu: http://www.root.cz/clanky/ graficka-knihovna-opengl-1/. Tišnovský, P. Vektorové grafické formáty a metaformáty [on-line]. 2007 [cit. 14. dubna 2008]. Dostupné na Internetu: http://www.root.cz/clanky/ vektorove-graficke-formaty-a-metaformaty/. Tobola, P. Matematické základy: Quaterniony [on-line]. Brno, 2004 [cit. 18. května 2008]. Dostupné na Internetu: http://www.fi.muni.cz/~ptx/PA010/Slides/ PA010_4.pdf.
33
Přílohy
A
Diagram tříd
Obr. 1: Jádro grafického enginu.
Enumerátor graphicModes definuje způsob vykreslování. Ve STENCIL režimu je aktivován hloubkový buffer, čímž je znemožněno vykreslování průhledných objektů. Uplatněním volby MODE BLENDING je povolena průhlednost na základě hodnoty alfa, dojde však ke zvýšení hardwarových nároků. Chráněné atributy třídy SceneManager slouží k periodickému výpočtu FPS a specifikaci zobrazované scény. Metoda resizeWindow je volána při každé změně velikosti okna, aby nedocházelo k vykreslování mimo určenou plochu. Její součástí je i volání stejnojmenné metody třídy Camera, která aktualizuje součadnice středu okna (midWidth, midHeight). Veškeré metody nastavující pozici kamery jsou definovány klíčovým slovem public, aby je bylo možné využít k obsluze trackovacích zařízení. 35
Obr. 2: Hierarchie modelových tříd.
Třída AnimatedModel odpovídá návrhu starší verze Quake modelů MD2. Modely MD3 jsou však složeny z několika nezávislých částí, které jsou v reálném čase spojovány (linkovány) pomocí tzv. tagů. Ty definují vertexy společné pro sousedící části modelu.
Obr. 3: Dvojice tříd pro zpracování trackerů.
36
B
Screenshoty aplikace
Obr. 4: Animovaný MD3 model v prostředí BSP úrovně.
37
Obr. 5: 3DS model ovládaný trackovacím zařízením.
38