Fakulta elektrotechnická

Podobne dokumenty
Internet a zdroje. (Zdroje na Internetu) Mgr. Petr Jakubec. Katedra fyzikální chemie Univerzita Palackého v Olomouci Tř. 17.

Aproximace funkcí 1,00 0,841 1,10 0,864 1,20 0,885. Body proložíme lomenou čarou.

Zásuvný modul QGISu. QGIS plugin pro práci s katastrálními daty

Numerické metody 8. května FJFI ČVUT v Praze

ggplot2 Efektní vizualizace dat v prostředí jazyka R Martin Golasowski 8. prosince 2016

Funkce zadané implicitně. 4. března 2019

1 Soustava lineárních rovnic

Matematika 2, vzorová písemka 1

Linea rnı (ne)za vislost

Automatové modely. Stefan Ratschan. Fakulta informačních technologíı. Evropský sociální fond Praha & EU: Investujeme do vaší budoucnosti

Komplexní analýza. Martin Bohata. Katedra matematiky FEL ČVUT v Praze Martin Bohata Komplexní analýza Mocninné řady 1 / 18

Kristýna Kuncová. Matematika B2 18/19

(1) Derivace. Kristýna Kuncová. Matematika B2 17/18. Kristýna Kuncová (1) Derivace 1 / 35

Numerické metody minimalizace

Kristýna Kuncová. Matematika B3

Úvodní informace. 18. února 2019

Powyższe reguły to tylko jedna z wersji gry. Istnieje wiele innych wariantów, można też ustalać własne zasady. Miłej zabawy!

Kapitola 4: Soustavy diferenciálních rovnic 1. řádu

DFT. verze:

Obsah. Zobrazení na osmistěn. 1 Zobrazení sféry po částech - obecné vlastnosti 2 Zobrazení na pravidelný konvexní mnohostěn

Edita Pelantová, katedra matematiky / 16

NÁVOD K POUŽITÍ KEZELÉSI KÉZIKÖNYV INSTRUKCJA OBSŁUGI NÁVOD NA POUŽÍVANIE. Česky. Magyar. Polski. Slovensky

PA152,Implementace databázových systémů 2 / 25

Co nám prozradí derivace? 21. listopadu 2018

Necht je funkce f spojitá v intervalu a, b a má derivaci v (a, b). Pak existuje bod ξ (a, b) tak, že f(b) f(a) b a. Geometricky

Katedra kybernetiky skupina Inteligentní Datové Analýzy (IDA) Evropský sociální fond Praha & EU: Investujeme do vaší budoucnosti

K SAMOSTATNÉ MODULOVÉ SCHODY MONTÁŽI. asta

Matematika (KMI/PMATE)

Katedra aplikované matematiky FEI VŠB Technická univerzita Ostrava

Obsah Atributová tabulka Atributové dotazy. GIS1-2. cvičení. ČVUT v Praze, Fakulta stavební, katedra mapování a kartografie

Geometrická nelinearita: úvod

Kristýna Kuncová. Matematika B2

Paradoxy geometrické pravděpodobnosti

Platforma pro analýzu, agregaci a vizualizaci otevřených dat souv

Vybrané kapitoly z matematiky

Inverzní Z-transformace

Martin Pergel. 26. února Martin Pergel

Obkládačky a dlaždičky Płytki ścienne i podłogowe: SIGHT šedá szary

ULS4805FE. Návod k použití Návod na použitie Instrukcja obsługi Instruction Manual Használatı utasítás. Licensed by Hyundai Corporation, Korea

TGH01 - Algoritmizace

MATEMATIKA 3. Katedra matematiky a didaktiky matematiky Technická univerzita v Liberci

kontaktní modely (Winklerův, Pasternakův)

Reprezentace dat. BI-PA1 Programování a Algoritmizace I. Ladislav Vagner

Anna Kratochvílová Anna Kratochvílová (FJFI ČVUT) PDR ve zpracování obrazu / 17

Biosignál II. Lékařská fakulta Masarykovy univerzity Brno

TGH01 - Algoritmizace

Matematika III Stechiometrie stručný

Rekrutacja List Motywacyjny

Statistika (KMI/PSTAT)

PŘENOS GEOMETRIE 3D SCÉNY PO SÍTI

(2) Funkce. Kristýna Kuncová. Matematika B2. Kristýna Kuncová (2) Funkce 1 / 25

Elementární funkce. Edita Pelantová. únor FJFI, ČVUT v Praze. katedra matematiky, FJFI, ČVUT v Praze

POLIURETANOWE SPRĘŻYNY NACISKOWE. POLYURETHANOVÉ TLAČNÉ PRUŽINY

Diferenciální rovnice základní pojmy. Rovnice se

Rovnice proudění Slapový model

Paralelní implementace a optimalizace metody BDDC

ČVUT v Praze, katedra geomatiky. zimní semestr 2014/2015

Vlastnosti. Příprava. Czech - 2 -

(13) Fourierovy řady

Lineární algebra - iterační metody

TVL LED NÁVOD K POUŽITÍ NÁVOD NA POUŽITIE

Příručka k rychlé instalaci: NWD2105. Základní informace. 1. Instalace softwaru

HL24285SMART. Návod k použití Návod na použitie Instrukcja obsługi Használatı utasítás. Licensed by Hyundai Corporation, Korea

Biofyzikální ústav Lékařské fakulty Masarykovy univerzity Brno

Stavový popis Stabilita spojitých systémů (K611MSAP) Katedra aplikované matematiky Fakulta dopravní ČVUT. čtvrtek 20. dubna 2006

TVL UMP2 NÁVOD K POUŽITÍ NÁVOD NA POUŽITIE

Cauchyova úloha pro obyčejnou diferenciální rovnici

IEL Přechodové jevy, vedení

Kristýna Kuncová. Matematika B2 18/19. Kristýna Kuncová (1) Vzorové otázky 1 / 36

podle přednášky doc. Eduarda Fuchse 16. prosince 2010

DXDB 215 NÁVOD K POUŽITÍ NÁVOD NA POUŽITIE INSTRUKCJA OBSŁUGI USER MANUAL

Funkce více proměnných: limita, spojitost, parciální a směrové derivace, diferenciál

Jednoduchá zobrazení. Podpořeno z projektu FRVŠ 584/2011.

Petr Křemen FEL ČVUT. Petr Křemen (FEL ČVUT) Vysvětlování modelovacích chyb 133 / 156

Průvodce studiem V této kapitole se budeme zabývat diferenciálním počtem pro funkce více

}w!"#$%&'()+,-./012345<ya

Register and win!

KATEDRA INFORMATIKY Roman Loník

Jednoduchá zobrazení. Podpořeno z projektu FRVŠ 584/2011.

MATEMATIKA 3 NUMERICKÉ METODY. Katedra matematiky a didaktiky matematiky Technická univerzita v Liberci

Design of Experiment (DOE) Petr Misák. Brno 2016

5. a 12. prosince 2018

návod k použití instrukcja obsługi

DelighTech Fitness App

Toto zadání je podepsané děkanem a vedoucím katedry, po obhajobě).

Česky. Magyar NÁVOD K POUŽITÍ KEZELÉSI KÉZIKÖNYV INSTRUKCJA OBSŁUGI NÁVOD NA POUŽÍVANIE. Polski. Slovensky

Logika V. RNDr. Kateřina Trlifajová PhD. Katedra teoretické informatiky Fakulta informačních technologíı BI-MLO, ZS 2011/12

L FL L FL CS PRAČKA NÁVOD K POUŽITÍ 2 PL PRALKA INSTRUKCJA OBSŁUGI 34

návod k použití instrukcja obsługi

Vladimír Ulman Centre for Biomedical Image Analysis. 10th October, 2007 FI MU, Brno

Euklidovský prostor. Funkce dvou proměnných: základní pojmy, limita a spojitost.

Stochastické modelování v ekonomii a financích Konzistence odhadu LWS. konzistence OLS odhadu. Předpoklady pro konzistenci LWS

Pracovní listy. Stereometrie hlavního textu

Západočeská univerzita v Plzni Fakulta aplikovaných věd Katedra matematiky METODA FAST MARCHING PRO

Expresivní deskripční logiky

Skraplacze wyparne. Odpaøovací kondenzátory D 127/3-5 PL/CZ

Definice Řekneme, že PDA M = (Q,Σ,Γ,δ,q 0,Z 0,F) je. 1. pro všechna q Q a Z Γ platí: kdykoliv δ(q,ε,z), pak δ(q,a,z) = pro všechna a Σ;

Komplexní analýza. Martin Bohata. Katedra matematiky FEL ČVUT v Praze Martin Bohata Komplexní analýza Úvod 1 / 32

Funkce více proměnných: limita, spojitost, derivace

Zadání: Vypočítejte hlavní momenty setrvačnosti a vykreslete elipsu setrvačnosti na zadaných

Transkrypt:

České vysoké učení technické v Praze Fakulta elektrotechnická ČVUT FEL katedra počítačů Diplomová práce Zobrazovací jádro pro vizualizaci třírozměrných dat v reálném čase Petr Slivoň Vedoucí práce: Ing. Jaroslav Sloup Studijní program: Elektrotechnika a informatika, dobíhající Obor: Výpočetní technika leden 2008

ii

Poděkování Děkuji svému vedoucímu Ing. Jaroslavu Sloupovi a všem, kteří mi pomohli, nebo mě podporovali při tvorbě této diplomové práce. Mé poděkování patří také mým rodičům, kteří mě při studiu vždy podporovali. iii

iv

Prohlášení Prohlašuji, že jsem svou diplomovou práci vypracoval samostatně a použil jsem pouze podklady uvedené v přiloženém seznamu. Nemám závažný důvod proti užití tohoto školního díla ve smyslu 60 Zákona č. 121/2000 Sb., o právu autorském, o právech souvisejících s právem autorským a o změně některých zákonů (autorský zákon). V Praze dne 14.1. 2008... v

vi

Abstract The aim of the thesis is to design and implement a general modular graphic engine. The engine is determined to visualize 3D data in real time (so called 3D engine). The main purpose of this work is to create universal library with wide area of exploitation, from specialized application used for scientific visualization to computer games. The user using the library needn t learn details about visualization itself, but he can only focus to modeling the problem he solves. Graphic 3D engine is very huge library which is generally created by a team of specialists for several years. Therefore, design in this work has been developed with accent to generality and extensibility. The intention is to create base of 3D engine which can be extended to more complex systems. The user, that want to use the library out of scope of base functions, can create own functions and adapt the engine to his needs and requirements. Abstrakt Práce se zabývá návrhem a implementací obecného modulárního zobrazovacího jádra, určeného pro vizualizaci trojrozměrných dat v reálném čase (tzv. 3D Enginu). Záměrem je vytvořit univerzální knihovnu s širokým záběrem využití, od specializovaných aplikací, určených pro vizualizaci vědeckých dat, až po počítačové hry. Uživatel využívající tuto knihovnu se tak nemusí zabývat detaily související s vizualizací, ale pouze detaily modelovaného problému. Grafický 3D engine je velice rozsáhlá knihovna, kterou často tvoří tým několika lidí po dobu až několika let. Proto se návrh v této práci nese v duchu maximální obecnosti a snadné rozšiřitelnosti. Záměrem je tedy vytvořit kvalitní základ enginu, na kterém lze pak dále stavět složitější systém. Uživatel, který bude chtít tuto knihovnu využít nad rámec základních funkcí, si tak bude moci chybějící funkce snadno doplnit a přizpůsobit tak engine vlastním potřebám a požadavkům. vii

viii

Obsah Seznam obrázků Seznam tabulek xiv xv 1 Úvod 1 1.1 Grafický 3D engine.............................. 1 1.2 Herní engine.................................. 2 2 Analýza existujících řešení 3 2.1 Existující grafické enginy........................... 3 2.1.1 CryENGINE TM 2........................... 3 2.1.2 Serious Engine 1.x.......................... 3 2.1.3 Unreal Engine 2............................ 4 2.1.4 OGRE 3D............................... 5 2.1.5 The Nebula Device 2......................... 7 2.2 Srovnání enginů a stanovení požadavků................... 8 3 Stanovení cílů práce 10 3.1 Nezávislost na grafickém API........................ 10 3.2 Rozšiřitelnost scénového grafu........................ 10 3.3 Import vstupních dat z různých formátů.................. 11 3.4 Systém modulů................................ 11 4 Analýza a návrh řešení 12 4.1 Trojrozměrný prostor............................. 12 4.2 Způsoby reprezentace trojrozměrných modelů............... 12 4.2.1 Objemová reprezentace........................ 13 4.2.2 Hraniční reprezentace......................... 13 4.3 Základní bloky grafického enginu...................... 14 4.4 Správce zdrojů dat.............................. 15 4.4.1 Centralizovaný a decentralizovaný přístup............. 15 4.4.2 Nezávislost správce zdrojů na grafickém API............ 16 4.4.3 Geometrie modelů.......................... 18 4.4.4 Textury................................ 19 4.5 Struktura FaceGroup............................. 22 4.6 Struktura Material.............................. 24 4.7 Rozhraní RenderDevice (Renderer)..................... 29 4.7.1 Synchronizace zdrojů dat....................... 31 4.7.2 Rendering............................... 33 4.8 Scénový graf.................................. 33 4.8.1 Základní struktury scénového managementu............ 35 4.8.2 Sdílení geometrie (Instancing).................... 41 4.9 Vstup dat................................... 42 4.9.1 Import geometrie a scény....................... 43 4.9.2 Import textur............................. 44 ix

4.10 Struktura Core................................ 44 5 Realizace 46 5.1 Třídy zdrojů dat............................... 46 5.1.1 Třída C SharedResource....................... 46 5.1.2 Třída C IndexBuffer......................... 46 5.1.3 Třída C VertexBuffer......................... 48 5.1.4 Třída C Surface............................ 48 5.1.5 Třída C Volume............................ 48 5.1.6 Třída C BaseTexture......................... 48 5.1.7 Třída C Texture........................... 48 5.1.8 Třídy C CubeTexture a C VolumeTexture.............. 49 5.2 Třídy hraničních objektů........................... 49 5.2.1 Třída C BoundingObject....................... 49 5.2.2 Třída C AABBox........................... 49 5.2.3 Třída C BSphere........................... 50 5.2.4 Třída C OBBox............................ 50 5.3 Třídy scénového grafu............................ 50 5.3.1 Třída C Object............................ 52 5.3.2 Třída C Frame............................ 52 5.3.3 Třída C SceneRoot.......................... 52 5.3.4 Třída C Visual............................ 52 5.3.5 Třda C Camera............................ 54 5.4 Třídy pro podporu pluginů.......................... 54 5.4.1 Třída C Plugin............................ 54 5.4.2 Třída C ClassDesc.......................... 55 5.5 Podpůrné třídy enginu............................ 55 5.5.1 C Core................................. 55 5.5.2 C FrameList.............................. 56 6 Testování 57 6.1 Testování algoritmů enginu.......................... 57 6.2 Testování enginu............................... 57 6.2.1 Testovací aplikace EngineTestNET................. 58 6.2.2 Plugin DDS Texture Import..................... 59 6.2.3 Plugin Wavefront OBJ Import.................... 59 6.2.4 Plugin intestscene.......................... 59 6.2.5 Plugin Direct3D RenderDevice................... 60 6.3 Shrnutí..................................... 61 7 Závěr 62 7.1 Zhodnocení.................................. 62 7.2 Další možné pokračování........................... 62 8 Seznam literatury 63 A Uživatelská příručka k EngineTestNET 65 A.1 Instalace.................................... 65 x

A.2 Ovládání.................................... 66 A.2.1 Import Testovací scény........................ 66 B Obsah přiloženého CD 69 xi

xii

Seznam obrázků 2.1 Scéna renderovaná CryENGINEM TM 2 včetně pomocných značek editoru. Tento a další screenshoty naleznete na http://www.crysis-online.com... 4 2.2 Indoor scéna renderovaná Serious Enginem 1.x. Screenshot převzat z databáze enginu na http://www.devmaster.net/engines/.......... 5 2.3 Editor herního levelu využívající Unreal Engine 2. Screenshot převzat z http://www.unrealtechnology.com...................... 6 2.4 Ukázka renderingu enginu OGRE 3D. Tento a další screenshoty naleznete na http://www.ogre3d.org........................... 7 2.5 Ukázka renderingu enginu The Nebula Device 2. Screenshot převzat ze stránek Radon Labs[3]............................. 8 4.1 Uspořádání os levotočivého (a) a pravotočivého (b) souřadnicového systému. 12 4.2 Objemová reprezentace modelu postavičky.................. 13 4.3 Hraniční reprezentace modelu krychle a koule................ 14 4.4 Základní bloky grafického enginu a jejich datová komunikace........ 15 4.5 Správce zdrojů s kombinací centralizované a decentralizované logiky.... 16 4.6 Způsob uchování dat zdroje při odpojeném (a) a připojeném (b) rendereru. 17 4.7 Příklad jednoduchého (a) a složitějšího (b) vertexu............. 19 4.8 Návrh rozhraní zdroje vertexů........................ 20 4.9 Návrh rozhraní zdroje indexů......................... 20 4.10 Struktura textury velikosti 16x16 pixelů................... 21 4.11 Návrh rozhraní zdroje typu Texture (a) a zdroje dat typu Surface (b)... 21 4.12 Orientace stěn cube textury.......................... 22 4.13 Návrh rozhraní cube textury (a) a vnitřní uspořádání kontejneru (b)... 23 4.14 Uspořádání objemové textury se třemi údovněmi detailů.......... 23 4.15 Návrh rozhraní zdroje typu Volumue Texture (a) a rozhraní zdroje typu Volume (b)................................... 23 4.16 Typy primitiv podporovaných v enginu.................... 24 4.17 Způsob míchání textur v materiálu RWE................... 27 4.18 Interakční vztah mezi strukturami Frame, SceneRoot, Visual a RenderDevice....................................... 35 4.19 Hierarchie reprezentující kolekci vzájemně svázaných objektů....... 36 4.20 Hierarchie scény enginu při použití více tříd SceneRoot........... 40 4.21 Scénový graf zjišťující instancing geometrie pomocí sdílením podgrafu scény. 41 4.22 Scénový graf z obrázku 4.21, který ovšem zajišťuje instancing sdílením dat geometrie (low-level instancing)........................ 42 5.1 Dědičnost tříd zdrojů dat........................... 47 5.2 Dědičnost tříd hraničních objektů....................... 50 5.3 Dědičnost tříd scénového grafu........................ 51 5.4 Způsob připojení pluginu k třídě C Plugin.................. 55 6.1 Okno Trace Serveru s logovacími informacemi po spuštění enginu..... 57 6.2 Hlavní okno testovací aplikace. Vlevo je zobrazen scénový graf enginu, vpravo zobrazovaná scéna. Horní toolbar nabízí tlačítka pro editaci transformace vybraného uzlu a pro skrytí scénového grafu............ 58 xiii

6.3 Graf využití grafické paměti a zatížení CPU za běhu enginu........ 60 A.1 Hlavní obrazovka testovacího programu EngineTestNet s popisem funkcí. 67 A.2 Dialog vyplněný informacemi z importního pluginu (intestscene)..... 68 xiv

Seznam tabulek 2.1 Tabulka vlastností enginu CryENGINE TM 2................. 3 2.2 Tabulka vlastností enginu Serious Engine 1.x................ 4 2.3 Tabulka vlastností enginu Unreal Engine 2.................. 5 2.4 Tabulka vlastností enginu OGRE 3D.................... 6 2.5 Tabulka vlastností enginu The Nebula Device 2.............. 7 2.6 Vzájemné porovnání vlastností enginů.................... 9 4.1 Tabulka možných složek vertexu a jejich význam.............. 19 4.2 Parametry materiálu enginu.......................... 25 4.3 Způsoby ořezávání polygonů hodnoty kterých může v materiálu nabývat parametr CULLMODE............................ 26 4.4 Způsoby vylňování polygonů hodnoty kterých může v materiálu nabývat parametr FILLMODE............................. 26 4.5 Způsoby stínování polygonů hodnoty kterých může v materiálu nabývat parametr SHADEMODE........................... 26 4.6 Způsoby míchání barev hodnoty kterých může v materiálu nabývat parametr SRCBLEND a DESTBLEND. R s,g s,b s,a s jsou barevné složky zdrojového pixelu a R d,g d,b d,a d jsou barevné složky cílového pixelu v kreslícím plátně................................ 26 4.7 Parametry blending stage pordporované v materiálu enginu........ 28 4.8 Možné operace blending stage a popis jejich výpočtu........... 28 4.9 Možné hodnoty a jejich význam pro blending stage parametry COL- ORARGx a RESULTARG........................... 30 4.10 Možné hodnoty a jejich význam pro blending stage parametr TEXCO- ORDINDEX.................................. 30 4.11 Funkce rozhraní RenderDevice pro synchronizaci zdrojů dat........ 32 4.12 Funkce rozhraní RenderDevice pro inicializaci, vyrkeslování scény a ukončení rendereru.................................... 33 4.13 Příznaky Frame flags a jejich význam.................... 38 4.14 Funkce rozhraní SceneImport......................... 43 4.15 Funkce rozhraní TextureImport........................ 44 5.1 Virtuální metody třídy C Frame a jejich význam.............. 53 5.2 Významné virtuální metody třídy C SceneRoot a jejich význam...... 53 xv

xvi

KAPITOLA 1. ÚVOD 1 1 Úvod Vizualizace je mocný nástroj, který usnadňuje pochopení složitých problémů. Člověk se od narození učí pozorováním okolního světa, který je trojrozměrný (3D). Trojrozměrný prostor je tedy pro člověka nejpřirozenější prostření. Vizualizace problémů v 3D prostoru člověku poskytuje mnohem více informací o vztazích modelovaného problému a umožňuje tak jeho rychlejší pochopení a nalezení případného řešení. Navíc, 3D vizualizace poskytuje možnost podívat se na problém doslova z různých úhlů pohledu. Faktu, že člověk má nejblíže k trojrozměrnému prostředí, si jako první všimli vývojáři počítačových her. S příchodem grafických akcelerátorů se vzhled modelovaných virtuálních světů neuvěřitelně zlepšoval. Herní průmysl byl vždy hráči počítačových her tlačen k využití hardwaru, až na samou hranici jeho možností. Díky tomu se stále vyvíjí nové algoritmy a postupy umožňující stále dokonalejší vizualizaci. Kontakt s počítačovým virtuálním světem se tak dnes již může stát natolik uchvacující, že je téměř nerozeznatelný od reálného prostředí[14]. V této práci se zabývám návrhem softwarové knihovny, která usnadňuje vizualizaci trojrozměrných dat ať už se jedná o vědecká data, nebo virtuální svět. Uživatel této knihovny se tak nemusí zabývat detaily souvisejícími s vizualizací, ale pouze detaily modelovaného problému. Softwarovou knihovnu, která umožňuje trojrozměrnou vizualizaci, nazýváme grafický 3D engine. Vytvořit kvalitní grafický engine je rozsahem implementace velice náročná věc. V herních firmách pracuje na grafickém enginu zpravidla několik členů týmu, po dobu až několika let. Proto účelem této práce není vytvořit špičkový rozsáhlý engine, ale pouze kvalitně navržené jádro, které bude snadné rozšiřovat a vylepšovat. V úvodu této práce si objasníme rozdíl mezi grafickým a herním enginem. 1.1 Grafický 3D engine Grafický 3D engine (dále jen engine) je softwarová knihovna, navržená pro snadnou vizualizaci trojrozměrných dat. Engine obsahuje mnoho funkcí pro práci s daty v prostoru, umožňuje je různě nesvětlovat, měnit je dynamicky v čase, otáčet a posouvat je v prostoru, nastavovat jim materiálové vlastnosti a podobně. Samotné vykreslování (rendering) může probíhat buď kompletně softwarově nebo za pomoci grafického akcelerátoru. Hlavní výhoda grafického akcelerátoru je v rychlosti vizualizace. Je tak možné interaktivně zobrazovat i velmi rozsáhlé trojrozměrné scény. Pokud chceme využít grafického akcelerátoru musíme využít některou z knihoven, která nám zpřístupní hardwarově akcelerované funkce pro rychlou vizualizaci. Máme na výběr ze dvou nejvíce rozšířených knihoven: OpenGL[19] nebo DirectX[16]. I když hardwarová akcelerace poskytuje v současné době obrovské možnosti, softwarový rendering zatím poskytuje kvalitnější výsledky. Při softwarovém renderingu probíhají veškeré výpočty na CPU. Jelikož je vizualizace velmi výpočetně náročná, je rychlost renderingu nízká a nehodí se tak pro aplikace, které vyžadují interaktivitu. Nicméně, hlavní výhodou softwarového renderingu je, že máme pod kontrolou celý proces vykreslení scény. Díky tomu můžeme doplnit speciální efekty, nebo docílit až foto-realistických výsledků, které jsou k nerozeznání od reálných fotografií. Vše ovšem závisí na zvolené metodě vizualizace a na času, který jsme ochotni čekat na výsledek.

2 KAPITOLA 1. ÚVOD 1.2 Herní engine Jako herní engine jsou označována komplexní softwarová řešení navržená pro potřeby herního průmyslu. Herní engine se vlastně skládá z několika základních stavebních kamenů, které jsou přímo navržený tak, aby spolu mohly hladce spolupracovat a tvořit jednotný celek. Jedním z těchto stavebních kamenů je právě grafický engine, který zajišťuje vizualizaci. Dalšími prvky jsou fyzikální engine, systém umělé inteligence, zvukový systém, podpora hraní po síti a další. V balíku softwaru představující herní engine také nalezneme různé podpůrné nástroje, nutné pro tvorbu počítačových her za použití daného softwarového balíku. Jedná se většinou o editory herního prostředí, pracující přímo nad herním enginem a různá rozšíření do komerčních grafických editorů typu 3DS MAX TM [7], která pomáhají přenést modely a animace mezi jednotlivými systémy.

KAPITOLA 2. ANALÝZA EXISTUJÍCÍCH ŘEŠENÍ 3 2 Analýza existujících řešení V této kapitole se budu věnovat existujícím alternativám této diplomové práce. Zaměřím se hlavně na části, které se tato práce snaží řešit odlišným přístupem. Podíváme se tedy na použitá grafická API, vlastnosti a možnosti rozšíření scénového managementu (scénový graf) a možnosti formátu vstupních dat. 2.1 Existující grafické enginy Představíme si několik zástupců existujících grafických případně herních enginů. Každého zástupce stručně představím a uvedu účel za jakým byl vytvořen. U všech budou v souhrnné tabulce uvedeny podstatné informace o vlastnostech enginu, vzhledem k této práci. Pro zajímavost se pokusím zjistit ceny licencí daných řešení. 2.1.1 CryENGINE TM 2 CryENGINE TM 2 je plně profesionální herní engine. Jedná se o komplexní řešení vyvíjené firmou Crytek[9]. Firma Crytek má za partnery velké inovátory v oblasti grafických akcelerátorů a procesorů, jako je například nvidia[18], AMD[6] nebo Intel[12]. Díky tomu má přístup k nejnovějším a nejpřesnějším informacím v daných oborech. Záměrem Cryteku bylo vytvořit takzvaný next-gen herní engine, využívající maximálních možností současného špičkového komerčního hardware. V současné době se jedná o nejlépe technologicky vybavený herní engine který znám. Na obrázku 2.1 je zobrazena scéna vykreslovaná CryENGINEm 2 včetně pomocných značek editoru. V tabulce 2.1 jsou uvedeny souhrnné informace o vlastnostech enginu. Podporovaná API DirectX9, DirectX10 Vlastnosti Scénový graf je navržen pro rychlý a kvalitní rendering scénového rozsáhlých scenérií i uzavřených vnitřních prostor. managementu Konkrétní typy algoritmů nejsou ve specifikaci uvedeny. Rozšiřitelnost Ano, pomocí zásuvných modulů. scénového grafu Možnosti vstupních Data jsou konvertována do vlastního formátu dat enginu pomocí rozšíření do 3DS MAX TM [7] a interních nástrojů dodaných v rámci sw balíku. Výrobce Crytek Cena licence Neveřejná, nicméně dá se očekávat v řádu několika desítek až stovek tisíc dolarů. Tabulka 2.1: Tabulka vlastností enginu CryENGINE TM 2. 2.1.2 Serious Engine 1.x Profesionální herní engine vytvořený firmou Croteam[8]. Engine byl vytvořen primárně pro hru Serios Sam a poté byl licencován pro mnoho jiných herních titulů. Opět je zde snaha implementovat nejnovější technologie z oboru 3D grafiky. Jako grafické API bylo vybráno OpenGL. Díky tomu bych očekával, že se engine rozšíří i na mnoho jiných

4 KAPITOLA 2. ANALÝZA EXISTUJÍCÍCH ŘEŠENÍ Obrázek 2.1: Scéna renderovaná CryENGINEM TM 2 včetně pomocných značek editoru. Tento a další screenshoty naleznete na http://www.crysis-online.com. operačních systémů než je Windows. Nicméně nestalo se tak a engine byl portován pouze na herní konzoli Xbox. Tabulka 2.2 uvádí souhrnné vlastnosti Serious Engine 1.x a na obrázku 2.2 můžete posoudit kvalitu renderingu indoor scény daného enginu. Podporovaná API OpenGL Vlastnosti Podpora indoor i outdoor scén. Konkrétní podporované scénového algoritmy jsou: Obecná detekce viditelnosti, Portály, managementu Occlusion Culling, Level-Of-Detail. Rozšiřitelnost Ve specifikaci není zmínka o jakékoliv možnosti modifikace scénového grafu enginu. Tedy ani scénový graf nejspíše nebude možné snadno obohatit o nové funkce. Možnosti Nejsou konkrétně ve specifikaci uvedeny. Data jsou vstupních pravděpodobně konvertována do vlastního formátu enginu dat pomocí interních nástrojů dodaných v rámci sw balíku. Výrobce Croteam Cena licence Licence má 4 úrovně, rozlišené podle velikosti podpory. Nejnižší úroveň stojí 20,000$, nejvyšší 100,000$. Tabulka 2.2: Tabulka vlastností enginu Serious Engine 1.x. 2.1.3 Unreal Engine 2 Profesionální herní engine firmy Epic Games Inc.[2]. Engine byl vyvíjen pro hru Unreal Turnament, nicméně jeho kvalit později využilo více než 20 dalších herních titulů. Engine byl navržen tak, aby ho bylo možné portovat na různé platformy. K tomu bylo nutné

KAPITOLA 2. ANALÝZA EXISTUJÍCÍCH ŘEŠENÍ 5 Obrázek 2.2: Indoor scéna renderovaná Serious Enginem 1.x. Screenshot převzat z databáze enginu na http://www.devmaster.net/engines/. striktně oddělit renderovací část enginu od scene managementu. I tak se ale podařilo využít moderních technologií grafických akcelerátorů. Nyní existují porty enginu na platformy Windows, Linux, MacOS, Xbox, Playstation, GameCube. V tabulce 2.3 jsou uvedeny souhrnné informace o enginu. Na obrázku 2.3 je screenshot herního editoru využívajícího Unreal Engine 2. Podporovaná API DirectX, OpenGL, Software rendering Vlastnosti Podpora indoor i outdoor scén. Konkrétní podporované scénového algoritmy jsou: Obecná detekce viditelnosti, Binary managementu Space Partitioning (BSP), Portály, Occlusion Culling, Level-Of-Detail (LOD) a Potentially Visible Set (PVS). Rozšiřitelnost Ano, pomocí zásuvných modulů. scénového grafu Možnosti Data jsou konvertována do vlastního formátu enginu vstupních pomocí interních nástrojů a editorů dodaných dat v rámci sw balíku. Výrobce Epic Games Inc. Cena licence Pro studijní účely je možné využít enginu zdarma s různými omezeními. Engine je například zakázáno modifikovat. Pro komerční účely je cena licence neveřejná. Tabulka 2.3: Tabulka vlastností enginu Unreal Engine 2. 2.1.4 OGRE 3D Jedná se o open-source 3D grafický engine firmy Torus Knot Software[5]. Jeho řešení je asi nejblíže této diplomové práci. Engine je navržen tak, aby ho bylo možné použít pro

6 KAPITOLA 2. ANALÝZA EXISTUJÍCÍCH ŘEŠENÍ Obrázek 2.3: Editor herního levelu využívající Unreal Engine 2. Screenshot převzat z http://www.unrealtechnology.com. mnoho různých typů aplikací, které vyžadují vizualizaci trojrozměrných dat v reálném čase. Nejedná se tedy vyloženě o herní engine, ale je možné ho pro tyto účely také použít. Engine má flexibilní scénový management, který je navržen pro snadnou modifikaci a přizpůsobení. Vstupní data je možno importovat z jakéhokoliv formátu. Podporovaná grafická API jsou OpenGL a DirectX. V tabulce 2.4 jsou opět uvedeny podstatné rysy enginu. Na obrázku 2.4 je screenshot scény z jednoho z mnoha projektů využívajících OGRE 3D. Podporovaná API DirectX, OpenGL - volbu API je možno provést před spuštěním enginu pomocí zásuvného modulu. Vlastnosti Flexibilní scénový management, který není svázán s scénového konkrétním typem scény. Jednotlivé algoritmy scénového managementu managementu je možné doplnit pomocí zásuvných modulů. Rozšiřitelnost Ano, pomocí zásuvných modulů. scénového grafu Možnosti Je možné vytvořit vlastní import dat do enginu nebo vstupních mohou být data uložena do vlastního formátu enginu dat pomocí rozšíření do existujících 3D editorů. Výrobce Torus Knot Software. Cena licence Engine je možno využívat pod GPL licencí. Pokud není možné použít engine v rámci GPL licence (např. z důvodu utajení) je možné zakoupit speciální licenci. Cena této licence není veřejná. Tabulka 2.4: Tabulka vlastností enginu OGRE 3D

KAPITOLA 2. ANALÝZA EXISTUJÍCÍCH ŘEŠENÍ 7 Obrázek 2.4: Ukázka renderingu enginu OGRE 3D. Tento a další screenshoty naleznete na http://www.ogre3d.org. 2.1.5 The Nebula Device 2 Open-source 3D herní a grafický engine vytvořený skupinou Radon Labs[3]. Jedná se o pokračování enginu The Nebula Device, který byl v roce 1998 uvolněn pro veřejnost pod velmi volnou open-source licencí. The Nebula Device 2 vzniká od roku 2002 a snaží se o implementaci nových technologií grafických akcelerátorů. K renderingu je využito API DirectX9 nebo OpenGL. V tabulce 2.5 jsou uvedeny souhrnné informace o enginu. Na obrázku 2.5 můžete posoudit kvalitu osvětlovacího modelu enginu. Podporovaná API DirectX9 nebo OpenGL Vlastnosti Podporované algoritmy jsou: Obecná detekce viditelnosti, scénového Portály, Oktalový strom a Occlusion Culling. managementu Rozšiřitelnost Ano. scénového grafu Možnosti Data jsou čtena z vlastního formátu enginu. V rámci sw vstupních balíku existují pluginy pro 3D editory, které exportují dat data do požadovaného formátu enginu. Výrobce Radon Labs GmbH. Cena licence Zdarma. Tabulka 2.5: Tabulka vlastností enginu The Nebula Device 2

8 KAPITOLA 2. ANALÝZA EXISTUJÍCÍCH ŘEŠENÍ Obrázek 2.5: Ukázka renderingu enginu The Nebula Device 2. Screenshot převzat ze stránek Radon Labs[3]. 2.2 Srovnání enginů a stanovení požadavků V předchozích sekcích jsme si představili některé současné grafické enginy. Nyní provedu analýzu a srovnání vlastností jednotlivých zástupců a pokusím se vymezit základní požadavky, které by měl engine navrhovaný v této práci splňovat. V tabulce 2.6 je srovnání vlastností enginů. Můžeme si všimnout, že některé funkce obsahují všechny enginy a v některých funkcích je velká variabilita. Právě ona variabilita vzniká nejčastěji na základě toho, k čemu přesně byl engine vytvořen. S velmi dobrým řešením, které engine osvobozuje od jeho primárního účelu přišel OGRE 3D. Ten onu variabilitu řeší pomocí zásuvných modulů. Díky tomu není engine svázán s danými již implementovanými algoritmy a je možné ho snadno rozšířit o konkrétní, námi požadované funkce. Samozřejmě nic není zadarmo, takové řešení může způsobit určité zpomalení renderingu. Nicméně, jeho výhody převažují nad nevýhodami a tak u universálního enginu, považuji toto řešení za ideální. Tato práce se snaží vytvořit maximálně obecný engine a proto by měla podobné řešení v návrhu uplatnit. Dále si můžeme všimnou, že enginy mají většinou jen několik daných možností renderingu. Někteří využívají OpenGL, někteří DirectX a výjimečně se objevuje možnost softwarového vykreslování. Navíc volba způsobu vykreslování se provádí buď již při kompilaci zdrojového kódu enginu, nebo při spuštění aplikace, která bude engine využívat. Jakákoli změna renderingu za běhu enginu není možná. Představme si ale aplikaci, ve které interaktivně nastavíme pohled do scény, a poté chceme získat fotorealistický obrázek scény pomocí kvalitního raytracingu 1. Taková aplikace by musela využívat služeb dvou grafických enginů, mezi kterými by bylo nutné data kopírovat. Jeden engine by sloužil pro 1 Jedná se o typ vykreslování trojrozměrných scén, při kterém se simuluje šíření světla ve 3D scéně na základě známých fyzikálních zákonů.

KAPITOLA 2. ANALÝZA EXISTUJÍCÍCH ŘEŠENÍ 9 Vlastnost enginu Enginy CE 2 SE 1.x UE 2 OGRE 3D TND 2 DirectX API Ano Ne Ano Ano Ano OpenGL API Ne Ano Ano Ano Ne Software rendering Ne Ne Ano Ne Ne Možnost ostatních typů Ne Ne Ne Ne Ne renderingu (Raytracer atp.) Základní ořezávání scény Ano Ano Ano Ano Ano Portály Ano Ano plug-in Ano Oktalový strom Ne Ne plug-in Ano Occlusion Culling Ano Ano plug-in Ano Binary Space Partitioning (BSP) Ne Ano plug-in Ne Potentialy Visible Set (PVS) Ne Ano plug-in Ne Level Of Detail (LOD) Ano Ano Ano plug-in Ano Podpora textur Ano Ano Ano Ano Ano Multitexturing Ano Ano Ano Ano Ano Správa materiálů Ano Ano Ano Ano Ano Správa geometrických dat Ano Ano Ano Ano Ano Vstup geometire z různých Ne Ne Ne plug-in Ne formátů Tabulka 2.6: Vzájemné porovnání vlastností enginů. Pro úsporu místa byly v záhlaví použity zkratky názvů enginů. Pomlčka znamená, že ve specifikaci nebyla konkrétní informace uvedena. Poznámka plug-in znamená možnost doplnění dané vlastnosti pomocí zásuvného modulu. interaktivní nastavení scény, a druhý pro raytracing. Druhá možnost, jak daný problém řešit je umožnit přímo v enginu výměnu typu vykreslování bez nutnosti restartu enginu. Samozřejmě je nutné, umožnit uživatelům enginu tvorbu vlastních specializovaných způsobů vykreslování. Tím engine získá další úroveň obecnosti s čímž souvisí i větší záběr aplikací, které mohou služeb enginu využít. V posledním řádku tabulky 2.6 je vidět, že jen málo egninů umožňuje vstup dat z různých formátů. Z pěti zástupců pouze OGRE 3D tuto funkci podporuje. Opět k tomu využívá zásuvných modulů. Jedná se o velmi flexibilní řešení, které mi přijde jako velmi vhodné pro obecný grafický engine o který nám v této práci jde. Posledním věc, kterou můžeme z tabulky 2.6 vyčíst je, že všechny enginy podporují textury, správu materiálů a geometrických dat. Jedná se o základní funkce 3D grafického enginu, které by tedy měly být v jeho jádru implementovány. Nyní si konečně můžeme shrnou požadavky kladené na tuto práci: výběr způsobu vykreslování a to i v průběhu běhu enginu snadná rozšiřitelnost schopností scénového managementu import dat z libovolného formátu správa zdrojů dat (materiálů, textur, geometrie)

10 KAPITOLA 3. STANOVENÍ CÍLŮ PRÁCE 3 Stanovení cílů práce Cílem této diplomové práce je vytvořit zobrazovací jádro, pro vizualizaci třírozměrných dat v reálném čase(tzv. 3D grafický engine) s maximálním důrazem na univerzálnost a rozšiřitelnost výsledného řešení. Ačkoli je na internetu mnoho enginu 1, jak komerčních tak zdarma, většinou mají jednotlivá řešení specifická omezení vzhledem k použitému API, implementovaným funkcím a záměru použití. Nejčastěji se jedná o enginy navrhované pro počítačové hry, kde je hlavním kriteriem rychlost vykreslování. To samozřejmě do značné míry ovlivní jejich obecnost. Dalším nepříjemným důsledkem je úzká svázanost s použitým grafickým API a případně operačním systémem. V této práci se snažím skloubit maximální univerzálnost a rozšiřitelnost s co nejnižší penalizací v rychlosti renderingu. Maximální obecnosti se pokusím dosáhnout následujícími vlastnostmi: 3.1 Nezávislost na grafickém API Nezávislost na grafickém API znamená, že samotný engine není na úrovni zdrojového kódu spjat s některým z existujících API (např. OpenGL[19], DirectX[16]), ale je možné způsob renderingu přizpůsobit vlastním požadavkům, bez nutnosti rekompilace enginu. Díky tomu bude možné vytvořit si vlastní způsob renderingu, který bude přesně vystihovat požadavky aplikace využívající engine. Možnosti přizpůsobení renderingu budou téměř nekonečné a bude tak možné vytvořit rendering například na bázi DirectX, OpenGL, softwarový renderer, foto-realistický raytracer atd. Jako vedlejší efekt nezávislosti na grafickém API je, že jádro enginu bude snadno přenositelné mezi platformami. 3.2 Rozšiřitelnost scénového grafu Scénový graf (dále jen SG) má v 3D enginu několik funkcí. Primárně je určen k jasnému a přehlednému vyjádření logického uspořádání objektů v zobrazované scéně. Objekty SG představují prvky zobrazovaného virtuálního světa. Jsou to například kamery, ze kterých se na náš virtuální svět díváme, světla nasvětlující svět, a konečně geometrické objekty (tzv. Visuály) reprezentující viditelné části scény. Visuály nemusí reprezentovat jen statické objekty (např.geometrie domu), ale mohou být dynamické a obsahovat například animovanou postavu, částicový systém (představte si padající sníh, vodu stříkající z fontány apod.), visuál může měnit složitost geometrie na základě vzdálenosti od kamery (tzv. LOD - Level Of Detail). Tento výčet specializovaných visuálů by mohl být opravdu dlouhý. I přesto, že jsem vyjmenoval pouze tři příklady, je zřejmé, že tyto problémy můžeme řešit různými způsoby. Navíc, svět se stále vyvíjí a s ním i nové algoritmy a postupy pro řešení specifických úloh. Jiná specifika na scénový graf klade počítačová hra a jiná grafický 3D editor typu Blender[1] nebo 3DS MAX TM [7]. Pokud tedy poskytneme možnost snadného doplnění nového specializovaného uzlu do SG, může být celý engine stále aktuální a hlavně je snadné engine přizpůsobit účelu, ke kterému ho chceme využít. 1 Databázi existujících enginu naleznete například na http://www.devmaster.net/engines/

KAPITOLA 3. STANOVENÍ CÍLŮ PRÁCE 11 Dalším důležitou funkcí scénového grafu je nápomoc při řešení viditelnosti jednotlivých částí scény. Ačkoli vývoj grafických akcelerátorů jde velkými kroky kupředu, vždy budou existovat scény mnohonásobně rozsáhlejší a detailnější, než aby mohly být vykreslovány celé v reálném čase. Proto je nutné před zobrazením ze scény odstranit nepodstatné objekty. To které objekty jsou nepodstatné opět závisí na typu aplikace, která bude engine využívat. Pokud budeme uvažovat o reálném světě a vnímání člověka bude se jednat o objekty které nejsou vidět nebo je člověk nevnímá - tedy objekty mimo pohledový jehlan kamery, objekty zakryté bližšími nebo příliš vzdálené apod. Je poměrně mnoho metod jak scénu ořezávat. Každá metoda má svá pro a proti a je koncipována na určitý druh scény. Samotné ořezávání není přímo úkolem scénového grafu, ale je výhodné mít pro něj ve SG podporu. V této práci budu také usilovat o možnost kombinování jednotlivých metod ořezávání v jedné scéně, neboť předpokládám, že se tím zvýší celková efektivita ořezání. Pokud tedy ve výsledku poskytneme možnost vytvořit si vlastní typy ořezávání scény a umožníme jejich kombinování, opět o podstatnou část zobecníme celý engine a jeho možnosti nasazení. 3.3 Import vstupních dat z různých formátů Data pro vizualizaci často dostáváme v různém formátu. Je potom nutné data různě konvertovat do některého ze standardních formátů, což přináší zdržení a možnost ztráty některé cenné informace, která se do výsledného formátu nedá zapsat. Mým cílem je umožnit vstup jak 2D dat - textury, tak 3D dat - scény do enginu z jakéhokoliv formátu. Tím se provede proces konverze a načtení dat v jednom kroku a jediná data, která se tak budou moci ztratit, jsou ta, která engine nedokáže zobrazit. Další výhodou je, že si můžeme stejná data zobrazit různými způsoby. 3.4 Systém modulů Všechny předchozí body, které umožní velkou flexibilitu enginu je nutné podpořit také jasnými pravidly, která budou definovat, jakým způsobem do enginu nové funkce přidat. Takováto pravidla budou vytvořena unifikovaným systémem zásuvných modulů (pluginů). Díky tomu bude možné přidávat schopnosti enginu přímo za jeho běhu a to bez nutnosti rekompilace. Moduly nám umožní import dat z různých formátů, zpřístupní různé druhy vykreslování a umožní zavést nové typy uzlů scénového grafu jednotným způsobem.

12 KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 4 Analýza a návrh řešení V této kapitole se se budeme věnovat návrhu enginu. Než se však začneme podrobně věnovat jednotlivým částem enginu, zavedeme si důležité pojmy a skutečnosti, které je nutné mít na paměti při návrhu enginu. Jedná se převážně o pojmy z oblasti trojrozměrné grafiky, ze kterých pak vychází jednotlivé konkrétní kroky návrhu. Jako podkladové materiály pro tuto kapitolu byly použity zdroje z [10, 11, 19, 16, 13]. 4.1 Trojrozměrný prostor Trojrozměrný (3D) prostor můžeme definovat pomocí Kartézské soustavy souřadnic. Takový prostor je potom definován třemi bázovými vektory, které jsou na sebe kolmé a protínají se v jednom bodě počátku soustavy souřadnic. Jedná se vlastně o známý dvojrozměrný prostor doplněný o další osu, která je kolmá na plochu definovanou osami x a y. Třetí osa je pojmenována z. To v jakém směru je osa z přidružena k x a y rozhoduje o tom zda se jedná o levotočivý nebo pravotočivý souřadnicový systém. Na obrázku 4.1 je vidět jak lze snadno zjistit o jaký souřadnicový systém se jedná. Obrázek 4.1: systému. Uspořádání os levotočivého (a) a pravotočivého (b) souřadnicového Všechny body v 3D prostoru můžeme vyjádřit pomocí vektoru, který vyjadřuje posunutí vzhledem k počátku soustavy souřadnic. Jednotlivé vektory v 3d prostoru vyjadřujeme pomocí lineární kombinace bázových vektorů souřadnicového systému. v = xb 1 + yb 2 + zb 3 x,y,z R (4.1) Skaláry x,y,z jsou souřadnice vektoru v v bázi b 1,b 2,b 3 a reprezentují vektor v v tomto prostoru jednoznačně. Bod v 3D prostoru tedy vyjadřujeme pomocí jeho souřadnic [x,y,z]. V počítačové grafice se bod ve 3D prostoru označuje jako vertex. 4.2 Způsoby reprezentace trojrozměrných modelů Abychom mohli vizualizovat nějaký trojrozměrný objekt, je nutné nejprve vytvořit jeho model. Modelem chápeme zjednodušené zobrazení zkoumané skutečnosti z reálného světa. Stejný model můžeme reprezentovat různými způsoby. V počítačové grafice se používají dvě základní reprezentace modelu.

KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 13 4.2.1 Objemová reprezentace Objemová reprezentace je velmi jednoduchá metoda modelování tělesa. Spočívá v tom, že rozdělíme prostor na m n k stejných krychliček (tzv. voxely). Dále pak pro reprezentaci konkrétního modelu stačí uvést seznam, který určuje jaké voxely jsou součástí modelu. Jedna z hlavních nevýhod této reprezentace je její značná paměťová náročnost. Existují struktury, které dokáží částečně snížit paměťovou náročnost, nicméně jedná se o reprezentaci, která není příliš vhodná pro vizualizaci interaktivních světů. Zásadním důvodem je, že současné grafické akcelerátory nemají přímou podporu pro vykreslování těles v objemové reprezentaci. Dále pak v její neprospěch hraje operační složitost algoritmů, pracujících s takovouto reprezentací. Jejich složitost zpravidla dosahuje řádu O(m n k). Objemová reprezentace se využívá například při vizualizaci statických lékařských dat, získaných pomocí počítačové tomografie. Na obrázku 4.2 můžete vidět příklad objemové reprezentace modelu postavičky. Obrázek 4.2: Objemová reprezentace modelu postavičky. 4.2.2 Hraniční reprezentace Druhou možností reprezentace trojrozměrného modelu je pomocí jeho hranice. Hranice modelu se rozloží do konečného počtu stěn. Stěnami modelu mohou být známé rotační plochy, parametrické plochy apod. Nejčastěji se však používají rovinné plošky a zakřivené plochy se jimi aproximují (viz. 4.3). Nejjednodušší rovinou ploškou je trojúhelník. Současný grafický hardware je optimalizován právě na rychlé vykreslování trojúhelníků, přičemž ostatní typy polygonů jako čtyřúhelníkové polygony a podobně skládá z trojúhelníků. Je mnoho způsobů jak reprezentovat plošky modelu v paměti. Abychom však rychlost vykreslování zbytečně nezatěžovali konverzemi mezi reprezentacemi, bude vhodné přizpůsobit volbu konkrétní reprezentace požadavkům grafického hardware. Budeme tedy v našem enginu pro popis modelů využívat hraniční reprezentace.

14 KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ Obrázek 4.3: Hraniční reprezentace modelu krychle a koule. 4.3 Základní bloky grafického enginu Na obrázku 4.4 je uspořádání základních bloků grafického enginu. V době, kdy počítače ještě zdaleka nedosahovaly současných výkonů, byly všechny tyto bloky z důvodu rychlosti vykreslování opravdu těsně svázány a dá se říci, že se částečně prolínaly. Díky tomu nebyly dřívější enginy tak univerzální a rychle zastarávaly, neboť na ně byly postupně kladeny stále větší požadavky a udržování a vylepšování tak velkého a složitě propojeného systému nebylo možné. Díky tomu se také vývoj vydal směrem k větší obecnosti a vznikl koncept se třemi základními bloky. V současné době mají všechny grafické enginy v základu uspořádání jako je na obrázku 4.4. Nyní si popíšeme funkci jednotlivých bloků. Prvním z nich je správce zdrojů dat. Úkolem tohoto bloku je udržovat potřebnou geometrii a textury v paměti, zodpovídá za korektní tvorbu a rušení zdrojů. Dále pak poskytuje možnost sdílení dat mezi více modely. Díky tomu je možné mít ve světě například více postav, ale jejich reprezentaci máme v paměti pouze jednou. Správce zdrojů je vlastně specializovaná databáze. Dalším velmi důležitým blokem enginu je management scény. Jedná se o poměrně komplikovaný blok, který velkou měrou ovlivňuje schopnosti celého enginu. Jeho úkolem je umožnit uživateli vytvořit si scénu, kterou chce následně zobrazovat. K tomu uživatel využívá dat ze správce zdrojů a struktur scénového managementu. Management scény dále pak obsahuje algoritmy, které umožňují různé typy dotazů na konkrétní scénu. Příkladem takového dotazu může být dotaz na aktuální kameru, kterou se bude svět vykreslovat, nebo jaké části světa jsou danou kamerou viditelné. Tento blok také zavádí určitá pravidla, která je nutno při tvorbě vlastní scény dodržovat. Kvalita návrhu scénového managementu rozhoduje asi největší měrou o následných možnostech rozšíření enginu. Posledním nutným blokem pro plnou funkčnost enginu je renderer. Jedná se o blok, který se stará o samotné převedení virtuální scény do grafické podoby na obrazovku. Jeho výstupem je tedy vykreslený snímek našeho virtuálního světa. K tomu, aby mohl renderer vykreslit scénu, potřebuje data ze správce zdrojů a popis scény. Na obrázku 4.4 je vidět, že renderer a správce zdrojů synchronizují svá data. To je poměrně časově náročná činnost neboť grafická data jsou poměrně objemná, a v rozsáhlých scénách mo-

KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 15 hou dosahovat až jednotek GB i více. Proto v enginech, které jsou vytvářeny nad jedním grafickým API, je manažer zdrojů postaven přímo nad strukturami používanými v rendereru. Díky tomu se engin zbaví zbytečné synchronizace před vykreslením snímku, ale stane se závislým na použitém grafickém API. Předání popisu scény rendereru je poměrně jednoduchá a časově nenáročná operace. Grafický 3D engine Správa geometrie a textur (zdroje dat) Synchronizace zdrojů dat a rendereru Data použitá ve scéně, update dynamických dat Management scény (scénový graf) Scéna a aktuální kamera Renderer Obrázek 4.4: Základní bloky grafického enginu a jejich datová komunikace. 4.4 Správce zdrojů dat Jak již bylo řečeno výše, jedná se o blok enginu, který se stará o zdroje dat použité ve scéně. Samotná data můžeme rozdělit do dvou skupin. Jednu skupinu tvoří geometrická data, která popisují tvar modelu. Druhá skupina jsou textury, které se používají pro doplnění rozličných povrchových detailů modelu. 4.4.1 Centralizovaný a decentralizovaný přístup Správce zdrojů může být centralizovaný nebo decentralizovaný. Centralizovaný správce provádí veškeré operace se zdroji pomocí jednoho rozhraní. V celém enginu je pak zdroj dat zastoupen pouze unikátním ID. Díky tomu má správce snadný přehled nad aktuálním stavem jednotlivých zdrojů dat, neboť veškerá komunikace se zdroji probíhá přes správce. Nicméně tento způsob není příliš komfortní pro uživatele enginu.

16 KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ Decentralizovaná správa nám přináší specifická rozhraní pro jednotlivé typy zdrojů dat, která nám usnadňují práci se samotnými daty zdroje. Díky tomu je používání zdrojů v enginu mnohem snazší. Další příjemnou vlastností je, že konkrétní zdroj je v enginu zastoupen přímo odkazem na své rozhraní. Decentralizovaná správa má ovšem nevýhodu v tom, že jednotlivá rozhraní musí sama obsahovat logiku spravování zdroje. Další velkou nevýhodou je roztroušenost zdrojů dat tedy nemožnost dotázat se na stav všech existujících zdrojů z konkrétního místa enginu. V této práci jsem navrhl kombinovaný správce zdrojů, který využívá výhod obou systémů (viz. obrázek 4.5). Každý typ zdroje má vlastní rozhraní, a ty jsou detailně popsána v následujících kapitolách. Samotná kombinace decentralizovaného a centralizovaného přístupu spočívá v tom, že jednotlivé zdroje je možno vytvořit pouze z jediného místa enginu, které nazývám Core. Díky tomu si může Core uchovávat tabulku existujících zdrojů, a je možné se kdykoliv dotazovat na stav libovolného zdroje dat. Logika obsažená v jednotlivých typech zdrojů dat je z shodná a umožňuje decentralizované zrušení a klonování zdroje. Každý zdroj také obsahuje referenci na Core, které ho vytvořilo. Díky tomu může probíhat komunikace mezi zdroji a Core obousměrně a je tak snadné upozornit Core například na změnu obsahu zdroje, nebo o jeho plánovaném zrušení. O tom zda zdroj při svém rušení zanikne či nikoli, rozhoduje opět logika v konkrétním zdroji. Ta si počítá počet modelů, ve kterých jsou data využita. V případě, že již data nejsou nikde použita, zdroj dat se zruší a uvolní paměť. Core Reference Výroba zdrojů dat Zdroj geometrie Distribuovaná logika zdroje Komunikace logiky Centrální logika správy zdrojů Zdroj textury Distribuovaná logika zdroje Správa zdrojů dat Obrázek 4.5: Správce zdrojů s kombinací centralizované a decentralizované logiky. 4.4.2 Nezávislost správce zdrojů na grafickém API Jedním z požadavků na engine navrhovaný v této práci, je nezávislost na grafickém API a možnost změny způsobu vykreslování za běhu enginu. Dále je požadováno, aby nevznikla příliš velká penalizace v rychlosti vykreslování scény. Tyto požadavky nejvíce ovlivňují návrh rozhraní jednotlivých zdrojů. Jak jsem se již zmínit dříve, enginy, které jsou závislé na grafickém API, vytváří správce zdrojů přímo nad strukturami použitého

KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 17 API. To je velmi výhodné, neboť není nutné po změně dat zdroje provádět zdlouhavou synchronizaci s rendererem, neboť se data změnila přímo ve strukturách rendereru. Ovšem abychom měli engine nezávislý na grafickém API, je nutné si vytvořit vlastní struktury, nad kterými budou zdroje dat pracovat. Dále pak požadavek na možnost výměny rendereru za běhu enginu nutně vytváří okamžik, kdy není žádný renderer v enginu přítomen. Ovšem ani v tomto okamžiku není možné data ztratit a je nutné je někde prozatímně uchovat, než se připojí jiný renderer. a) Rozhraní zdroje Rozhraní zdroje (klon I) Distribuovaná logika zdroje Distribuovaná logika zdroje Struktury enginu Reference Sdílené informace Konkrétní data zdroje Odpojený Renderer Rozhraní zdroje (klon II) Distribuovaná logika zdroje b) Rozhraní zdroje Rozhraní zdroje (klon I) Rozhraní zdroje (klon II) Distribuovaná logika zdroje Distribuovaná logika zdroje Distribuovaná logika zdroje Struktury enginu Reference Sdílené informace Data rendereru Reference Struktury Rendereru Vlastní struktua rendereru pro uchování dat zdroje Konkrétní data zdroje Obrázek 4.6: Způsob uchování dat zdroje při odpojeném (a) a připojeném (b) rendereru. Nakonec se mi podařilo všechny tři požadavky skloubit následujícím způsobem. Jednotlivé zdroje využívají vlastních struktur pro uchování dat. Pokud tedy není k enginu připojen žádný renderer, data jsou uchována ve vlastních strukturách. V okamžiku, kdy se připojí k enginu renderer, centrální logika správce zdrojů zajistí vytvoření všech zdrojů v rendereru. Dále pak provede synchronizaci dat s rendererem. Samotný ren-

18 KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ derer má možnost si vybrat, zda využije služeb struktur enginu, nebo si data překopíruje do vlastních struktur. Pokud renderer spravuje data ve vlastních strukturách, data ve strukturách enginu se zruší a požadavky, které pracují s daty, jsou pak přesměrovány až do rendereru. Díky tomu pak není nutné u rendererů, které dokáží spravovat data ve vlastních strukturách, provádět synchronizaci dat. To zda renderer pro konkrétní zdroj využije struktury enginu nebo vlastní je nutné sdělit logice zdroje, což se částečně projeví i na rozhraní rendereru. Samozřejmě v celém enginu se na zdroje odkazujeme referencemi na jejich rozhraní. Stejně tak se děje i v samotném rendereru. Ten tedy, pokud má data uložená ve vlastních strukturách, musí mít možnost získat odkaz na vlastní strukturu spřaženou s konkrétním zdrojem. Všechna rozhraní tedy musí tuto funkci umožnit. Na obrázku 4.6 je vidět situace, kdy není žádný renderer připojen (a). Tento obrázek je shodný i se situací, kdy renderer neukládá data zdrojů do vlastních struktur. Obrázek 4.6 (b) ukazuje způsob navázání vlastních struktur rendereru na struktury enginu. Na obrázku 4.6 je také vidět způsob sdílení dat zdrojů. 4.4.3 Geometrie modelů Jak již bylo zmíněno výše, tak grafický hardware je uzpůsoben pro vykreslování modelů v povrchové reprezentaci. Způsobů popisu modelu v povrchové reprezentaci je několik a můžete se o nich více dozvědět v [11]. My se opět přizpůsobíme reprezentaci používané v grafickém akcelerátoru. Popis povrchu tedy budeme reprezentovat tabulkou vertexů a tabulkou indexů. Jednotlivé vertexy definují body na povrchu modelu. Indexy pak odkazují do tabulky vertexů a definují výslednou posloupnost vertexů. Tato posloupnost potom vytváří jednotlivé stěny modelu. Abychom měli popis kompletní je ještě nutné definovat typ grafického elementu - tedy zda posloupnost generuje množinu trojúhelníků, čtyřúhelníků, nebo polygon apod. Podrobnosti o typech grafických elementů naleznete opět v [11]. Nyní již tedy víme, že pro popis modelu potřebujeme dva typy zdrojů dat - tabulku vertexů a tabulku indexů. Buffer vertexů Na první pohled se může zdát, že na tabulce vertexů není potřeba nic navrhovat. Nicméně jak se schopnosti trojrozměrné grafiky rozrůstaly, tak se k vertexu postupně začaly přidávat další informace, které jsou svázány s konkrétním místem na modelu tedy jsou svázány s daným vertexem. Postupně se tedy k vertexu přiřadila například informace o normále povrchu 1, barva modelu v daném místě, nebo texturové koordináty, které definují způsob mapování textur na povrch modelu. Navíc různé modely přiřazují vertexům různé přídavné informace. Tím se z jednoduché tabulky stává poměrně variabilní zdroj dat. Je tedy nutné aby buffer vertexů podporoval variabilní složení vertexu. Na obrázku 4.7 (a) je příklad jednoduchého vertexu složeného z pozice a normály. Obrázek 4.7 (b) ukazuje složitější vertex, obsahující nejen pozici a normálu ale také difusní a specular barvu povrchu a koordináty pro dvě vrstvy textur. Nyní je nutné stanovit jaké konkrétní informace chceme ve vertexu podporovat. Studiem API OpengGL[19] a DirectX[16] jsem došel ke konkrétním složkám vertexu, které by měl současný grafický engine podporovat. Jednotlivé složky vertexu a jejich 1 Normála je vektor kolmý na povrch modelu a využívá se hlavně pro výpočet osvětlení. Podrobnější informace naleznete v [11]

KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 19 význam jsou popsány v tabulce 4.1. Z předchozího plyne, že rozhraní bufferu vertexů musí mít možnost definovat složení vertexu. Konkrétní návrh minimálního rozhraní splňující všechny výše zmíněné požadavky je na obrázku 4.8. Vertex Position Normal Vertex Position Normal Diffuse color Specular color Texture1 coord Texture2 coord a) b) Obrázek 4.7: Příklad jednoduchého (a) a složitějšího (b) vertexu. Složka vertexu Význam Position XYZ Pozice vertexu v 3D prostoru. Position XYZW Transformovaná pozice vertexu v homogenních souřadnicích. Normal Normála k povrchu modelu v daném bodě. Diffuse color Difuzní barva pro výpočet osvětlení v daném bodě modelu. Specular color Barva zrcadlové složky při výpočtu osvětlení v daném bodu modelu. Point size Definuje velikost vykreslovaného bodu u bodových modelů. Texture1 Texture8 Až osm vektorů definujících způsob mapování textur na coordinates povrch modelu. Vektory mohou být 1D 4D. Tabulka 4.1: Tabulka možných složek vertexu a jejich význam. Buffer indexů Rozhraní bufferu indexů je velmi podobné rozhraní vertex bufferu. Index buffer je jednoduchá tabulka celých čísel. Jedná se tedy o nejjednodušší ze všech typů zdrojů dat. Konkrétní návrh rozhraní je na obrázku 4.9. 4.4.4 Textury Texturu si můžeme v tom nejjednodušším případě představit jako 2D obrázek. Pokud ovšem půjdeme více dopodrobna zjistíme, že současný HW podporuje více typů textur. Dále si k textuře musíme přidat takzvané mipmapy. Mipmapa je vlastně původní textura zmenšená na poloviční rozlišení. Mipmap se využívá v rendereru při mapování textur na vykreslované polygony. Dle velikosti polygonu na obrazovce se vybere nevhodnější velikost mipmapy pro mapování. Více podrobností o užití mipmap naleznete v [11].

20 KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ Rozhraní zdroje typu Vertex Buffer Společné rozhraní všech zdrojů dat Clone vytvoří kopii zdroje Release zruší zdroj (nebo jeho klon) Set Vertex Format nastaví formát vertexu v bufferu Set Size nastaví počet vertexů v bufferu Lock zpřístupní data uložená v bufferu Unlock informuje buffer o dokončení změn dat Obrázek 4.8: Návrh rozhraní zdroje vertexů. Rozhraní zdroje typu Index Buffer Společné rozhraní všech zdrojů dat Clone vytvoří kopii zdroje Release zruší zdroj (nebo jeho klon) Set Size nastaví počet indexů v bufferu Lock zpřístupní data uložená v bufferu Unlock informuje buffer o dokončení změn dat Obrázek 4.9: Návrh rozhraní zdroje indexů. Základní textury (Texture) Pokud se tedy bude mluvit o textuře, budeme mít na mysli 2D obrázek, který může mít k sobě přiřazen určitý počet mipmap. Mipmapy jsou zpravidla generovány až do velikosti 1x1 pixel. Objekt textury je tedy jakýsi kontejner na jednotlivé mipmapy, přičemž mipmapa nulté úrovně je náš původní obrázek. Textura se tedy stává speciálním typem zdroje dat, neboť neobsahuje přímo obrazová data, ale jen seznam struktur, které obsahují data mipmapy. Strukturu obsahující data mipmapy budeme nazývat Surface. Surface tedy obsahuje přímo pixelová data dané mipmapy a je nutné ho synchronizovat s redererem. Jedná se podobný zdroj dat jako je například buffer vertexů nebo indexů. Na obrázku 4.10 je vidět vztah textury a surface. Z obrázku plyne, že rozhraní textury bude jiné než rozhraní surfacu. Textura musí být schopna podat informaci o počtu surfaců a zpřístupnit je. Surface naopak musí být schopen zpřístupnit přímo pixelová data, podat informace o formátu pixelu, o rozměru plochy. Návrh rozhraní textury je na obrázku 4.11 (a) a rozhraní surface 4.11 (b). Speciální případem textury je 1D textura, což je textura, která má pouze jeden řádek barevné informace a na geometrické objekty se mapuje jednorozměrnou texturovou koordinátou. Tato textura je pouze speciálním případem 2D textury. Při vytváření nové textury a její následné synchronizaci s rendererem musí být zajištěno toho, že renderer se nejprve dozví o nové textuře a všech jejích parametrech, a teprve poté se začnou synchronizovat jednotlivé Surface dané textury. Tento postup je nutný, neboť renderer nejprve musí vytvořit texturu ve vlastních strukturách a poté teprve

KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 21 může provést synchronizaci dat. Jinak řečeno, nelze synchronizovat Surface neexistující textury. Podobný postup musí být uplatněn u všech zdrojů kontejnerového typu, tj.: Texture, Cube Texture a Volume Texture. Texture Surface 0 Surface 1 Surface 2 Surface 3 Surface 4 Mipmap Data 16x16 pixels Mipmap Data 8x8 pixels Mipmap Data 4x4 pixels Mipmap Data 2x2 pixels Mipmap Data 1x1 pixels Obrázek 4.10: Struktura textury velikosti 16x16 pixelů. Rozhraní zdroje typu Texture Společné rozhraní všech zdrojů dat Clone vytvoří kopii zdroje Release zruší zdroj (nebo jeho klon) GetLevelCount zjistí počet mipmap textury GetLevelDesc vrátí popis dané mipmapy GetSurfaceLevel vrátí Surface mipmapy Rozhraní zdroje typu Surface Společné rozhraní všech zdrojů dat Clone vytvoří kopii zdroje Release zruší zdroj (nebo jeho klon) GetContainer vrátí texturu do které Surface patří GetDescription vrátí popis Surface (mipmapy) LockRect zpřístupní pixelová data UnlockRect informuje o dokončení změn dat a) b) Obrázek 4.11: Návrh rozhraní zdroje typu Texture (a) a zdroje dat typu Surface (b). Cube textury (Cubic Environment Maps) Cube textury jsou speciálním typem textury. Jedná se o textury, které obsahují obrazová data reprezentující scénu obklopující objekt, tak jak ji vidí objekt ze středu krychle. Každá stěna cube textury pokrývá devadesáti stupňový úhel pohledu v horizontálním a vertikálním směru. Cube textura je tvořena šesti stěnami. Orientace stěn je vidět na obrázku 4.12. Cube textury jsou implementovány jako sada textur. Nabízí se myšlenka implementovat cube textury v enginu jako kontejner na šestici 2D textur. Řešení je to lákavé, ale má jednu nevýhodu v podobě distribuované logiky zdroje. Pokud bychom vytvářeli cube texturu tímto způsobem, neměli bychom plnou moc nad způsobem jakým se bude cube textura synchronizovat s rendererem. Z toho důvodu je nutné navrhnout objekt cube textury jako nový typ kontejneru pro jednotlivé surface, s vlastní logikou synchronizace. Při návrhu je nutné vzít v úvahu, že jednotlivé stěny mohou mít mipmapy. Uspořádání kontejneru cube textury je na obrázku 4.13 (b) a návrh rozhraní na obrázku 4.13 (a). Rozhraní zpřístupňuje jednotlivé stěny cube mapy včetně všech mipmap dané stěny. Pixelová data je poté možno měnit, stejně jako u standardních textur, pomocí

22 KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ rozhraní Surface. Metody GetLevelDesc a GetCubeMapSurface musí mít na vstupu uvedeno jaké stěny a kolikáté mipmapy se operace bude týkat. Face 2 -Z -X +Y +Z +X Face 1 +Y -X +Y +X Face 4 +Y +Z Face 0 +Y +X Face 5 +Y -Z +Z +X -Z -X -Z -Y Face 3 +Z -Y +X Obrázek 4.12: Orientace stěn cube textury. Objemové textury (Volume Texture) Objemové textury jsou třídimenzionální kolekce pixelů, které mohou být mapovány na dvourozměrné primitivy jako například trojúhelník nebo přímka. Pro mapování na primitivy je nutno použít trojrozměrných texturových koordinát. V okamžiku, kdy se primitivum vykresluje, je každý pixel vybarven barvou pixelu z vnitřku objemové textury, podomně jako tomu je v případě dvoudimenzionálních textur. Objemové textury se nevykreslují přímo, neboť nemáme třídimenzionální primitivy na které by mohla být objemová textura nanesena. Tyto textury se nejčastěji využívají k různým druhům speciálních efektů jako je mlha, exploze, kouř atd. Objemové textury jsou organizovány do řezů. Každý řez je dvourozměrná plocha s danou šířkou a výškou. Tyto řezy jsou složeny za sebe a vytváří tak hloubku objemu. Objemové textury mohou mít více úrovní detailů (level), podobně jako jsou mipmapy u dvourozměrných textur. V každé nižší úrovni detailů jsou rozměry objemové textury zmenšeny na polovinu, oproti předchozí úrovni. Na obrázku 4.14 je vidět uspořádání objemové textury se třemi úrovněmi detailů. Rozhraní pro objemové textury bude podobné jako rozhraní pro standardní dvourozměrné textury. Opět se bude jednat o kontejner struktur. Tyto struktury nazveme Volume. Struktura Volume bude uchovávat data jedné úrovně detailů objemové textury. Rozhraní struktury Volume musí umět podat informace o rozměrech objemu a formátu pixelu. Dále pak musí umožnit přístup k pixelům (texelům) objemu. Návrh rozhraní Volume Texture je na obrázku 4.15 (a) a návrh rozhraní Volume na obrázku 4.15 (b). 4.5 Struktura FaceGroup Samostatné zdroje dat nám pro vykreslení trojrozměrného objektu nestačí. Například samotný buffer vertexů sice obsahuje množinu trojrozměrných bodů, ale není řečeno, co

KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 23 Cube Texture Face 0 (+X) Surface 0 Mipmap Data 16x16 pixels Surface 1 Mipmap Data 8x8 pixels Surface 2 Mipmap Data 4x4 pixels Surface 3 Mipmap Data 2x2 pixels Surface 4 Mipmap Data 1x1 pixels Rozhraní zdroje typu Cube Texture Face 1 (-X) Společné rozhraní všech zdrojů dat Clone vytvoří kopii zdroje Release zruší zdroj (nebo jeho klon) GetLevelCount zjistí počet mipmap textury GetLevelDesc vrátí popis dané mipmapy GetCubeMapSurface vrátí Surface mipmapy pro danou face cube mapy Surface 0 Mipmap Data 16x16 pixels Surface 1 Mipmap Data 8x8 pixels Surface 2 Mipmap Data 4x4 pixels Face 2 (+Y) Face 3 (-Y) Face 4 (+Z) Face 5 (-Z) Surface 3 Mipmap Data 2x2 pixels Surface 4 Mipmap Data 1x1 pixels a) b) Obrázek 4.13: Návrh rozhraní cube textury (a) a vnitřní uspořádání kontejneru (b). Level 0 (8x4x2) Level 1 (4x2x1) Level 2 (2x1x1) Depth Height Depth Height Depth Height Width Width Width Obrázek 4.14: Uspořádání objemové textury se třemi údovněmi detailů. Rozhraní zdroje typu Volume Texture Společné rozhraní všech zdrojů dat Clone vytvoří kopii zdroje Release zruší zdroj (nebo jeho klon) GetLevelCount zjistí počet úrovní detailů GetLevelDesc vrátí popis dané úrovně detailů GetVolumeLevel vrátí strukturu Volume požadované úrovně detailů Rozhraní zdroje typu Volume Společné rozhraní všech zdrojů dat Clone vytvoří kopii zdroje Release zruší zdroj (nebo jeho klon) GetContainer vrátí texturu do které Volume patří GetDescription vrátí informace o tomto Volume LockBox zpřístupní data objemu UnlockBox informuje o dokončení změn dat a) b) Obrázek 4.15: Návrh rozhraní zdroje typu Volumue Texture (a) a rozhraní zdroje typu Volume (b).

24 KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ tyto body představují a zda se mají vykreslit jako sada napojených přímek (line list), nebo sada trojúhelníků a podobně. Stejně tak buffer indexů nemá řečeno, co indexy indexují a textury neví jak a kam se mají mapovat. K doplnění těchto chybějících údajů slouží právě pomocné struktury jako je FaceGroup a Material. Struktura FaceGroup spojuje buffer vertexů a buffer indexů a doplňuje informaci, jakým způsobem má být sada vertexů, nebo indexovaných vertexů interpretována. Tedy jaký typ primitivy se má při vykreslování použít. Tím FaceGroupa definuje geometrický tvar trojrozměrného objektu. Poslední informací, kterou FaceGroup přináší je materiál, který se bude při vykreslování objektu používat. FaceGroup je nejmenší prvek scény, se kterým bude renderer enginu pracovat při vykreslování. Na obrázku 4.16 jsou typy primitiv, které by měla FaceGroupa podporovat. Dále je vhodné podporovat jak indexované, tak i neindexované množiny vertexů. Geometrie objektů u neindexovaných FaceGroup je tvořena pouze informací o typu vykreslované primitivy a bufferem vertexů. Je tedy dovoleno aby FaceGroup neobsahovala odkaz na buffer indexů. Každá FaceGroup přiřazuje právě jeden materiál. Pokud je tedy objekt složen z více častí z různých materiálů musí se i v enginu složit pomocí více FaceGroup. Seznam Facegroup je uložen ve strukturách scénového grafu, konkrétně to zajišťuje uzel typu Visual. V1 V3 V1 V3 V1 V3 V0 V2 V0 V2 V0 V2 POINT LIST LINE LIST LINE STRIP V1 V3 V5 V1 V3 V5 V1 V2 V0 V2 V4 TRIANGLE LIST V0 V2 V4 TRIANGLE STRIP V0 TRIANGLE FAN V3 Obrázek 4.16: Typy primitiv podporovaných v enginu. 4.6 Struktura Material Materiál je další pomocnou strukturou, která uvádí jednotlivé zdroje dat do kontextu s ostatními informacemi nutnými pro rendering. U materiálů se jedná hlavně o způsob použití textur, jejich vzájemné míchání (blending), způsob mapování na model. Materiály dále přináší spoustu dalších informací v podobě koeficientů pro výpočet osvětlení, způsobu vykreslování plošných primitiv nebo nastavení průhlednosti objektu. Strukturu materiálu je nutné dobře navrhnout, neboť schopnosti materiálů z velké části ovlivňují visuální možnosti celého enginu. Pokud prostudujeme současná grafická API zjistíme, že

KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 25 podporují podobné vlastnosti. V tabulce 4.2 jsou uvedeny parametry materiálu, jejich typy a význam, které by měl engine podporovat. Parametr Typ Význam AMBIENT 4D vektor Ambientní koeficienty materiálu, více naleznete v [11]. DIFFUSE 4D vektor Difuzní koeficienty materiálu, více naleznete v [11]. SPECULAR 4D vektor Spekulární koeficienty materiálu, více naleznete v [11]. EMISSIVE 4D vektor Emisní koeficienty materiálu, více naleznete v [11]. POWER Real Vyjadřuje ostrost spekulárního odlesku. CULLMODE Výčet Mód ořezávání vykreslovaných polygonů. Nabývá vždy jedné z hodnot z tabulky 4.3. FILLMODE Výčet Mód vyplňování vykreslovaných polygonů. Nabývá vždy jedné z hodnot z tabulky 4.4. SHADEMODE Výčet Mód výpočtu stínování polygonu. Nabývá vždy jedné z hodnot z tabulky 4.5. ZENABLE Boolean Pro hodnotu TRUE je před zápisem pixelu do výstupního plátna proveden hloubkový test. Vypnutím hloubkového testu můžeme docílit speciálních efektů. LIGHTING Boolean Parametr určuje zda se bude provádět výpočet osvětlení pro polygony vykreslované s tímto materiálem. SPECULAR Boolean Parametr určuje, zda se ve výpočtu osvětlení bude ENABLE ALPHABLEND ENABLE Boolean uvažovat spekulární složka. Parametr určuje, zda se bude před zápisem pixelu provádět míchání barev s pixelem z výstupního plátna. Způsob míchání barev pixelů je určen parametry materiálu SRCBLEND a DESTBLEND. SRCBLEND Výčet Způsob úpravy zdrojové barvy před mícháním. Jednotlivé způsoby výpočtu jsou uvedeny v tabulce 4.6. DESTBLEND Výčet Způsob úpravy barvy pixelu z výstupního plátna před mícháním. Jednotlivé způsoby výpočtu jsou uvedeny v tabulce 4.6. Tabulka 4.2: Parametry materiálu enginu. Parametry uvedené v tabulce 4.2 jsou však pouze základ materiálu. Další informace, které materiál přináší, jsou textury. Současná API podporují vykreslování polygonů až s osmi vrstvami textur najednou. Využití osmi textur najednou je poměrně extrémní případ, ale poměrně často se využívá dvou až šesti vrstev. V materiálu budeme tedy podporovat až osm textur. Pro každou texturu je nutné říci, jakým způsobem se bude barevně míchat s ostatními texturami a jaké texturovací koordináty mají být pro mapování textury použity. Jednotlivé textury a nastavení jejich blending stage je vázáno na index. První textura a blending stage je na indexu nula. Na obrázku 4.17 je vidět způsob míchání textur a indexování blending stages, tak jak je myšleno v materiálu enginu. Samozřejmě není nutné využívat všech blending stages, pokud aplikace potřebuje například dvě textury, nastaví do materiálu tyto dvě textury a parametry pro dvě blending stages a ostatní blending stages jsou ignorovány mají nastaven parametr COL-

26 KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ Název CW CCW NONE Popis Při vykreslování nebudou vykresleny polygony, které jsou ke kameře přední stranou. Za přední stranu je považována stěna tvořená vertexy jdoucími ve směru hodinových ručiček vzhledem ke kameře a středu polygonu. Při vykreslování nebudou vykresleny polygony, které jsou ke kameře zadní stranou. Za zadní stranu je považována stěna tvořená vertexy jdoucími proti směru hodinových ručiček vzhledem ke kameře a středu polygonu. Při vykreslování nebudou žádné polygony ořezávány. Tabulka 4.3: Způsoby ořezávání polygonů hodnoty kterých může v materiálu nabývat parametr CULLMODE. Název Popis POINT Polygon bude vykreslen jako seznam bodů, kterými je definován. WIREFRAME Polygon bude vykreslen jako seznam hraničních přímek. SOLID Polygon bude vykreslený včetně výplně. Tabulka 4.4: Způsoby vylňování polygonů hodnoty kterých může v materiálu nabývat parametr FILLMODE. Název Popis FLAT Pro celý polygon je vybrána barva, vypočtená pro první vertex polygonu. GOURAND Pro všechny body polygonu je vypočtena barva dle parametrů materiálu a světel. Barva je poté uvnitř polygonu lineárně interpolována. Tabulka 4.5: Způsoby stínování polygonů hodnoty kterých může v materiálu nabývat parametr SHADEMODE. Název Popis ZERO Blend faktor je (0, 0, 0, 0). ONE Blend faktor je (1, 1, 1, 1). SRCCOLOR Blend faktor je (R s,g s,b s,a s ). INVSRCCOLOR Blend faktor je (1 R s, 1 G s, 1 B s, 1 A s ). SRCALPHA Blend faktor je (A s,a s,a s,a s ). INVSRCALPHA Blend faktor je (1 A s, 1 A s, 1 A s, 1 A s ). DESTALPHA Blend faktor je (A d,a d,a d,a d ). INVDESTALPHA Blend faktor je (1 A d, 1 A d, 1 A d, 1 A d ). DESTCOLOR Blend faktor je (R d,g d,b d,a d ). INVDESTCOLOR Blend faktor je (1 R d, 1 Gd, 1 B d, 1 A d ). SRCALPHASAT Blend faktor je (f,f,f, 1) kde f = min(a s, 1 A d ). Tabulka 4.6: Způsoby míchání barev hodnoty kterých může v materiálu nabývat parametr SRCBLEND a DESTBLEND. R s,g s,b s,a s jsou barevné složky zdrojového pixelu a R d,g d,b d,a d jsou barevné složky cílového pixelu v kreslícím plátně.

KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 27 OROP na DISABLE. Parametry, které je možno nastavovat pro jednotlivé blending stage jsou uvedeny v tabulce 4.7. COLORARG1 (ALPHAARG1) COLOROP (ALPHAOP) Stage 0 COLORARG2 (ALPHAARG2) RESULTARG Blending stage 1 (n-1) Stage n Polygon Obrázek 4.17: Způsob míchání textur v materiálu RWE. Jak je vidět z množství tabulek v uvedených této kapitole, materiály mají velké množství parametrů a nabízí tak poměrně velkou variabilitu výsledného vzhledu objektu. Ovšem velký počet parametrů také přináší komplikace při jejich nastavování. Lidé si nepamatují význam všech parametrů a jejich možnosti. Proto je vhodné zavést do systému materiálů šablony. Šablona materiálu bude potom nést veškerá nastavení a v novém materiálu, postaveném na této šabloně pak můžeme změnit (přetížit) jen určitou vlastnost. Například si tak můžeme vytvořit šablonu, která vytváří průhledný materiál s jednou texturou. Poté v materiálu, využívajícím původní materiál jako šablonu můžeme změnit například použitou texturu, nebo úroveň průhlednosti atd.. Bylo by vhodné aby každý nový materiál mohl být zárověň šablonou. Tím se nám stírá rozdíl mezi materiálem a šablonou a v podstatě vše je materiál, jen je možné ho použít jako šablonu. Abychom dosáhli takovýchto schopností, navrhneme materiál jako kontejner na jednotlivé materiálové parametry. Buďto určitý parametr obsahuje a zná jeho hodnotu, nebo daný parametr v kontejneru nemá uložen. Dotaz na hodnotu některého parametru pak bude probíhat následovně. Vyhledám parametr ve vlastním kontejneru. V případě, že parametr naleznu, vrátím jeho hodnotu a končím. V případě, že parametr nebyl nalezen, zkontroluji zda mám nastaven nějaký šablonový materiál. Pokud ano, nechám vyhledat hodnotu v šabloně. Pokud ne, vrátím defaultní hodnotu pro daný parametr. Tento systém má výhodu v tom, že není paměťově náročný, neboť si materiál nese informace pouze o parametrech změněných oproti šabloně nebo defaultní hodnotě. V materiálu je nutné mít i funkci pro vymazání záznamu o určitém parametru, abychom měli možnost se vrátit k hodnotám z šablony.

28 KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ Parametr COLOROP Popis Způsob míchání barevné informace. Jednotlivé způsoby míchání jsou uvedeny v tabulce 4.8. COLORARG1 Parametr definuje zdroj 1.vstupního parametru pro COLOROP. Možné hodnoty jsou uvedeny v tabulce 4.9. COLORARG2 Parametr definuje zdroj 2. vstupního parametru pro COLOROP. Možné hodnoty jsou uvedeny v tabulce 4.9. ALPHAOP Způsob míchání alfa informace. Jednotlivé způsoby míchání jsou uvedeny v tabulce 4.8. ALPHAARG1 Parametr definuje zdroj 1. vstupního parametru pro ALPHAOP. Možné hodnoty jsou uvedeny v tabulce 4.9. ALPHAARG2 Parametr definuje zdroj 2. vstupního parametru pro ALPHAOP. Možné hodnoty jsou uvedeny v tabulce 4.9. TEXCOORD INDEX Každý vertex vykreslované geometrie může mít u sebe několik sad texturových koordinát. Tento parametr určuje, která sada texturových koordinát bude využita pro mapování textury. Indexování začíná od nuly. Pokud chceme využít automatického generování texturových koordinát, nastavíme tento idex na jednu z hodnot z tabulky 4.10. COLORARG0 Parametr definuje zdroj 3. vstupního parametru pro COLOROP. Možné hodnoty jsou uvedeny v tabulce 4.9. ALPHAARG0 Parametr definuje zdroj 3. vstupního parametru pro ALPHAOP. Možné hodnoty jsou uvedeny v tabulce 4.9. RESULTARG Definuje kam bude uložen výsledek operace. Možné hodnoty jsou uvedeny v tabulce 4.9. CONSTANT Pro operace (COLOROP / ALPHAOP) pracující s konstantou, definuje tento parametr hodnotu konstanty. Tabulka 4.7: Parametry blending stage pordporované v materiálu enginu. Tabulka 4.8: Možné operace blending stage a popis jejich výpočtu. Název DISABLE SELECTARG1 SELECTARG2 MODULATE MODULATE2X MODULATE4X ADD Popis výpočtu výstupu blending stage Zastaví práci této a všech blending stage s vyšším indexem. Na výstup zkopíruje hodnoty z 1. argumentu. S RGBA = Arg1 Na výstup zkopíruje hodnoty z 2. argumentu. S RGBA = Arg2 Vynásobí složky vstupních argumentů. S RGBA = Arg2 Arg2 Vynásobí složky vstupních argumentů a výsledek vynásobí dvěma. S RGBA = 2 (Arg2 Arg2) Vynásobí složky vstupních argumentů a výsledek vynásobí čtyřmi. S RGBA = 4 (Arg2 Arg2) Sečte složky vstupních argumentů. S RGBA = Arg1 + Arg2

KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 29 Tabulka 4.8: (pokračování). Možné operace blending stage a popis jejich výpočtu Název ADDSIGNED Popis výpočtu výstupu blending stage Znaménkově sečte komponenty vstupních argumentů. S RGBA = Arg1 + Arg2 0.5 ADDSIGNED2X Znaménkově sečte komponenty vstupních argumentů a výsledek vynásobí dvěma. S RGBA = 2 (Arg1 + Arg2 0.5) SUBTRACT ADDSMOOTH BLEND DIFFUSE ALPHA BLEND TEXTURE ALPHA BLEND TEXTURE ALPHAPM BLEND CURRENT ALPHA MODULATE ALPHA ADDCOLOR MODULATE COLOR ADDALPHA MODULATE INVALPHA Odečte složky vstupních argumentů. S RGBA = Arg1 Arg2 Sečte složky vstupních argumentů a odečte od nich jejich násobek. S RGBA = (Arg1 + Arg2) Arg1 Arg2 = Arg1 + Arg2 (1 Arg1) Lineárně promíchá argumenty podle interpolovaného alfa kanálu z jednotlivých vertexů. S RGBA = Arg1 (Alpha) + Arg2 (1 Alpha) Lineárně promíchá argumenty podle alfa kanálu z textury pro tuto blending stage. S RGBA = Arg1 (Alpha) + Arg2 (1 Alpha) Lineárně promíchá argumenty podle alfa kanálu z textury pro tuto blending stage. První argument obsahuje barvu vynásobenou alfa kanálem. S RGBA = Arg1 + Arg2 (1 Alpha) Lineárně promíchá argumenty podle alfa kanálu z textury pro předchozí blending stage. S RGBA = Arg1 (Alpha) + Arg2 (1 Alpha) Tuto operaci je možné nastavit pouze pro COLOROP. S RGBA = Arg1 RGB + Arg1 A Arg2 RGB Tuto operaci je možné nastavit pouze pro COLOROP. S RGBA = Arg1 RGB Arg2 RGB + Arg1 A Tuto operaci je možné nastavit pouze pro COLOROP. S RGBA = Arg1 RGB + (1 Arg1 A ) Arg2 RGB ADDCOLOR DOTPRODUCT3 Do všech složek výstupního vektoru uloží hodnotu skalárního součinu: S RGBA = (Arg1 r Arg2 r + Arg1 g Arg2 g + Arg1 b Arg2 b ) MULTIPLYADD S RGBA = Arg1 + Arg2 Arg3 LERP Lineárně interpoluje mezi druhým a třetím argumentem na základě váhy z prvního argumentu. S RGBA = Arg1 Arg2 + (1 Arg1) Arg3 4.7 Rozhraní RenderDevice (Renderer) Grafický engine je na své nejnižší úrovni zodpovědný za vykreslení objektů, které jsou viditelné pozorovateli. Pro vykreslování je nejčastěji využito některého z grafických API

30 KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ Název CONSTANT CURRENT DIFFUSE SPECULAR TEMP TEXTURE Popis Použije konstantu této blending stage jednotky. Hodnota je pouze pro čtení. Použije výstup z předchozí blending stage. Pro nultnou blending stage je CURRENT ekvivalentní DIFFUSE. Pro parametr RESULTARG má význam posílání výsledku do další blending stage. Použije difuzní barvu interpolovanou mezi vertexy v průběhu Gourand stínování. Hodnoty jsou pouze pro čtení. Použije spekulární barvu interpolovanou mezi vertexy v průběhu Gourand stínování. Hodnoty jsou pouze pro čtení. Dočastný registr pro zápis nebo čtení. Lze tedy použít pro RESULT- ARG. Použije barvu z textury nastavené pro tuto blending stage. Hodnota je pouze pro čtení. Tabulka 4.9: Možné hodnoty a jejich význam pro blending stage parametry COL- ORARGx a RESULTARG. CAMERASPACE POSITION CAMERASPACE REFLECTION VECTOR SPHEREMAP Název Popis 0 -- 7 Použije příslušnou sadu texturových koordinát z vertexu. CAMERASPACE NORMAL Automaticky generuje texturové koordináty: transformuje normálu vertexu do camera space a výsledek použije jako texturovácí koordináty pro tuto texturovací jednotku. Automaticky generuje texturové koordináty: transformuje pozici vertexu do camera space a výsledek použije jako texturovácí koordináty pro tuto texturovací jednotku. Automaticky generuje texturové koordináty: transformuje vektor odrazu do camera space a výsledek použije jako texturovácí koordináty pro tuto texturovací jednotku. Vektor odrazu je počítán z pozice a normály vertexu. Automaticky generuje texturové koordináty: u = Nx + 0.5 2 v = Ny + 0.5, kde N 2 x a N y jsou složky normálového vektoru, transformovaného do camera space. Tabulka 4.10: Možné hodnoty a jejich význam pro blending stage parametr TEXCO- ORDINDEX.

KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 31 jako je OpenGL[19] nebo DirectX[16]. Na platformách, kde není možné využít těchto API, je nutné celý grafický systém naprogramovat tak, aby běžel na CPU. Výsledkem je softwarový renderer. Ačkoli současné komerční grafické karty mají obrovský výkon, potřeba programovat softwarové renderery stále trvá pro systémy s grafický výstupem, jako jsou mobilní telefony nebo PDA. Dalším případem softwarových rendererů je třída foto-realistických rendererů. Jedná se o různé druhy raytracerů a radiozit. V této kapitole se nezaměřuji na problematiku programování samotného rendereru, ale zaměřím se na návrh abstraktní vrstvy, přes kterou bude engine s rendererem komunikovat. 4.7.1 Synchronizace zdrojů dat RenderDevice je rozhraní za kterým se bude v enginu skrývat samotný renderer. Přes jeho funkce bude probíhat veškerá komunikace včetně samotného vykreslování scény. V předchozích kapitolách jsme si představili všechny druhy dat, se kterými bude renderer pracovat a tedy i komunikovat. Abychom docílili minimálního zpoždění při vykreslování objektů, je vhodné grafická data ukládat přímo do struktur použitého API. Rozhraní RenderDevice musí tedy obsahovat funkce, pro synchronizaci zdrojů dat. Nyní si krok za krokem projdeme cyklem připojení a odpojení RenderDevice k enginu a ujasníme si, jakým způsobem bude synchronizace dat probíhat. V okamžiku, kdy se renderer připojí k enginu, se musí nejprve dozvědět o všech existujících zdrojích dat, které v enginu již existují. V tomto okamžiku si renderer vytvoří příslušné struktury, na základě vlastního využívaného API. Dalším krokem je zkopírování dat ze struktur enguinu, do struktur rendereru. Rozhraní RenderDevice tedy musí mít možnost informovat renderer o nutnosti vytvořit nový zdroj dat a o nutnosti uploadování dat ze struktur enginu. V tomto okamžiku existují dvě kopie dat zdroje. To je samozřejmě nežádoucí stav, vzhledem k paměťové náročnosti grafických zdrojů. Proto engine, po úspěšném uploadu dat, zruší jejich kopii z vlastních stuktur. Tato vlastnost nám sice malinko zkomplikuje odpojení RenderDevice od enginu, ale úspora paměti je obrovská. Aktuální pozici dat si udržuje decentralizovaná logika každého jednotlivého zdroje. V okamžiku, kdy chceme RenderDevice od enginu odpojit, je nutné nejprve data všech zdrojů zapsat zpět do struktur enginu a následně můžeme data zdroje zrušit. K tomu nám poslouží opět dvě funkce rozhraní RenderDevice, jedna pro stažení dat a druhá informuje renderer, že je možné daný zdroj zrušit. Všechny výše zmíněné funkce nám postačí pouze pro synchronizaci dat, nikoliv pro manipulaci s tady zdroje. Představte si situaci, kdy je RenderDevice připojeno k enginu a uživatel chce změnit obsah některého z bufferů. Situace se dá řešit dvěma způsoby. První způsob je, že se data nakopírují zpět do struktur enginu, tam se provede změna dat a ty se pak zpět zapíší do struktur rendereru. Tento způsob je však značně neefektivní. Představte si, že bychom chtěli změnit například pozici deseti vertexů v bufferu, kde jich je deset tisíc. Druhou možností je, že nám RenderDevice zpřístupní požadovaná data přímo z vlastních struktur. Tímto způsobem není nutno nikam nic kopírovat, neboť se mění data přímo ve strukturách rendereru. Tento způsob je maximálně efektivní, ovšem předpokládá, že je uspořádání dat ve strukturách rendereru a enginu shodné. V případě, že tomu tak není, musí to renderer vzhledem k rozhraní zajistit. Ovšem i tak je druhý způsob výhodnější, neboť uživatel ve svém požadavku na změnu dat udává, zda chce data pouze zapisovat nebo i číst a uvádí jak veliký blok dat ho zajímá. Renderer, který má struktury nekompatibilní s enginem může dočasně vytvořit paměťový blok, který nabídne uživateli.

32 KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ Ten zapíše nové hodnoty a hned poté renderer přepíše výsledek do vlastních struktur. Možnost přístupu k datům rendereru nám tedy přináší do rozhraní další dvě funkce. Postup synchronizace, který byl výše zmíněn, platí pro případy kdy RenderDevice dokáže spravovat zdroje dat ve vlastních strukturách. Pro jednodušší nebo softwarový renderer může být výhodnější, ponechat správu zdrojů na enginu. Datové bloky zdrojů zůstávají v tomto případě po celou dobu ve strukturách enginu. Renderer nemusí implementovat všechny funkce pro správu zdrojů dat. V případě, že renderer ani nekopíruje data do vlastních struktur nemusí implementovat žádnou z funkcí pro synchronizaci. Pokud ovšem chce využít alespoň zkopírovaní dat zdroje do vlastní struktury, nebo si chce k jednotlivým zdrojům přidat vlastní informace musí implementovat minimálně funkce pro vytvoření a zrušení zdroje, případně funkci pro upload zdrojů dat kterou engine volá při změně obsahu dat některého zdroje. Pro synchronizaci zdrojů dat, musí mít rozhraní RenderDevice funkce uvedené v tabulce 4.11. Jejich implementace v rendereru je volitelná a závisí na požadovaných schopnostech rendereru. Název Vstup Význam funkce v rozhraní CreateRes Struktura zdroje dat Informuje renderer o vzniku nového zdroje dat. DeleteRes Struktura zdroje dat Informuje renderer o zrušení daného zdroje dat. UploadRes Struktura zdroje dat Umožňuje rendereru zkopírování dat zdroje do vlastních struktur nebo doplnění informaci k datovému zdroji. DownloadRes Struktura zdroje Požadavek enginu pro zkopírování dat ze LockRes UnlockRes dat Struktura zdroje dat + popis požadavku Struktura zdroje dat struktur rendereru do struktur enginu. Požadavek na zpřístupnění dat zdroje ze struktur enginu. Informace pro rendereru o dokončení změny dat. Tabulka 4.11: Funkce rozhraní RenderDevice pro synchronizaci zdrojů dat. Jak jsem zmínil výše, renderer si může vybrat způsob synchronizace a tím přímo ovlivňuje schéma, kterým se řídí volání synchronizačních funkcí. Aby engine věděl které schéma použít, musí se od rendereru dozvědět, jaký typ synchronizace si přeje. Když se zamyslíme nad jednotlivými typy synchronizace zjistíme, že jsou pouze dva. Buď plná podpora ze strany rendereru, kdy data jsou přesouvána do jeho struktur a implementuje tak všechny synchronizační funkce, nebo částečná, případně žádná podpora synchronizace. Enginu tedy bude stačit, když se dozví, zda renderer podporuje plnou synchronizaci pro daný typ zdroje. V opačném případě zvolí jednodušší schéma volání synchronizačních funkcí. K tomu aby se engine tyto informace dozvěděl obohatíme rozhraní RenderDevice o funkci GetRenderDeviceCaps, která musí být každým rendererem implementována. Metoda nám vrací informace, popisující jaký typ synchronizace si renderer přeje pro jednotlivé typy zdrojů. Renderer tak může využít pro každý typ zdroje jiný způsob synchronizace což mu dodává jistou flexibilitu při jeho návrhu.

KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 33 4.7.2 Rendering Nyní umí RenderDevice synchronizovat a vytvářet zdroje dat, ale ještě nemá dostatek informací pro samotný rendering. Každé zařízení je nutné na začátku činnosti inicializovat, říci mu do jakého okna má vykreslovat a jak je dané okno velké. K tomu účelu doplníme rozhraní RenderDevice o funkci Init, SetOutputWindow a ResizeOutputWindow. Engine musí zajistit, aby RenderDevice znalo výstupní okno před samotnou inicializací. RenderDevice naopak musí umožnit kdykoliv změnit velikost výstupního okna. Konečně se dostáváme k samotnému vykreslování. K tomu účelu doplníme rozhraní RenderDevice o dvě funkce. Funkce Render provede vykreslení scény a funkce Present zobrazí výsledek v požadovaném okně. Vstupem funkce Render bude kořen scénového grafu, který reprezentuje scénu a kamera kterou se do scény díváme. Způsob vykreslení je plně v moci rendereru. Aby byla práce se scénou unifikovaná v rámci použití různých RenderDevice, měl by engine poskytnout nástroje pro různé typy dotazů na scénu. Příkladem může být dotaz na seznam objektů viditelných před kamerou, nebo dotaz na světla osvětlující daný objekt a podobně. Renderer poté již pracuje jen se seznamem Visuál ů a Face- Group, které má vykreslit. Funkce rozhraní RenderDevice pro inicializaci a rendering jsou souhrnně popsány v tabulce 4.12. Tabulka obsahuje také funkci Done, ve které by měl renderer provádět začišťovací úkony před odpojením od enginu. Název Vstup Význam funkce v rozhraní Init Provede přípravu rendereru pro vykreslování. SetOutputWindow Cílové okno pro vykreslování Informuje renderer o požadovaném výstupním okně. ResizeOutputWindow Šířka a výška Informuje renderer o změně velikosti okna v pixelech výstupního okna. Render Kořen scénového Pořadavek na vykreslení scény grafu a kamera zadanou kamerou. Present Požadavek na zobrazení výsledku posledního vykreslování. Done Informace rendereru o ukončení jeho činnosti. Tabulka 4.12: Funkce rozhraní RenderDevice pro inicializaci, vyrkeslování scény a ukončení rendereru. 4.8 Scénový graf Existuje mnoho internetových stránek poskytujících tutoriály a příklady zdrojových kódů, které pomáhají nováčkům v počítačové grafice programovat renderery, za použití OpenGL nebo Direct3D. To je užitečný výukový nástroj pro pochopení, jak funguje vykreslování na nejnižších úrovních. Ovšem dle mého názoru, postrádají tyto příklady nahlédnutí do problému, jak navrhnout grafický systém pro složité aplikace, jako jsou hry nebo grafické editory. Tento problém nás klade před praktické otázky jako: 1. Jak poskytnout data rendereru efektivně, aby mohlo vykreslování probíhat v reálném čase?

34 KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 2. Jaký bude interface aplikace s rendererem? 3. Jak zajistit snadné použití enginu programátorem, který engine využívá? 4. Jak zajistit minimální změny v systému v okamžiku, kdy je nutné do něj přidat nové vlastnosti? Je možné si klást i další otázky, nicméně tyto čtyři jsou jedny z nejdůležitějších. První otázka je jasná, chceme vytvořit engine, který je schopen vykreslovat scénu v reálném čase. Ovšem není možné po rendereru požadovat vykreslení všech objektů, které rozsáhlá scéna obsahuje. Ořezání a mechanizmus hloubkového testu v použitém API sice eliminuje neviditelné objekty, ovšem i tato eliminace zabírá podstatnou část výpočetního výkonu. Navíc, samotné grafické API nemá o struktuře scény žádné informace a nemůže tedy žádné objekty z vykreslení vyřadit. Ovšem programátor enginu tyto informace má a může tak přesně řídit renderer. Svět který engine vykresluje je v angličtině nazýván scene. Objekty, které se nacházejí v našem světě jsou tedy součástí scény. Pokud spojíme jednotlivé objekty mezi sebou a se světem do vzájemného vztahu, dostaneme to, čemu se říká scénový graf scene graph. V okamžiku, kdy můžeme omezit objekty zaslané do rendereru na takové, které jsou viditelné, nebo potenciálně viditelné, bude zátěž rendereru mnohem nižší. Takové správě dat scény říkáme scene graph management. Určení viditelných objektů je jeden z aspektů scénového managementu, jsou ovšem i další, které si v této kapitole objasníme. Management scénoveho grafu je systém vyšší úrovně než renderer a může být chápán jako nadstavba rendereru, zkonstruovaná pro jeho efektivní využití. Při návrhu enginu je nutné dobře promyslet návrh rozhraní mezi těmito dvěma systémy, zvláště pak, pokud chceme mít možnost snadno doplňovat schopnosti enginu. To objasňuje podstatu výše zmíněné druhé otázky. Jinak řečeno, nechceme měnit a přepisovat rozhraní mezi scénovým managementem a rendererem, kvůli každému rozšíření enginu. Ačkoli je občas při vývoji nového enginu nějaká změna nutná, je výhodné dobře promyslet návrh abstraktní vrstvy rendereru, která pak minimalizuje dopad takových změn na ostatní subsystémy enginu. V našem případě je rozhraní mezi scénovým grafem a rendererem tvořeno virtuálním RenderDevice, které jsme si představili v předchozí kapitole. Třetí otázka je také poměrně důležitá. Pokud bylo složité engine používat, jen málokdo by ho využil ve své aplikaci. Scénový graf pomáhá izolovat programátora aplikace od rendereru a od detailů vykreslování scény. Zároveň však musí být schopen zprostředkovat všechny schopnosti rendereru ve formě, kterou je snadné používat. Programátor aplikace se tak může zaměřit pouze na to, jak jeho objekty mají ve světě vypadat a interagovat. Na této úrovni jsou jakékoli detaily renderingu nepodstatné. Poslední otázka je možná jedna z nejdůležitějších. Navíc souvisí i s požadavky kladenými na tuto práci snadná rozšířitelnost enginu. Z vlastní zkušenosti vím, že častým a možná i nejhorším případem požadavku na rozšíření je přidání nového visuálního efektu, nebo přidání nového typu geometrického objektu. Představme si například, že chceme do enginu přidat možnost vykreslovat rozsáhlý terén, za použití adaptivního LOD terénu vzhledem k poloze kamery. Dříve tato možnost v enginu nebyla a proto nyní musíme vytvořit sadu tříd pro scénový management, které zajistí efektu podporu. Je možné, ale málo pravdě pravděpodobné, že bude potřeba částečně poupravit renderer pro podporu tohoto efektu. Pokud by však přidání nového efektu, vyžadovalo významný přepis scénového managementu nebo rendereru, je velmi pravděpodobné, že původní návrh byl špatný.

KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 35 V této sekci se pokusím na základě vlastních zkušeností a studia navrhnout scénový management tak, aby odpovídal požadovaným cílům práce. Budu se snažit objasnit své kroky při návrhu jednotlivých částí. Ovšem je nutné mít na paměti, že žádné řešení není ideální a že v tomto ohledu existuje i mnoho jiných správných řešení. 4.8.1 Základní struktury scénového managementu Nejdůležitější subsystémy scénového managementu jsou zapouzdřeny do tří struktur: Frame, SceneRoot, Visual a abstraktního rendereru v podobě rozhraní RenderDevice. Struktury jsou navrženy tak, aby umožňovaly efektivně poskytovat data do RenderDevice. Obrázek 4.18 ukazuje jak jednotlivé struktury interagují. Šipky v diagramu značí určitou formu závislosti: Objekt na hrotu šipky závisí nějakým způsobem na objektu, na začátku šipky. SceneRoot Visual FaceGroup RenderDevice Visibility solver Time Update or Prerender Update Bound Index Buffer Vertex Buffer Material Indices World vertices & normals colors, textures, materials, blending settings...... World transform Frame Local transform Frame flags World Bound Parent world transform Parent Frame Obrázek 4.18: Interakční vztah mezi strukturami Frame, SceneRoot, Visual a RenderDevice. Struktura BoundObject Struktura BoundObject definuje část prostoru obsazenou modelem. V nejjednodušším případě se jedná o kouli obsahující všechny vrcholy modelu. Přesnější možností definice hranice jsou růné typy hraničních boxů (bounding box), případně konvexní obálky modelu apod. Velikosti hraničních objektů jsou většinou generovány procedurálně na základě aktuálního modelu. Tyto objekty jsou používány pro zjištění, zda je model viditelný pozorovatelem nebo nikoli.

36 KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ Struktura Frame Význam struktury FaceGroup jsme si objasnili již dříve. Jedná se o reprezentaci geometrického modelu, často vytvořeného umělcem v některém z 3D grafických editorů. FaceGroup obsahuje kromě informací o geomterii i materiál, který určuje vzhled povrchu modelu. Nyní si představme situaci, kdy chceme vytvořit model místnosti do které je vložen stůl. Samotný model stolu a místnosti je často modelován nezávisle na sobě. V okamžiku, kdy modelujeme místnost, by bylo vhodné mít možnost načíst a vložit již hotový model stolu. Problém ovšem spočívá v tom, že modely jsou vytvořeny ve svém vlastním nezávislém souřadnicovém systému. Abychom tedy vložili stůl do místnosti, musí být posunut, orientován (pootočen) a v některých případech i upravena jeho velikost (scale). Výsledná lokální transformace je nesporně vlastnost finální scény. Transformaci nazýváme lokální neboť tato transformace je aplikována pouze na model stolu a je aplikována relativně vůči souřadnicovému systému místnosti. Model stolu je tedy umístěn v místnosti a relaci mezi stolem a místností, je možno chápat jako parent-child. Samotná místnost může být opět situována vůči jinému objektu, například domu, kdy je využito lokální transformace místnosti relativně vůči transformaci domu. Samotný dům je umístěn relativně vůči souřadnicím našeho světa. Můžeme tedy říci, že lokální transformace domu je zároveň jeho světovou transformací. Objekty jsou skládány v přirozené hierarchii. Na obrázku 4.19 je hierarchie modelů z předchozího příkladu doplněná o další místnost, židli, talíř a vidličku s nožem. Světová transformace ovlivní nejen geometrii modelu, ale také geometrii hraničního objektu uloženou ve struktuře BoundingObject. Každý objekt ve scéně má svou světovou transformaci, aby ho bylo možné přímo umístit do scény. Světová transformace se vytvoří z jeho lokální transformace a světové transformace rodiče. Dům Místnost 1 Místnost 2 Stůl Židle Talíř Vidlička Nůž Obrázek 4.19: Hierarchie reprezentující kolekci vzájemně svázaných objektů. Vezměme, že L objektu je lokální transformace, která umístí objekt do souřadnicového systému svého rodiče a W objektu je světová transformace objektu. Z hierarchie na obrázku 4.19 vyplývá následující skládání matic:

KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 37 W Dum = L Dum W Mistnost1 = W Dum L Mistnost1 = L Dum L Mistnost1 W Mistnost2 = W Dum L Mistnost2 = L Dum L Mistnost2 W Stul = W Mistnost1 L Stul = L Dum L Mistnost1 L Stul W Zidle = W Mistnost1 L Zidle = L Dum L Mistnost1 L Zidle W Talir = W Stul L Talir = L Dum L Mistnost1 L Stul L Talir W Nuz = W Stul L Nuz = L Dum L Mistnost1 L Stul L Nuz W V idlicka = W Stul L V idlicka = L Dum L Mistnost1 L Stul L V idlicka První rovnice nám říká, že Dům je vložen do našeho světa přímo. Tedy, že jeho lokální a světová transformace je stejná. Druhá rovnice říká, že Místnost1 je nejprve transformována do souřadnicového systému domu a následně pak do souřadnic světových pomocí světové transformace Domu. Ostatní rovnice mají podobný význam. Abychom tedy získali světovou transformaci pro talíř, je nutné aplikovat všechny předchozí lokální transformace. Jelikož však lokální transformace je měněna jednou za čas, je efektivní využít při vypočtu světovou transformaci rodiče (která je již vypočtena) a vlastní lokální transformaci a pomocí jejich součinu vytvořit světovou transformaci syna. Proto struktura Frame obsahuje lokální i světovou transformaci. Struktura Frame zajišťuje seskupování objektů do hierarchie. Kompozice matic je zajištěna depth-first traverzováním stromu do hloubky. Každý rodičovský uzel vypočte svou světovou transformaci a poskytne ji svým potomkům, což je přirozeně rekurzivní proces. Transformace jsou tak propagovány směrem dolů od kořenových uzlů k listům. V rámci optimalizace je vhodné mít ke světové transformaci ještě příznak určující, zda je transformace validní. Tento příznak je potom nastaven na TRUE v okamžiku, kdy se světová transformace vypočte. Pokud však některý z uzlů změní svoji lokální transformaci, nastaví tento příznak na FALSE sobě i svým potomkům. V případě dotazu na světovou transformaci se pak nejprve vyhodnotí příznak validity. Pokud není transformace validní vytvoříme ji z lokální transformace a ze světové transformace rodiče, který opět při dotazu na světovou transformaci vyhodnotí její validitu. Takto se rekurzivně dostaneme až k bodu, kde je světová transformace validní. Touto optimalizací zajistíme minimální přepočet transformací, neboť světové transformace nad bodem změny se nebudou počítat znovu. Struktura Frame může mít ve scénovém grafu pouze jednoho rodiče. Jelikož Frame tvoří základ pro ostatní typy uzlů, které je možné vkládat do scénového grafu, stává se tak scénový graf acyklickou stromovou strukturou. Jelikož specializované uzly poděděné od Frame mají na engine různé požadavky, je vhodné mít ve třídě Frame pole příznaků Frame flags, které specifikují vlastnosti a požadavky daného uzlu. Příkladem může být požadavek na automatické volání metody TimeUpdate. Pokud je tento příznak nastaven, danému uzlu zavolá management scény metodu TimeUpdate při každé změně času ve scéně. Příkladem specifikace speciální vlastnosti Frame je uvedení, že je Frame dynamický to může být využito například při rozhodování, jakou metodou u tohoto uzlu provést detekci viditelnosti. Dynamickým uzlem je myšlen uzel, který mění často svou pozici ve světě nebo svou geometrii. Základní příznaky a jejich význam je uveden v tabulce 4.13. Příznaky může nastavovat jak uživatel enginu, tak specializovaný uzel zděděný od Frame. Z tabulky 4.13 vyplývá, že třída Frame nabízí metody TimeUpdate a PrerenderUpdate. V rámci třídy Frame tyto metody neprovádí žádnou činnost. Svoje využití naleznou až

38 KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ Příznak Disabled Dynamic Always List TimeUpdate PrerenderUpdate Význam Aktuální uzel je disabled, scénový managemend se k němu chová jako by tento uzel a jeho potomci nebyli vůbec zařazeni ve scénovém grafu. Informuje scénový management, že aktuální uzel často mění velikost nebo pozici BoundigObject. Aktuální uzel je vždy považován za list ikdyž má nenulový počet potomků. Tohoto příznaku mohou využívat specializované uzly pro dočasné skrývání geometrie a podobně. Požaduje po scénovém managementu automatické volání metody TimeUpdate pro tento uzel scénového grafu, při každé změně času scény. Požaduje po scénovém managementu automatické volání metody PrerenderUpdate pro tento uzel scénového grafu, před začátkem vykreslování scény jkoukoliv kamerou. Tabulka 4.13: Příznaky Frame flags a jejich význam. ve specializovaných třídách zděděných od Frame. Struktura Visual Frame nám zajišťuje správné provedení transformací a umožňuje seskupování objektů. Abychom však mohli do scény vkládat viditelné objekty, musí ve scénovém grafu existovat uzel, který dokáže odkazovat na nějakou geometrii. Přesně to zajišťuje uzel Visual. V našem případě je geometrie uložena ve strukturách FaceGroup. Visual tedy představuje jakýsi kontejner na struktury FaceGroup. Visual může seskupit i větší množství Face- Group a tak postupně skládá složitější objekty. Objekty pak mohou být tvořeny více druhy geometrických primitiv a materiálů. Třída Visual může také obsahovat odkaz na třídu BoundingObject, která reprezentuje část prostoru obsazenou grafickými daty Visualu. Informace z BoundingObject jsou využity při testování, zda je Visual viditelný pozorovateli. Algoritmů pro detekci viditelnosti objektu je poměrně hodně a všechny se nějakým způsobem opírají o hraniční objekty. Proto je vhodné mít u každého visuálu i tuto informaci. Data hraničního objektu jsou vetšinou dopočtena na základě aktuální geometrie Visuálu. Okamžik výpočtu je vhodné nechat na uživateli enginu, neboť ten ví nejlépe, kdy je seznam FaceGroup kompletní. Je však možné uložit vlastní popis hranic a výpočet tak vynechat. V případě, kdy Visual neobsahuje žádnou informaci o hranicích objektu, je vždy vyhodnocen jako viditelný, což může mít v případě velkého množství takovýchto visuálů, negativní následky na rychlost vykreslování scény. Struktura Visual nijak nevyužívá funkcí TimeUpdate ani PrerenderUpdate, neboť zajišťuje pouze základní vlastnosti zobrazení objektů. Ovšem uzly vytvořené na základu struktury Visual, mohou okamžitě využít těchto funkcí a přetížit jejich činnost. Lze pak snadno procedurálně generovat obsah všech typů zdrojů dat. Takto lze poměrně snadno vytvořit rozličné efekty, od jednoduchých billboardů až po komplexní efekty jako jsou různé particle systémy nebo objekty s fyzikální simulací. Vše již záleží pouze na kreativitě a šikovnosti uživatele enginu.

KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 39 Struktura SceneRoot Na začátku této práce jsme si stanovili cíle. Jeden z nich uvádí možnost snadného rozšíření schopností scénového grafu. To zahrnuje možnost doplnění visuálních efektů, ale také možnost doplnění scénového managementu o nové způsoby detekce viditelných objektů. Vytváření visuálních efektů máme zajištěno specializací struktur Frame a Visual. Jak je vidět na obrázku 4.18 tak struktura SceneRoot v sobě nic jiného, než právě algoritmus pro detekci viditelných objektů nemá. SceneRoot tedy zajišťuje určité typy dotazů do scény. Jedním takovým typem dotazu je dotaz na aktuálně viditelné objekty pro danou kameru. Uzel SceneRoot se předává rendereru, který potom zadává požadavky podle své vlastní potřeby. Jinak řečeno jiný typ dotazu do scény bude mít realtime renderer a jiný raytracer. Každá scéna v našem enginu tedy musí mít alespoň jeden uzel typu SceneRoot na svém vrcholu, jinak by ji nebylo možné vykreslit. Jednotlivé techniky detekce viditelných objektů si vytváří různé pomocné struktury. Většinou se jedná o různé typy vyhledávacích stromů apod. Do těchto struktur se pak zařazují objekty tak, aby bylo možné rychle určit, zda se objekt nachází před kamerou nebo ne. Jak ovšem zajistit správné tvoření těchto vyhledávacích struktur? SceneRoot musí mít mechanizmus, který zajistí že se dozví o nových uzlech v jeho podstromu. Na každý nový uzel v podstromu pak budou moci zareagovat algoritmy úpravou svých pomocných struktur. Stejně tak je nutné informovat SceneRoot v okamžiku, kdy se některý z uzlů z podstromu odebírá. Pokud budeme do podstromu přidávat nebo z něj odebírat uzel, který má pod sebou další uzly, musí se SceneRoot dozvědět i o těchto uzlech. Toto je automaticky zajištěno scénovým managementem interně. SceneRoot je však nutné informovat i v situacích, kdy se uzel z grafu nevyjímá ani se do něj nepřidává. V podstatě se jedná o situace, kdy se mění velikost nebo pozice BoundigObjektu statického visuálu. O těchto situacích nejlépe ví uživatel enginu. Struktura Frame, a tedy i všechny od ní odvozené, obsahuje metodu Update, která v okmažiku volání upozorňuje Scene- Root o tom, že tento visuál byl pozměněn. Jelikož úprava vyhledávacích struktur nemusí být triviální záležitostí, je toto řešení mnohem efektivnější, než příliš časté automatické volání Update. Dalším důležitým cílem této práce je, aby scénový graf umožňoval kombinovat různé algoritmy detekce viditelných objektů v rámci jedné scény. Celou Scénu je poté možno dobře optimalizovat, neboť každý typ algoritmu je vhodný na jiný typ scény. To můžeme zajistit tak, že umožníme do scény vkládat více uzlů typu SceneRoot (každý bude mít vlastní typ detekce viditelnosti). Možnost vložení více uzlů typu SceneRoot s sebou přináší i určité komplikace jako například: Jakému SceneRoot se má uzel nahlásit, když je do scény vkládán, vyjímán nebo provádí Update? Jak se k sobě budou navzájem chovat jednotlivé SceneRoot? Interní scénový management tedy doplníme o následující pravidla, která zajistí korektnost: 1. Uzel se při vkládání, odebírání nebo volání funkce Update hlásí prvnímu SceneRoot, který nalezne na cestě směrem ke kořenovému uzlu stromu. 2. Každý uzel typu SceneRoot se při prohledávání grafu chová k jinému SceneRootu jako by byl list i v případech, kdy má nalezený SceneRoot nenulový počet potomků. Po zavedení výše zmíněných pravidel se bude scénový graf jednotlivým SceneRootům jevit jako izolované scény vnořené do sebe. Ještě je nutné vyřešit jeden problém. Ten se týká průběhu samotného dotazu na viditelné scény. V okamžiku, kdy chceme získat

40 KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ seznam viditelných objektů danou kamerou, provedeme dotaz na samém vrcholu scény. SceneRoot vyhodnotí dotaz, a zařadí do seznamu příslušné objekty. Tím ovšem nekončí. Musí ještě zajistit propagaci dotazu do všech SeceneRootů v jeho podstromě (přičemž stále zachovává 2. pravidlo). Jedná se opět o rekurzivní proces a dotaz se tak dostane ke všem SceneRoot uzlům ve scéně. SceneRoot 1 Dům Les SceneRoot 2 Strom Strom Místnost 1 Další místnosti s příslušenstvím Stůl Frame Talíř Vidlička Nůž Židle Legenda: Uzel typu Frame Uzel typu Visual Uzel typu SceneRoot Nadřazeným uzlem je SceneRoot 1 Nadřazeným uzlem je SceneRoot 2 Obrázek 4.20: Hierarchie scény enginu při použití více tříd SceneRoot. Na obrázku 4.20 je hierarchie scény v případě, kdy je použito více SceneRootů. Tvar uzlu vyjadřuje typ použitého uzlu a způsob výplně nám říká, který SceneRoot je danému uzlu nadřazený. Z obrázku je patrné, že v enginu není nutné mít Visualy jako listy grafu. Díky tomu není nutné kvůli každé skupině geometrie vkládat uzel, který by toto seskupení zajistil. Zároveň však tomu engine nebrání a například skupina Talíř + Vidlička + Nůž je seskupena uzlem typu Frame i když by mohla být umístěna přímo pod uzlem geometrie stolu a výsledná scéna by vypadala shodně. Využití více typů uzlů SceneRoot má nesporné výhody. Představme si, že dům bude mít velké množství pokojů, včetně vybavení. Pokud se kamera nachází vně domu, není nutné vykreslovat jeho pokoje a vybavení. Můžeme tedy navrhnout algoritmus detekce viditelnosti, který využije nejen pohledu kamery, ale i její pozice a pokud se kamera nebude nacházet v některé z místností, nebude její obsah zařazen k vykreslení 2. Pokud by takovým algoritmem 2 Samozřejmě je nutné vykreslovat alespoň vybavení v blízkosti prosklených ploch a podobně. S tím samozřejmě algoritmy počítají a umí zjistit, které konkrétní modely jsou pozorovateli z jeho pozice viditelné i v případě, že se nachází vně budovy.

KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 41 byl vybavený uzel SceneRoot 2 z obrázku 4.20, byla by scéna ideálně optimalizovaná. Vnitřní vybavení domu by nebylo zbytečně vykreslováno dokud bychom se nenacházeli uvnitř domu. Zároveň SceneRoot 1 by zajišťoval ořezaní venkovní scény tak, aby se nevykreslovaly objekty mimo pohledový jehlan kamery. Možnost kombinace takových algoritmů je velmi mocný nástroj k dosažení optimálního ořezání scény a zároveň dává enginu velkou flexibilitu, neboť je možné ho použít na real-time vykreslování odlišných typů scén. 4.8.2 Sdílení geometrie (Instancing) Když je jeden objekt sdílený dvěma dalšími objekty, existují efektivně dvě instance prvního objektu. Fyzicky však je pouze jediná, na kterou se oba objekty odkazují. Procesu sdílení se říká instancing. Když si představíme náš scénový graf, zjistíme, že má vždy podobu stromové struktury. To je zajištěno tím, že každý uzel scénového grafu má pouze jediného předka, což vyžaduje struktura Frame. V našem scénovém grafu tedy není možné aby měl uzel více předků a tím zajistil své sdílení. Otázkou je proč jsem zvolil takové řešení. Dům Místnost 1 Místnost 2 Sdílený podgraf definující obsah místnosti Obrázek 4.21: Scénový graf zjišťující instancing geometrie pomocí sdílením podgrafu scény. Abychom si objasnili proč bylo zvoleno takové řešení, představme si hierarchii, ve které by uzel mohl mít více předků. Jednoduchý příklad je na obrázku 4.21. Tento scénový graf reprezentuje dům se dvěma místnostmi. Tyto místnosti mohou být chápány jako instance stejného sdíleného modelu. Dům má dva přímé odkazy na místnosti. Každá místnost má přímý odkaz na uzel s geometrii místnosti. Tato geometrie je tedy sdílena. Hlavní důvod ke sdílení je snížení paměťové zátěže. Jaký je tedy problém v hierarchii z obrázku 4.21? Hlavním problémem je, že je možné dosáhnout uzlu obsahujícím geometrii místnosti dvěma cestami hierarchií. Každá z cest vede na příslušnou sérii lokálních transformací, což zajistí umístění geometrie do příslušné části domu. Světová transformace aplikovaná na geometrii místností je přirozeně pro každou instanci modelu jiná. Ovšem je nutné někde tuto transformaci uložit. Ve stromové struktuře je tato transformace uložena přímo v každém uzlu. Pro struktury z obrázku 4.21 je můžeme ukládat buď do uzlu, nebo někde mimo a do uzlu ukládat jen odkaz. V každém případě můžeme předpokládat, že u dynamického systému, bude třeba často měnit počet předků a to i o libovolný počet. Stejně jako světové transformace je nutné někde ukládat hraniční objekt. Dalším důsledkem je, že pokud budeme chtít

42 KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ změnit data některého ze sdílených uzlů, musíme rozlišit, kterou instanci chceme změnit. To samotné činí z této operace poměrně komplexní situaci a celý systém scénového grafu to znepřehledňuje. To jsou hlavní důvody, které mě vedly k tomu, použít v návrhu scénového grafu stromovou strukturu. Ta výše zmíněnými nedostatky netrpí. Jak jsem zmínil výše, je podpora sdílení objektů velmi důležitá vlastnost enginu. Jak ji tedy zajistíme ve stromové struktuře? Odpovědí je sdílení objektu na nízké úrovní, tedy na úrovni samotné geometrie. Data geometrie jsou mnohem větší než data, která jsou potřeba pro tvorbu uzlů scénového grafu. Proto, je-li třeba vytvořit instanci některé části scény, zabere duplikace uzlů daného podgrafu jen malou část paměti navíc. Samotná geometrie, na kterou odkazují uzly typu Visuál je samozřejmě sdílena. Pokud chceme další instanci existující geometrie uložené ve struktuře FaceGroup, požádáme FaceGroupu aby se naklonovala. To zajistí, že veškerá geometrická data a materiály jsou sdíleny oběma strukturami FaceGroup. Na obrázku 4.22 je příklad sdílení objektů na nízké úrovni. Dům Místnost 1 Místnost 2 Obsah místnosti 1 Obsah místnosti 2 Sdílená Geometrická Data Obrázek 4.22: Scénový graf z obrázku 4.21, který ovšem zajišťuje instancing sdílením dat geometrie (low-level instancing). 4.9 Vstup dat Před tím, než se začneme procházet virtuálním světěm, nebo než si budeme prohlížet vizualizaci různých typů dat, musíme data do enginu nějakým způsobem nahrát. Data mohou být uložena v různých formátech, proto není vhodné implementovat takové načítání fixně. Lepším způsobem je využít zásuvných modulů (pluginů), které načtení dat zajistí. Takovými moduly pak engine můžeme snadno doplnit o nové možnosti importu a to bez nutnosti engine rekompilovat. Takovéto moduly je možné připojovat dokonce za běhu enginu. Import dat můžeme rozdělit do dvou skupin. První skupinou je import geometrických dat a kompletních scén enginu. Druhou skupinou je import textur všech typů. Jelikož používáme v celém enginu pro vykreslování virtuální RenderDevice, můžeme do zásuvných modulů umístit i samotný renderer. Návrh rozhraní RenderDevice byla věnována samostatná kapitola a tak jak bylo navrženo je možné ho použít i v případě, kdy je do enginu poskytnuto pluginem. Nyní se tedy zaměřím na návrh rozhraní pro import textur a scény.

KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 43 Pod vstupem dat si většinou představíme načtení souboru. Ovšem data je možné generovat i procedurálně popřípadě kombinací obojího. V případě procedurálního generování je většinou potřeba zadat vstupní parametry, na základě kterých se data vygenerují. V případě čtení souboru je třeba zadat cestu k takovému souboru. Jak tedy snadno zajistit možnost předaní různých typů vstupních dat do importní funkce. Řešením je textový příkaz. Do textového řetězce je možné zapsat rozličné datové typy. Každá importní třída si tedy musí sama navrhnout v jakém formátu má vypadat vstupní příkaz. Pro lepší pochopení uvedu dva příklady. První je příklad importní třídy, která importuje scénu ze souboru na disku. Taková třída potřebuje jako vstup cestu a jméno souboru, ze kterého má data načíst. To není samozřejmě problém do textového řetězce zapsat. Druhým příkladem je importní třída, která vygeneruje povrch grafu na základě vstupní funkce. Taková třída bude požadovat na svém vstupu funkci grafu, a také rozsah hodnot, které má pro generování povrchu použít. To opět není problém do vstupního příkazu v textové podobě zapsat, ale je nutné dodržet syntaxi, kterou si třída zvolila. Importní třída si na začátku importu příkaz rozkóduje a zkontroluje vstupní parametry. V případě, že je vše v pořádku, provede import. Jinak nahlásí chybu. Aby uživatel byl schopen vstup importní funkci zadat, je nutné importní třídu doplnit o funkci, která nám popíše jak mají vstupní parametry vypadat. Pak bude možné použít i pluginy vytvořené třetí stranou. Importní třída vytváří nové zdroje dat. Proto je nutné, aby importní funkce měla jako vstup také odkaz na třídu Core, která je zodpovědná za všechny vytvořené zdroje enginu. 4.9.1 Import geometrie a scény Importní třída, která načítá do enginu scénu jí postupně sestavuje z uzlů scénového grafu. K visuálům vytváří příslušné FaceGroupy. K těmto FaceGroupám vytváří a vyplňuje daty index buffery, vertex buffery a materiály. O tom jak bude výsledná scéna vypadat tedy rozhoduje algoritmus importní funkce. Importní třída může sestavovat scénový graf nejen z uzlů, které jsou v enginu standardně podporovány, ale může vytvářet i vlastní specializované uzly. Tím může importní plugin scénový management doplnit o nové funkce, které jsou specifické pro importovanou scénu. V tabulce 4.14 jsou funkce rozhraní SceneImport. Název Vstup Význam funkce v rozhraní DoImport Textový příkaz, Provede import scény na základě vstupního Odkaz na Core, textového příkazu. Jednotlivé zdroje dat Odkaz na uzel Frame jsou vyráběny pomocí Core. Výsledná scéna je připojena pod zadaný uzel. CommandHelp Vrátí textovou informaci popisující syntaxi textového příkazu definujícího import. Copyright Vrátí textovou informaci popisující práva použití pluginu. Tabulka 4.14: Funkce rozhraní SceneImport. Rozhraní SceneIport je vhodné doplnit i funkcí, která zobrazí copyright, neboť v kódu

44 KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ importního pluginu můžeme použít kódy třetích stran, které spadají pod jiný druh licence. 4.9.2 Import textur Rozhraní třídy pro import textur je analogické k rozhraní SceneImport. Jen importní funkce bude mít pozměněné některé parametry. Tento typ importu pouze vytváří nové zdroje textur všech typů a naplní tyto zdroje platnými daty. Ovšem i textury je možno generovat procedurálně. Proto zůstane vstupní příkaz opět v textové podobě, i když ve většině případů, budeme texturu načítat ze souboru. Rozhraní budeme říkat TextureImport a jeho funkce naleznete v tabulce 4.15. Název Vstup Význam funkce v rozhraní DoImport Textový příkaz, Odkaz na Core Vytvoří příslušný typ textury pomocí Core. Vyplní data textury a vrátí její odkaz. CommandHelp Vrátí textovou informaci popisující syntaxi textového příkazu definujícího import. Copyright Vrátí textovou informaci popisující práva použití pluginu. Tabulka 4.15: Funkce rozhraní TextureImport. 4.10 Struktura Core O Core jsem se již zmiňoval v kapitole zabývající se zdroji dat. Tato struktura však souvisí se všemi podsystémy celého enginu. Jedná se o jakousi propojku mezi zdroji dat, scénovým grafem, rendererem a pluginy systému. Logika pro synchronizaci zdrojů je kombinací centralizovaného a decentralizovaného přístupu. Centralizovaná logika se nachází právě v Core. Struktura umožňuje vytvářet jednotlivé zdroje dat. Při tvorbě nového zdroje si zařadí klon nově vzniklého zdroje, do seznamu všech existujících zdrojů. Díky tomu má Core vždy přehled o všech existujících zdrojích. Tohoto seznamu je následně využíváno při synchronizaci zdrojů s RenderDevice. Synchronizace je provedena u všech zdrojů, které změnily svůj obsah a tento obsah není synchronizován s rendererem takové zdroje označujeme jako dirty. To zda bude zdroj takto označen si řídí interní logika zdroje a závisí to i na typu synchronizace, kterou si RenderDevice vybralo. Kontrola, zda je nutné některý ze zdrojů synchronizovat se provádí před vykreslením každého snímku. Výhodou takového seznamu také je, že můžeme uživatele enginu před ukončením programu varovat, že některé zdroje dat nebyly zrušeny. To pomáhá zajistit správné užívání enginu. Core také zajišťuje správné vykreslení scény. K tomu potřebuje odkaz na kořenový SceneRoot uzel scénového grafu a odkaz na aktuálně připojený renderer. Ve funkci Render se pak dějí následující kroky: 1. Volání funkce PrerenderUpdate pro danou kameru, kterou se bude scéna vykreslovat. Volání se provede na kořenovém uzlu scénového grafu a scene graph manage-

KAPITOLA 4. ANALÝZA A NÁVRH ŘEŠENÍ 45 ment zajistí propagaci tohoto volání do všech uzlů, které si o toto volání funkce požádaly. 2. Synchronizace zdrojů označených jako dirty. 3. Volání funkce Render, aktuálně připojeného RenderDevice. Jako vstup pro toto volání je aktuální kamera a kořen scénového grafu. Pořadí těchto kroků nemůže být stanoveno jinak. Nejprve je nutné zavolat všem uzlům PrerenderUpdate. Specializované uzly scénového grafu v tomto bodě mohou pozměnit geometrii nebo textury. Následovat tedy musí synchronizace těchto pozměněných zdrojů s RenderDevice a až poté samotný rendering scény. Aby mohlo proběhnout vykreslení scény, musí mít Core odkaz na připojený renderer. Core musí tedy sama zajistit připojení a odpojení rendereru. Při připojování nejprve odpojí aktuální RenderDevice a pokusí se připojit požadovaný. Při připojování provede následující kroky: 1. Pokud již je nějaký RenderDevice připojen, odpojí ho. 2. Nastaví výstupní okno novému RenderDevice. 3. Nastaví velikost okna. V tomto okamžiku má Core nastaven nový RenderDevice, ale rendering ještě není možné provést, neboť neproběhla inicializace nového zařízení. Jak jsem psal dříve, musí být zajištěno, že výstupní okno RenderDevice je nastaveno již před jeho inicializaci. Proč tedy neprovést inicializaci v okamžiku připojení rendereru? Odpověď je jednoduchá. RenderDevice jsme sice nastavili výstupní okno, ovšem v tomto okamžiku to nemusí být platné výstupní okno a inicializace by se tak nezdařila. Proto je odděleno připojení a inicializace RenderDevice, neboť uživatel enginu ví nejlépe kdy bylo nastaveno platné výstupní okno, a je tak možné zavolat inicializaci RenderDevice. Inicializaci opět provádí funkce třídy Core, která zajistí po platné inicializaci také vytvoření a synchronizaci zdrojů dat s novým RenderDevice. Při odpojení zdroje naopak probíhá download a rušení zdrojů z RenderDevice. Posledním krokem při odpojení RenderDevice je volání funkce Done. Core musí také zajišťovat správu importních pluginů. Díky tomu, že jsou všechny pluginy drženy v jednom seznamu, je možné zajistit jejich interakci. Například plugin, který importuje geometrii, může využít služeb pluginu pro import textur. Díky tomu můžeme pluginy snadno rozšířit importní schopnosti enginu. Podpora importu musí být zajištěna přímo v Core. Tu zajistíme tak, že Core bude samo vyřizovat požadavky na import. Pokud přijde například požadavek na import textury, Core projde všechny třídy v seznamu, které jsou určené pro import textur, a pokusí se daný požadavek pomocí těchto tříd vyřídit. Obdobně to provádí s importem geometrie. Zde však prochází třídy importující geometrii.

46 KAPITOLA 5. REALIZACE 5 Realizace V této kapitole se budu zabývat popisem implementace a realizací, se zaměřením na nestandardní části řešení. Engine je implementován v jazyce C++ jako staticky linkovaná, dynamická knihovna RWE.dll. Tato knihovna je závislá pouze na knihovnách operačního systému Windows. To je způsobeno částí enginu, která má na starosti dynamické připojování importních a renderer pluginů. To je jediná platformově závislá část enginu, pokud neuvažujeme samotné pluginy, které mají tuto vlastnost z principu. Samotná implementace se velmi přibližuje návrhu z této práce. V návrhu však nejsou obsaženy veškeré detaily a problémy spojené s implementací. V následujících sekcích se podíváme na implementaci zdrojů dat. 5.1 Třídy zdrojů dat Z návrhu práce je zřejmé, že jednotlivé zdroje dat musí podporovat sdílení. To plyne i ze způsobu využití zdrojů při instancingu. Na obrázku 5.1 je diagram dědičnosti tříd zdrojů dat. Nové instance jednotlivých typů zdrojů je možné vytvořit pouze pomocí příslušné metody CreateXXX ve třídě C Core. To je z důvodu toho, že každý zdroj musí od počátku mít definováno, ke které které třídě C Core patří a tedy se kterým RenderDevice má synchronizovat svá data. 5.1.1 Třída C SharedResource Jedná se o abstraktní třídu určenou k dědění. Všechny třídy zdrojů dat jsou poděděny z této třídy. C SharedResource zajišťuje základní funkčnost společnou všem zdrojům zdrojům dat. Zároveň nařizuje všem zděděným zdrojům implementaci funkcí Clone a Release, které vytváří klon daného zdroje nebo ho ruší. Jednotlivé typy zdrojů je nutné od sebe rozpoznat a proto třída implementuje funkci GetType, která vrací typ zdroje. C SharedResource také implementuje funkce SetPrivateData, GetPrivateData a FreePrivateData, umožňující rendereru nebo uživateli enginu přiložit si vlastní data k danému zdroji. Posledními důležitými funkcemi jsou GetFormat, která vrací formát dat uvnitř bufferu a GetUsage, která určuje způsob použití zdroje. Způsoby použití jsou specifické pro jednotlivé zdroje. Jedná se o příznaky, které optimalizují zacházení s daty bufferu, nebo nastavují zdroji určitý typ chování. Například můžeme zdroj označit jako dynamický, což optimalizuje uložení bufferu do správné paměti tak, abychom k němu měli rychlý přístup, nebo můžeme například textuře nastavit automatické generování mipmap. C SharedResource nezajišťuje automatické zařazení zdroje do seznamu existujících zdrojů. Tuto činnost má na starosti třída C Core. 5.1.2 Třída C IndexBuffer Třída implementuje buffer indexů. Indexy mohou mít formát 16 nebo 32 bitů. Index buffer je používán pro indexaci bodů ve vertex bufferu. Většina modelů má méně než 2 16 vertexů, a proto je vhodné podporovat 16ti bitové indexy, které tak ušetří paměť. Navíc 16ti bitové indexy jsou podporovány současným hardwarem. Velikost bufferu není možné v průběhu jeho života měnit. To je z důvodu optimalizace. Aktuální velikost bufferu v bytech lze zjistit funkcí GetSize. Data obsažená v index bufferu je nutné synchronizovat s RenderDevice.

KAPITOLA 5. REALIZACE 47 C_SharedResource +ResourceType +Release() +Clone() +GetCore() +RefCount() +GetUsage() +GetFormat() +SetPrivateData() +GetPrivateData() +FreePrivateData() C_Surface +Release() +Clone() +GetContainer() +GetDescription() +LockRect() +UnlockRect() C_Volume +Release() +Clone() +GetContainer() +GetDescription() +LockBox() +UnlockBox() C_VertexBuffer C_IndexBuffer C_BaseTexture +Release() +Clone() +GetVertexFormat() +GetVertexSize() +GetSize() +Lock() +Unlock() +Release() +Clone() +GetSize() +Lock() +Unlock() +GenerateMipSubLevels() +SetAutoGenFilterType() +GetAutoGenFilterType() +GetLevelCount() C_Texture C_CubeTexture C_VolumeTexture +GenerateMipSubLevels() +SetAutoGenFilterType() +GetAutoGenFilterType() +GetLevelCount() +Release() +Clone() +GetLevelDesc() +GetSurfaceLevel() +LockRect() +UnlockRect() +GenerateMipSubLevels() +SetAutoGenFilterType() +GetAutoGenFilterType() +GetLevelCount() +Release() +Clone() +GetLevelDesc() +GetCubeMapSurface() +LockRect() +UnlockRect() +GenerateMipSubLevels() +SetAutoGenFilterType() +GetAutoGenFilterType() +GetLevelCount() +Release() +Clone() +GetLevelDesc() +GetVolumeLevel() +LockBox() +UnlockBox() Obrázek 5.1: Dědičnost tříd zdrojů dat.

48 KAPITOLA 5. REALIZACE 5.1.3 Třída C VertexBuffer Třída implementuje buffer vertexů. Každý vertex buffer má definován vnitřní formát vertexu a velikost celého bufferu. Jednotlivé složky formátu vertexu jsou uvedeny v tabulce 4.1 v návrhu řešení. Vertexy obsažené ve vertex bufferu tvoří tvar modelu. Mohou být indexovány pomocí indexbufferu. Data obsažená ve vertex bufferu je nutné synchronizovat s RenderDevice. 5.1.4 Třída C Surface Třída je určitou abstrakcí obrázku. Má definovanou šířku a výšku v pixelech a formát pixelu. Je navržena k uložení pixelových dat nejrůznějších formátů. Formáty pixelu jsou od jednoduchých R8G8B8 (Red, Green, Blue 8 bitů pro každou barevnou složku), až po složité formáty podporující 32 bitový float na jednu barevnou složku pixelu, včetně alfa kanálu. V současné implementaci není možné, aby surface mohl existovat sám o sobě. Vždy je zařazen jako součást kontejneru, například do C Texture. Nicméně do budoucna se počítá, že bude moci surface existovat i samostatně. To pro podporu speciálních efektů. C Surface obsahuje metodu GetContainer, která vždy vrací odkaz na aktuální kontejner, do kterého je C Surface zařazen. C Surface může být zařazen vždy jen do jednoho nebo žádného kontejneru. Data C Surface je nutné synchronizovat s RenderDevice. 5.1.5 Třída C Volume Třída je velmi podobná C Surface. Opět můžeme říci, že se jedná o buffer pro uložení pixelových dat různých formátů. Tentokrát jsou však jednotlivé pixely umístěny v určitém objemu. C Volume je tedy definován výškou, šířkou, hloubkou a formátem pixelu (texelu). Data jsou organizována do dvourozměrných řezů o dané šířce a výšce. Takovýchto řezů je potom tolik jako je hloubka C Volume. C Volume je využit pro uložení dat jednotlivých úrovní detailů objemové textury. Data tohoto zdroje je nutné synchronizovat s RenderDevice. Současná implementace enginu z časových důvodů tuto třídu neobsahuje. 5.1.6 Třída C BaseTexture Jedná se o abstraktní třídu, od které jsou zděděny všechny typy textur podporovaných v enginu. Třída doplňuje C SharedResource o metody společné zdrojům textur. Každá textura v enginu může mít automatické generování mipmap. C BaseTexture obsahuje metody pro nastavení a zjištění filtru, který bude použit při generování mipmap a metodu, která zajistí okamžité přegenerování mipmap. Automatické generování mipmap nastavíme textuře při jejím vytváření pomocí jednoho z příznaků usage. Pokud má textura tento příznak nastaven, je uživateli přístupný pouze C Surface s nejvyšší úrovní detailů. Ostatní úrovně jsou generovány automaticky. 5.1.7 Třída C Texture Třída představuje dvourozměrnou texturu enginu. Implementována je jako kontejner pro C Surface. Při svém vzniku, si třída vygeneruje parametry všech C Surface, které má obsahovat. Poté jednotlivé C Surface vytvoří a zařadí si je do svého seznamu. Všem vytvořeným C Surface nastaví svůj klon jako kontejner do kterého daný C Surface

KAPITOLA 5. REALIZACE 49 patří. Jak jsem se zmínil výše, tak zařazení zdroje dat do seznamu existujících zdrojů si řídí třída C Core. Aby bylo možné zařadit C Surface vytvořené uvnitř C Texture je C Texture doplněna o metodu AddSurfacesToResourceList, kterou může C Core volat po vytvoření nové textury. Data této třídy není nutné synchronizovat. Jednotlivé C Surface si samy udržují informaci, zda jejich data byly změněny. Pokud ano, označí se jako dirty a C Core je před vykreslením snímku synchronizuje na základě informace, získané ze seznamu existujících zdrojů dat. O C Texture se RenderDevice dozví pouze v okamžiku kdy ji má vytvořit nebo rušit. 5.1.8 Třídy C CubeTexture a C VolumeTexture Obě tyto třídy jsou v návrhu enginu uvažovány. Jedná se o podobné kontejnery jako je C Texture a implementují se obdobně. Bohužel z časových důvodů nejsou v současné implementaci enginu obsaženy. 5.2 Třídy hraničních objektů Engine je možné doplňovat o nové metody, které řeší, zda je daný objekt pozorovateli viditelný nebo nikoli. Tyto metody vetšinou neřeší viditelnost objektu na základě jeho geometrie, ale na základě jeho určité aproximace. Prostor, který objekt zabírá je možné aproximovat různě. Nejčastěji se však používají různé typy hraničních boxů, nebo hraniční koule. Standardně engine využívá k aproximaci objektů, osově zarovnaný hraniční box. Aby tvůrce nového algoritmu pro detekci viditelnosti objektu nebyl nucen používat právě hraniční box nebo kouli, je celý systém hraničních objektů zděděn z abstraktní třídy C BoundingObject. Ta definuje povinné metody pro všechny typy hraničních objektů. Na obrázku 5.2 je uspořádání dědičnosti hraničních objektů enginu. 5.2.1 Třída C BoundingObject Třída definuje nutné vlastnosti všech hraničních objektů používaných v enginu. Pokud algoritmus detekce viditelnosti narazí na nový typ hraničního objektu se kterým neumí pracovat, může si nechat zkonstruovat hraniční box nebo kouli obalující původní hraniční objekt. K tomu obsahuje třída C BoundingObject metody GetAABBox a GetBSphere. Takto je zajištěna kompatibilita cizího algoritmu detekce viditelnosti s jakýmkoli typem hraničního objektu. Kompatibilitu s interním systémem detekce viditelnosti zajišťuje virtuální metoda IsInFrustum, kterou systém volá při detekci, zda se hraniční objekt nachází v pohledovém jehlanu kamery. Tuto metodu musí implementovat všechny typy obálek v enginu. Hraniční objekty musí být schopné se transformovat do daného souřadnicového systému. Musí tedy implementovat virtuální metodu Transform. Poslední neméně důležitou virtuální metodou C BoundingObject je GetTransformedCenter, která vrací transformovaný střed hraničního objektu. Podle pozice tohoto bodu systém řadí průhledné objekty před jejich vykreslením. 5.2.2 Třída C AABBox Jedná se o třídu reprezentující osově zarovnaný hraniční box (Axis Aligned Bounding Box). Tento typ hraničního boxu je i po transformaci osově zarovnán. Interně si uchovává hodnoty lokálního (netransformovaného) hraničního boxu. Tyto hodnoty při požadavku

50 KAPITOLA 5. REALIZACE C_BoundigObject +IsInFrustum() +GetAABBox() +GetBSphere() +Transform() +GetCenter() +GetTransformedCenter() C_AABBox C_BSphere C_OBBox +IsInFrustum() +GetAABBox() +GetBSphere() +Transform() +GetCenter() +GetTransformedCenter() +Absorb() +SetMin() +GetMin() +SetMax() +GetMax() +SetMinMax() +IsInFrustum() +GetAABBox() +GetBSphere() +Transform() +GetCenter() +GetTransformedCenter() +SetCenter() +SetRadius() +GetRadius() +GetTransformedRadius() +IsInFrustum() +GetAABBox() +GetBSphere() +Transform() +GetCenter() +GetTransformedCenter() +Absorb() +SetMin() +GetMin() +SetMax() +GetMax() +SetMinMax() Obrázek 5.2: Dědičnost tříd hraničních objektů. na transformaci transformuje a z nově vzniklý orientovaný box opět uzavře do osově zarovnaného boxu. Tento typ je v současné době standardně využíván ve třídě C Visual. 5.2.3 Třída C BSphere Třída reprezentuje hraniční kouli. Díky své jednoduchosti je její test viditelnosti rychlejší než u hraničních boxů, ale ve většině případů je horší aproximací prostoru obsazeného objektem než je box. 5.2.4 Třída C OBBox C OBBox je podobná třídě C AABBox. V lokálních souřadnicích objektu se jedná o osově zarovnaný box definovaný minimálním a maximálním bodem. Po transformaci se však už jedná o orientovaný box v prostoru. Tento box je pak definován všemi svými vrcholy v rozích. Tato třída je sice navržena, ale z časových důvodů není součástí aktuální implementace enginu. 5.3 Třídy scénového grafu Třídy scénového grafu v sobě nesou logiku spojenou s transformacemi souřadnicových systémů, systém pro detekci viditelných objektů apod. Všechny uzly scénového grafu vycházejí z uzlu C Frame. Diagram dědičnosti je na obrázku 5.3. Abychom mohli od sebe jednotlivé typy uzlů rozlišit je C Frame zděděn od třídy C Object, která popisuje typ objektů.

KAPITOLA 5. REALIZACE 51 +GetMasterTypeID() +GetSubTypeID() +Release() +TypeID C_Object +GetParent() +GetChildCount() +GetChild() +NextSiblink() +PrevSiblink() +LinkTo() +Unlink() +AddFlags() +RemoveFlags() +GetFlags() +SetName() +GetName() +ClearTransform() +SetTranstlation() +SetRotation() +SetScale() +GetMatrix() +GetTransform() +ClearWorldTransform() +SetWorldTranslation() +SetWorldRotation() +SetWorldScale() +GetWorldTransform() +Update() +TimeUpdate() +PreRenderUpdate() +GetBoundigObject() +Release() #AfterLinkTo() #OnWorldChange() C_Frame +LookAt() +GetUp() +GetDir() +GetRight() +SetProjectionMatrix() +GetProjectionMatrix() +SetPerspectiveFov() +SetPerspective() +SetOrtho() +SetCameraMatrix() +GetCameraMatrix() +GetFrustum() +Release() C_Camera +GetFaceGroupCount() +AddFaceGroup() +GetFaceGroup() +DeleteFaceGroup() +DeleteAllFaceGroups() +UpdateBoundObject() +GetBoundingObject() +Update() +TimeUpdate() +PreRenderUpdate() +Release() C_Visual +GetCameraCount() +GetCamera() +GetSceneRootCount() +GetSceneRoot() +FindFrames() +UpdateUpdate() +PreRenderUpdate() +Release() #RegisterFrame() #UpdateFrame() #UnregisterFrame() C_SceneRoot +GetType() +SetDir() +GetDir() +SetDiffuse() +GetDiffuse() +SetAmbient() +GetAmbient() +SetSpecular() +GetSpecular() +SetRange() +GetRange() +SetFalloff() +GetFalloff() +SetAttenulation0() +GetAttenulation0() +SetAttenulation1() +GetAttenulation1() +SetAttenulation2() +GetAttenulation2() +SetInnerConeAngle() +GetInnerConeAngle() +SetOuterConeAngle() +GetOuterConeAngle() +GetBoundVolume() +Release() C_Light Obrázek 5.3: Dědičnost tříd scénového grafu.

52 KAPITOLA 5. REALIZACE 5.3.1 Třída C Object C Object zajišťuje jednoznačnou identifikací třídy uvnitř enginu. Všechny objekty scénového grafu, interface importních tříd a render device je zděděno od této třídy. Celá identifikace typu objektu se skládá z takzvaného MasterType a SubType. Master- Type určuje třídu objektu tedy zda se jedná o objekt typu Frame, Visual nebo importní třídu TextureImport apod. Jelikož můžeme mít v enginu více typů jednotlivých objektů (například více importních tříd), tak se celý typ ještě skládá z podtypu (SubType). Sub- Type je náhodné číslo, které si zvolíme, když vytváříme nový podtyp dané třídy objektu. Základní objekty enginu mají podtyp vždy roven nule. C Object umožňuje porovnávat objekty na základě jejich kompletního typu, tedy dotaz zda se jedná o stejný typ objektu (ne instanci), nebo se můžeme dotazovat zda se jedná o danou třídu objektu to je výhodné například v algoritmech detekce viditelnosti, nebo při dotazech do scénového grafu. 5.3.2 Třída C Frame Třída je základním prvkem scénového grafu. Všechny třídy, které je možno vkládat do scénového grafu musí být odvozeny od C Frame. C Frame zajišťuje veškerou logiku transformací. Uživateli nabízí mnoho funkcí pro změnu lokální transformace. Dokáže také automaticky dopočítat změnu lokální transformace v případě, že zadáme transformaci ve světových souřadnicích. Veškeré změny transformací jsou nabízeny v uživatelsky přívětivém formátu, kdy změna jednoho parametru transformace nijak neovlivní jiný parametr. Například změna matice rotace neovlivní scale. Třída umožňuje zapojení do scénového grafu. Obsahuje tedy metody pro práci s potomky, a umožňuje nalinkování sama sebe pod jiný uzel scénového grafu. V tom okamžiku se stává potomkem uzlu, pod který byla třída nalinkována. Třída má několik virtuálních metod, které využívají potomci C Frame. Význam těchto metod je uveden v tabulce 5.1. Třídě C Frame je možné nastavit jméno a také obsahuje metody pro nastavení příznaků, které definují vlastnosti uzlu scénového grafu a nastavují automatické volání některých virtuálních metod. 5.3.3 Třída C SceneRoot Třída doplňuje C Frame o možné dotazy do scény. V současné implementaci jsou to dotazy na počet kamer a získání příslušné kamery z podstromu. Dále se můžeme dotazovat na počet uzlů typu C SceneRoot v podstromu. Jednotlivé uzly je možno pak snadno získat pomocí jejich indexu. Další typy dotazů je možné doplnit zděděním uzlu C SceneRoot a doplněním nové třídy o požadovaný dotaz. Nejdůležitější metodou třídy je virtuální metoda FindFrames. Tělo této metody definuje algoritmus, který z podstromu vybírá objekty viditelné pozorovateli. Tento algoritmus je možné v nové třídě přetížit a tak doplnit systém o nový typ detekce viditelnosti. Třídy zděděné od C SceneRoot mohou využít virtuálních metod z tabulky 5.2. 5.3.4 Třída C Visual C Visual umožňuje do scénového grafu vložit základní geometrii. Jedná se o kontejner FaceGroup, doplněný o hraniční objekt. Všechny visuální objekty, které je možné do scény vkládat musí být odvozeny od této třídy. Třída je navržena pro uchování statické

KAPITOLA 5. REALIZACE 53 Název TimeUpdate PrerenderUpdate Význam funkce Funkce je automaticky volána při změně času scény. Každý uzel scénového grafu může zažádat o volání této funkce nastavením příslušného příznaku (flagu) uzlu C Frame. Funkce je automaticky volána před vykreslením snímku kamerou, kterou dostane funkce jako parametr. O volání funkce si uzel scénového grafu zažádá nastavením příslušného příznaku (flagu). UpdateBoundObject Uživatelem volaná funkce v okamžiku, kdy je nutno přepočítat hraniční objekt. GetBoundingObject Metoda vrací aktuální hraniční objekt. Virtuální je z důvodu, že každý uzel scénového grafu může počítat hraniční objekt jiným způsobem a vrací pak jinou instanci hraničního objektu. Update OnWorldChange AfterLinkTo Uživatelem volaná funkce po změně nastavení příznaků (flagů), nebo změně velikosti hraničního objektu u statických visuálů. Automaticky volaná funkce v případě, kdy se uzlu změnila světová matice. Automaticky volaná funkce v případě, kdy byl uzel přelinkován pod jiný uzel. Tabulka 5.1: Virtuální metody třídy C Frame a jejich význam. Název FindFrames RegisterFrame UpdateFrame Význam funkce Metoda definuje algoritmus detekce viditelných objektů. Jako vstup má metoda kameru pro kterou chceme vyhledat viditelné objekty a seznam do kterého viditelné objekty zařazujeme. Metoda je automaticky volána v okamžiku, kdy je do podstromu tohoto SceneRoot uzlu nalinkován nový uzel. Funkce má jako vstupní parametr odkaz na tento vkládaný uzel. Metodu je možné použít k úpravě struktur pro detekci viditelnosti. Metoda je volána v okamžiku, kdy uživatel zavolá metodu Update některého z uzlu v podstromu tohoto SceneRoot uzlu. UnregisterFrame Metoda je automaticky volána v okamžiku, kdy je z podstromu tohoto SceneRoot uzlu vyjmut některý uzel. Funkce má jako vstupní parametr odkaz na vyjímaný uzel. Metodu je možné použít k úpravě struktur pro detekci viditelnosti. Tabulka 5.2: Významné virtuální metody třídy C SceneRoot a jejich význam.

54 KAPITOLA 5. REALIZACE geometrie uložené ve strukturách FaceGroup. Dynamickou geometrii jako jsou například animované objekty, částicové systémy, objekty s proměnlivou úrovní detailů a podobně si uživatel enginu vytváří sám, na základně této třídy. K tomu může využít virtuálních metod z třídy C Frame. 5.3.5 Třda C Camera Třída kamery doplňuje C Frame o matici promítání a funkce s ní spojené. Tato matice definuje způsob promítání scény. Implicitně je nastavena na perspektivní promítání. Kamera na základě nastavených parametrů pozice a promítání dokáže spočítat takzvané frustum. Jedná se o pohledový jehlan definující prostor snímaný kamerou. Tento prostor můžeme ohraničit šesti rovinami horní, spodní, pravá, levá, přední a zadní rovina. Těchto rovin je následně využito v algoritmu detekce viditelných objektů. Třída C Camera plně využívá metod pro nastavení orientace a pozice ve scéně, zděděných z C Frame. Na rozdíl od ostatních uzlů scénového grafu, musí být výsledná transformace kamery provedena inverzně. To plyne z následujícího příkladu: Pohneme-li kamerou doprava, musí se scéna zobrazovaná touto kamerou posunout doleva. Obdobně je tomu u rotací. Proto je třída doplněna o funkci GetCameraMatrix, která vrací matici inverzní světové transformace. Této transformace by měl využít renderer, který chce vykreslit scénu danou kamerou. 5.4 Třídy pro podporu pluginů Podpora pluginů byl jedním z cílů této práce. Nestačí však vytvořit podporu pouze v návrhu jednotlivých rozhraní tříd, ale musíme také engine doplnit o možnost snadného připojení existujícího pluginu. K tomu tomu slouží následující dvě třídy: C Plugin a C ClassDesc. 5.4.1 Třída C Plugin Pod pojmem plugin si většinou představíme dynamickou knihovnu, kterou můžeme existující aplikaci doplnit o určitou novou funkčnost. Stejně tak tomu je i v našem enginu. Engine můžeme doplnit o podporu importu nových typů dat, o nový typ RenderDevice nebo o nové typy uzlů scénového grafu. Plugin může obsahovat i větší počet tříd. Třída C Plugin se stará o komfortní načtení dll souboru, včetně potřebných kontrol a z zpřístupnění tříd obsažených v pluginu. Dynamicky linkovaná knihovna dokáže exportovat pouze funkce, nikoliv celé třídy. Abychom byli schopni snadno vytvářet importní třídy, definujeme si funkce, které nám umožní získat adresu tříd z vnitřku pluginu. Na obrázku 5.4 je vidět způsob napojení exportovaných funkcí pluginu na metody třídy C Plugin. Po připojení pluginu můžeme díky metodě GetClassCount zjistit počet tříd určených pro import z pluginu do aplikace. Metoda GetClassDesc potom dokáže získat adresu objektu, který popisuje importovanou třídu a dokáže vytvořit novou instanci této popisované třídy. Díky těmto metodám tedy dostaneme určitý popis tříd, které plugin nabízí, aniž bychom byli nuceni vytvářet jejich instance. Každý plugin také exportuje funkci RWELibDescription, která vrací slovní popis pluginu (informuje uživatele k jakému záměru byl plugin vytvořen) a funkci RWELibVersion, která vrací verzi API enginu, pro kterou byl plugin zkompilován. Verze API je nutné kontrolovat, abychom k enginu nepřipojili nekompatibilní (zastaralý) plugin.

KAPITOLA 5. REALIZACE 55 Třída C_Plugin Plugin GetClassCount() RWELibClassCount() Class Count: 3 static ClassDesc1 static ClassDesc2 static ClassDesc3 i GetClassDesc(int i) RWELibClassDesc( int i ) Obrázek 5.4: Způsob připojení pluginu k třídě C Plugin. 5.4.2 Třída C ClassDesc Třída C ClassDesc popisuje třídu, kterou je možné importovat z pluginu. Každá třída, která má být importována z pluginu, musí mít i svou třídu C ClassDesc. C ClassDesc popisuje třídu jak slovně, pomocí krátkého a dlouhého slovního popisu, tak i pomocí MasterType a SubType, které vyjadřují typ třídy v programu. Nejdůležitější metodou C ClassDesc je CreateInstance, která vytváří již konkrétní instanci popisované třídy. Tuto instanci je pak možné využít kdekoliv v enginu. Nejčastěji se však jedná o importní třídy, nebo RenderDevice a ty spravuje třída C Core automaticky. 5.5 Podpůrné třídy enginu Podpůrné třídy enginu v sobě nesou algoritmy, které jistě bude uživatel enginu nebo RenderDevice vyžadovat. Tím, že tyto třídy vytvoříme, zajistíme správnost algoritmů a nenutíme uživatele enginu tyto algoritmy vytvářet znovu a znovu v jednotlivých aplikacích využívajících engine. Navíc, některé akce které podpůrné třídy obsahují, by ani uživatel nemohl naprogramovat z důvodu úzkého propojení mezi třídami enginu. 5.5.1 C Core Třída tvoří jádro enginu. Přes její interface se provádí veškeré operace nastavení enginu. Třída spravuje připojené pluginy. Umožňuje snadno připojit nový plugin a prozkoumat jeho nabízené funkce. Z připojených pluginů, třída automaticky importuje třídy pro import textur, scén a RenderDevice. Stačí jen připojit plugin a okamžitě můžeme využívat výhod importu dat z nových formátů. Třída také obsahuje centralizovanou část správy zdrojů dat. Dokáže vytvářet jednotlivé zdroje dat a automaticky je synchronizuje podle požadavků aktuálně připojeného RenderDevice. Třída slouží také k nastavení a inicializaci RenderDevice.

56 KAPITOLA 5. REALIZACE 5.5.2 C FrameList C FrameList slouží jako univerzální seznam. Je využíván hlavně v případě, že vytváříme vlastní RenderDevice. Do tohoto seznamu ukládá algoritmus detekce viditelnosti viditelné objekty. Seznam umí s těmito objekty pracovat. V současné době nabízí pouze možnost setřídění vložených objektů podle vzdálenosti od kamery. V budoucnu by měl nabízet složitější metody třídění a vyhledávání. Příkladem může být třídění objektů nejen na základě vzdálenosti od kamery, ale také podle použitého materiálu, nebo vyhledání všech světel osvětlující určený objekt a podobně. Toto třídění je určeno k urychlení vykreslování scény a používá se téměř ve všech rendererech. Proto je vhodné tyto algoritmy umístit do enginu, tak aby je tvůrce nového Renderdevice nemusel implementovat.

KAPITOLA 6. TESTOVÁNÍ 57 6 Testování Testování enginu je možné rozdělit do dvou hlavních částí: testování dílčích algoritmů enginu a testování enginu jako komplexního celku. 6.1 Testování algoritmů enginu Testování jednotlivých algoritmů bylo prováděno v okamžiku jejich implementace. Většinou se jedná o známé algoritmy a jejich testování tak bylo triviální a bylo jej možné zajistit hned po jejich implementaci. Engine je možné přeložit jak v debug tak release módu. V debug módu engine provádí logování veškeré alokace a dealokace paměti do souboru a kontroluje, zda všechny alokované paměťové prostředky byly před ukončením programu uvolněny. Logování práce s pamětí zaručuje určitou správnost algoritmů a takto byl engine kontrolován v průběhu celého jeho vývoje. Dalším nástrojem pro testování algoritmů byl vlastní Trace Server. Jedná se o separátní program, který dokáže přijímat logovací informace z enginu a programu, který engine využívá. Tyto logovací informace je možné snadno použít pro testování jak jednotlivých algoritmů enginu, tak i pro test komplexního chování. V tom případě je nutné aby programátor podrobně analyzoval vypsaná hlášení. Screenshot Trace Serveru je na obrázků 6.1. Jedná se o neocenitelný nástroj, který mi pomohl odladit skryté chyby, zejména v situacích, kdy program záhadně havaroval. I v takových situacích je seznam logovacích informací stále k dispozici. Obrázek 6.1: Okno Trace Serveru s logovacími informacemi po spuštění enginu. 6.2 Testování enginu Abychom otestovali engine jako celek, bylo nutné testovat jeho jednotlivé části:

58 KAPITOLA 6. TESTOVÁNÍ Test funkce scénového managementu. Test připojení a synchronizace zdrojů dat s RenderDevice. Test importu textur. Test importu scény. Test vkládání specializovaných uzlů do scénoého grafu. 6.2.1 Testovací aplikace EngineTestNET Abych mohl snadno provést výše zmíněné testy, vytvořil jsem testovací aplikaci využívající engine (viz. obrázek 6.2). Tato aplikace je napsaná v managed C++ (platforma.net). Aplikace umožňuje provádět základní operace s enginem. Zobrazuje aktuální scénový graf enginu, umožňuje jeho editaci: transformaci uzlů grafu, jejich rušení a vkládání, přelinkování uzlů do jiné části podstromu apod. Testování správné funkce scénového grafu byla provedena pomocí akcí provedených ve scénovém grafu a visuální kontrolou změn ve scéně. Aplikace umožňuje za běhu připojit libovolný plugin, čímž okamžitě zpřístupní jeho importní funkce, popřípadě jeho RenderDevice uživateli. Nově připojený plugin je tak možné možné hned otestovat. Pro snazší visuální kontrolu scény, umožňuje aplikace scénou libovolně procházet. Obrázek 6.2: Hlavní okno testovací aplikace. Vlevo je zobrazen scénový graf enginu, vpravo zobrazovaná scéna. Horní toolbar nabízí tlačítka pro editaci transformace vybraného uzlu a pro skrytí scénového grafu.