Mendelova univerzita v Brně Provozně ekonomická fakulta
Možnosti jazyka Java pro práci s 3D grafikou Bakalářská práce
Vedoucí práce: Ing. Petr Jedlička, Ph.D.
Miroslav Švec
Brno 2010
{ zde se nachází originál zadání práce }
Děkuji vedoucímu mé pr{ce, panu Ing. Petru Jedličkovi, Ph.D., za smysluplné a ucelené odborné vedení, konstruktivní rady a výraznou podporu a motivaci po celou dobu psaní této bakal{řské pr{ce.
Prohlašuji, že jsem tuto bakal{řskou pr{ci vypracoval samostatně a v seznamu literatury a pramenů uvedl veškeré informační zdroje, které jsem použil.
Brno 4. ledna 2010
<<<<<<<<<<<<<<
Abstract ŠVEC, M. Support for creating 3D scenes using Java. Bachelor thesis. Brno, 2010. The thesis deals with theoretical and practical basis for creating 3D scenes using Java3D and JOGL. The methodological part provides information on methods, which are used, for creating an example. The practical part shows how the 3D scene is build. Practical part also describes support for loading 3D models.
Keywords Java3D, JOGL, 3D scenes, Java
Abstrakt ŠVEC, M. Možnosti jazyka Java pro pr{ci s 3D grafikou. Bakal{řsk{ pr{ce. Brno, 2010. Pr{ce se zabýv{ teoretickými a praktickými z{klady pro vytv{ření 3D scén, za použití Java3D a JOGL. Metodick{ č{st poskytuje informace o metod{ch, které jsou použity při tvorbě příkladu. Praktick{ č{st ukazuje, jak je 3D scéna vytvořena. V praktické č{sti je z{roveň pops{na podpora Javy při nahr{v{ní 3D modelů.
Klíčová slova Java3D, JOGL, 3D scény, Java
6
Obsah 1
2
Úvod a cíl pr{ce ...................................................................................................... 8 1.1
Úvod pr{ce ........................................................................................................ 8
1.2
Cíl pr{ce ............................................................................................................ 8
Teoretick{ východiska pr{ce ................................................................................. 9 2.1
2.1.1
Osvětlení .................................................................................................... 9
2.1.2
Vzhled ........................................................................................................ 9
2.2
4
Java3D ............................................................................................................... 9
2.2.1
Graf scény ................................................................................................ 10
2.2.2
Jednotlivé třídy Java3D ........................................................................... 12
2.2.3
Podpora Java3D ....................................................................................... 14
2.3
3
Obecné problémy tvorby 3D scény ................................................................. 9
JOGL ................................................................................................................ 15
2.3.1
Jednotlivé třídy a metody JOGL ............................................................ 16
2.3.2
Podpora JOGL ......................................................................................... 18
Metodika ................................................................................................................ 19 3.1
Demonstrační model ...................................................................................... 19
3.2
Použité vývojové n{stroje .............................................................................. 19
Implementace ........................................................................................................ 20 4.1
Java3D ............................................................................................................. 20
4.1.1
Vytvoření vesmíru .................................................................................. 20
4.1.2
Osvětlení .................................................................................................. 21
4.1.3
Rozmísťov{ní objektů ............................................................................. 21
4.1.4
Upravení vzhledu ................................................................................... 23
4.2
JOGL ................................................................................................................ 25
7
4.2.1
Vytvoření okna ........................................................................................ 25
4.2.2
Osvětlení .................................................................................................. 26
4.2.3
Rozmístění objektů.................................................................................. 26
4.2.4
Upravení vzhledu ................................................................................... 28
4.3
Podpora 3D modelů ....................................................................................... 33
4.3.1
Hraniční reprezentace ............................................................................. 34
4.3.2
Objemov{ reprezentace .......................................................................... 34
4.3.3
Struktura 3D modelů .............................................................................. 34
4.3.4
Popis vybraných 3D modelů .................................................................. 35
4.3.5
Princip načít{ní 3D modelů .................................................................... 35
5
Z{věr ...................................................................................................................... 37
6
Literatura ............................................................................................................... 38
1 - Úvod a cíl práce
8
1 Úvod a cíl práce 1.1 Úvod práce Informační technologie jsou běžnou souč{stí našich životů. S jejich rozvojem se objevilo mnoho možností, jak počítače využívat. Od samého poč{tku byly počítače využív{ny také k z{bavě. První grafické hry pracovaly s jednoduchou dvourozměrnou grafikou. Díky navyšov{ní výkonu se však začaly objevovat první hry, využívající grafiku trojrozměrnou. V dnešní době pokl{d{me využití 3D grafiky za samozřejmost, kter{ n{s nijak nepřekvapí. K zobrazov{ní trojrozměrné grafiky můžeme použít mnoho knihoven i různých vývojových prostředí. Zajímavou možností je využití programovacího jazyka Java. Díky jeho struktuře, n{m poskytuje způsob, jak vytvořit v podstatě jediný program, který bude možné spouštět na několika operačních systémech, bude možné ho umístit na internetové str{nky a další řadu výhod, které plynou z využití jazyka Java.
1.2 Cíl práce Použív{ní jazyka Java pro vývoj aplikací využívající 3D grafiku je pops{no na řadě míst, ale program{tor se musí sezn{mit s množstvím informací, než je schopen se rozhodnout, jakým způsobem bude při vývoji postupovat a které knihovny bude využívat. Cílem této pr{ce je zjistit, jaké jsou možnosti programov{ní 3D grafiky v Javě, jaké jsou k dispozici balíky knihoven a jak{ je podpora těchto balíků. Při sezn{mení se s knihovnami, zjistíme z{roveň kvalitu dokumentace a další zdroje, kde je možné čerpat potřebné informace. Na z{věr se pokusíme na jednoduchém příkladu demonstrovat možnosti jazyka Java, při tvorbě 3D scén. Protože budeme porovn{vat několik vývojových prostředí, využijeme jeden modelový příklad, který se pokusíme zobrazit za použití každého z vývojového prostředí. Jednotlivé kroky popíšeme a tím demonstrujeme konkrétní rozdíly. Scénou, na které budeme demonstrovat rozdíly, bude stůl, který bude složen ze čtyř v{lců a jednoho kv{dru. Tyto objekty budou doplněny o textury a osvětleny. Po prostudov{ní této pr{ce, by program{tor měl být schopen určit, který typ vývojového prostředí, je pro jeho potřeby vhodnější.
2 - Teoretická východiska práce
9
2 Teoretická východiska práce 2.1 Obecné problémy tvorby 3D scény 2.1.1 Osvětlení Způsob, jakým světlo dopad{ na objekty, vytv{ří stíny a to n{m pom{h{ vidět objekty ve třech rozměrech. Při vytv{ření osvětlení scény můžeme využít několika druhů světel. Ambientní složka vznik{ odrazem "ambientního" světla, což je světlo přich{zející ze všech směrů se stejnou intenzitou. Samotné ambientní světlo n{m, vzhledem ke své povaze, plastické zobrazení objektů ve scéně nezajistí, ale je užitečné, ve spojení s dalšími složkami. Difúzní složka světla představuje odraz světla poch{zejícího z určitého daného zdroje nebo alespoň směru. Od povrchu tělesa se odr{ží do všech směrů stejně. Sama o sobě difúzní složka stačí k zobrazení matných objektů. Poslední ze složek odraženého světla jsou odlesky. Odlesky vznikají ze světla, které se odr{ží podle z{kona odrazu.
2.1.2 Vzhled Materi{ly Materi{ly se využívají, aby všechny objekty na scéně nevypadaly stejně. Každý materi{l se skl{d{ z pěti složek. První z těchto složek, ambientní, určuje, jakou č{st ambientního světla objekt odr{ží. Druh{ složka, emisní, určuje světlo, které objekt s{m vyzařuje. Třetí složka, difuzní, ud{v{, jak{ č{st difúzního světla, přich{zejícího určitého směru, se bude odr{žet. Čtvrt{ složka ud{v{, jak{ č{st světla se bude odr{žet jako odlesk. Poslední složkou je lesklost. Určuje, jak moc se odleskov{ složka světla svým chov{ním odchýlí od z{kona odrazu. Tato složka nabýv{ různých hodnot. Čím menší je hodnota parametru, tím větší je úhel, do něhož se světlo rozptyluje, a tedy i velikost odlesku. Množství odraženého světla, které uvidí pozorovatel, zleží na pozici pozorovatele a úhlu odrazu světla.
Textury Texturov{ní polygonů v Java3D je dosaženo vytvořením příslušného objektu, vzhledu, načtením textury do tohoto objektu, určením umístění obr{zku textury na geometrickém objektu a nastavením texturovacích atributů. Jak uvidíme, nastavení textur může být složité. Naštěstí existují třídy utilit, které n{m s tím pomohou. Z{kladní nastavení textur je vhodné pro jednoduché aplikace textur. V JOGL ž{dný objekt, který by n{m ulehčil načtení textur není. Proto musíme načít{ní textur implementovat sami.
2.2 Java3D Java3D je doplňkem ke knihovn{m jazyka java. Jedn{ se o knihovny vyvíjené přímo společností Sun Microsystems. Knihovny podporují modelov{ní pomocí DirectX i
2 - Teoretická východiska práce
10
OpenGL. Java3D umožňuje jednodušší modelov{ní než některé knihovny popsané níže, ale st{le je modelov{ní dostatečně efektivní, pro tvorbu zajímavých programů, využívajících 3D zobrazení. Java3D API je hierarchie Java tříd, které slouží jako rozhraní pro sofistikované trojrozměrné grafické vykreslov{ní a prostorové ozvučení. Program{tor pracuje s vysokoúrovňovými konstrukcemi pro tvorbu a manipulaci 3D geometrických objektů. Tyto geometrické objekty jsou umístěny ve virtu{lním vesmíru, který je n{sledně vykreslov{n. API je flexibilně navrženo, pro tvorbu přesných virtu{lních vesmírů široké šk{ly velikostí, od astronomických po subatom{rní. Přes všechnu funkcionalitu je st{le jednoduché API používat. Detaily vykreslov{ní jsou obsluhov{ny automaticky. Díky výhodě, jakou je pr{ce s vl{kny, je Java3D j{dro (renderer) schopno vykreslovat paralelně. J{dro také prov{dí automatickou optimalizaci, kter{ zvyšuje vykreslovací výkon. Java3D program vytv{ří instance Java3D objektů a umisťuje je do datové struktury grafu scény. Graf scény je uspoř{d{ním 3D objektů ve stromové struktuře kter{ kompletně specifikuje obsah virtu{lního vesmíru a to, jak bude vykreslov{n. Java3D programy mohou být ps{ny jako samostatné aplikace stejně jako applety do prohlížečů které mají rozšíření podporující Java3D, nebo obojí.
2.2.1 Graf scény Java3D virtu{lní vesmír je tvořen grafem scény. Graf scény je vytvořen za použití instancí tříd Java3D. Graf scény se skl{d{ z objektů, které definují geometrii, zvuky, světla, pozici, orientaci a vzhled vizu{lních a zvukových objektů. Graf je běžně definov{n jako množina uzlů a referencí. Uzel je datový element a reference je vztah mezi datovými elementy. Za uzly grafu scény jsou považov{ny instance tříd Java3D. Reference reprezentují dva druhy vztahů mezi instancemi Java3D tříd. Nejběžnějším vztahem je vztah rodič – potomek. Kořen grafu může mít jakýkoliv počet potomků, ale může existovat pouze jeden kořen. Uzel typu list může mít pouze jeden rodičovský uzel, ale ž{dného potomka. Dalším vztahem je odkaz. Odkaz spojuje objekt NodeComponent s grafem scény Node. Objekty NodeComponent definují geometrické a vzhledové atributy použité k vykreslení vizu{lních objektů. Graf scény je zkonstruov{n z uzlů ve vztahu rodič – potomek, tvořících stromovou strukturu. V této stromové struktuře je jeden z uzlů kořenem. Další uzly jsou z kořene přístupné za použití referencí vych{zejících z kořene. Tyto reference nemohou tvořit cykly. Objekty NodeComponent a reference nejsou souč{stí grafu scény. Cesta z kořene grafu scény ke konkrétnímu uzlu listu je cesta grafu scény uzlu listu. Jestliže cesta grafu scény vede přesně k jednomu listu, pak existuje jedna cesta grafu scény pro každý list v grafu scény. Každ{ cesta grafu scény v Java3D grafu scény kompletně specifikuje informaci o stavu svého listu. Informace o stavu zahrnuje umístění, orientaci a velikost vizu{lního objektu. Z toho vyplýv{, že vizu{lní vlastnosti každého vizu{lního objektu z{visí jen na cestě grafu scény daného objektu. Vykreslovací j{dro Java3D těží z tohoto faktu a vykresluje listy v pořadí, které si samo určí jako nejefektivnější. Program{tor v Java3D norm{lně nem{ kontrolu nad pořadím vykreslov{ní objektů. Jedinou kontrolu nad pořadím vykreslov{ní m{ program{tor pomocí třídy OrderedGroup. Tato třída zde není pops{na. Grafické reprezentace grafu scény mohou sloužit jako n{vrhové nebo
2 - Teoretická východiska práce
11
dokumentační n{stroje pro Java3D programy. Grafy scény se kreslí standardními grafickými symboly (obr{zek 1). Java3D programy mohou mít mnohem více objektů než v tomto grafu scény. K navrhov{ní Java3D virtu{lního vesmíru se graf scény kreslí pomocí standardní sady symbolů. Poté co je n{vrh kompletní, ten samý graf je zkr{cenou reprezentací programu (za předpokladu že byl vytvořen podle specifikace). Graf scény nakreslený dle existujícího programu dokumentuje graf scény, který program vytv{ří.
Obr{zek 1
Každý ze symbolů na levé straně reprezentuje samotný objekt použitý v grafu scény. První dva symboly reprezentují objekty dvou konkrétních tříd: VirtualUniverse a Locale. Další tři symboly vlevo objekty třídy Group, Leaf a NodeComponent. Tyto tři symboly jsou často komentov{ny - indikují podtřídu určitého objektu. Poslední symbol vlevo se použív{ jako reprezentace pro jakýkoli jiný objekt. Pln{ šipka představuje vztah rodič – potomek mezi dvěma objekty. Č{rkovan{ šipka je odkaz na jiný objekt. Odkazované objekty mohou být sdíleny mezi různými větvemi grafu scény. Příklad jednoduchého grafu scény (obr{zek 2), (Java3D.org).
2 - Teoretická východiska práce
12
Obr{zek 2
2.2.2 Jednotlivé třídy Java3D
Třída BranchGroup Objekty tohoto typu se používají k tvorbě grafů scény. Instance BranchGroup jsou kořenem subgrafů. Objekty BranchGroup, jsou jedinými objekty, které mohou být dětmi objektů Locale. Objekty BranchGroup můžou mít mnoho dětí. Děti objektu BranchGroup mohou být objekty Group nebo Leaf (Java3D.org).
Z{kladní konstruktor třídy BranchGroup BranchGroup()
Instance BranchGroup slouží jako kořeny větví grafu scény; Objekty BranchGroup jsou jedinými objekty, které mohou být vloženy do sady objektů Locale (Java3D.org).
Třída Canvas3D Třída Canvas3D je odvozena od třídy Canvas z AWT (Abstract Windowing Toolkit). Nejméně jeden objekt Canvas3D musí být odkazov{n z pohledového grafu větve (Java3D.org).
Konstruktor třídy Canvas3D Canvas3D(GraphicsConfiguration graphicsConfig)
Vytv{ří a inicializuje nový objekt Canvas3D, který může Java3D vykreslit, pokud je mu před{n platný objekt GraphicsConfiguration. Je rozšířením třídy Canvas z AWT.
2 - Teoretická východiska práce
13
Více informací o objekt GraphicsConfiguration můžete najít ve specifikaci Java 2D (Java3D.org).
Třída Transform3D Objekty Transform3D představují transformace 3D geometrických objektů jako je posun nebo rotace. Tyto objekty se typicky používají pouze při vytv{ření objektu TransformGroup. Prvně se vytvoří objekt Transform3D, je možné ho vytvořit i z více objektů Transform3D. Poté se vytvoří objekt TransformGroup použitím objektu Transfrom3D (Java3D.org).
Z{kladní konstruktor třídy Transform3D Zobecněný objekt transformace je interně reprezentov{n jako matice 4x4 s dvojitou přesností v plovoucí desetinné tečce. Objekt Transform3D není použit v grafu scény. Je použit k určení transformace objektu TransformGroup. Transform3D()
Vytv{ří objekt Transform3D který představuje nulovou matici (ž{dn{ transformace) Objekt Transform3D může představovat posun, rotaci, změnu velikosti nebo kombinace těchto možností. Když upřesňujeme rotaci, úhel je vyj{dřen v radi{nech. Jedna pln{ otočka je 2 PI radi{nů. Jeden způsob je, použít k vyj{dření úhlů konstantu Math.PI. Jiný způsob je zadat hodnotu přímo (Java3D.org).
Metody třídy Transform3D (č{stečný seznam) Objekty Transform3D představují transformace jako rotace, posun a změna velikosti. Třída Transform3D jednou z m{la těch, co se nepoužívají v ž{dném grafu scény. Transformace objektem Transform3D se používají k vytvoření objektů TransformGroup které se používají v grafu scény. void rotX(double angle)
Nastavuje hodnotu transformace proti směru hodinových ručiček kolem osy x. Úhel se zad{v{ v radi{nech. void rotY(double angle)
Nastavuje hodnotu transformace proti směru hodinových ručiček kolem osy y. Úhel se zad{v{ v radi{nech. void rotZ(double angle)
Nastavuje hodnotu transformace proti směru hodinových ručiček kolem osy z. Úhel se zad{v{ v radi{nech. void set(Vector3f translate)
Nastavuje hodnotu posunu této matice na hodnoty parametru Vector3f, a nastavuje ostatní složky matice jako by to byla nulov{ matice (Java3D.org).
Třída TransformGroup Jako podtřída třídy Group, instance TransformGroup se používají pro vytvoření grafů scény a mají kolekci potomků objektů node. Objekty TransformGroup drží
2 - Teoretická východiska práce
14
transformace jako posun nebo rotace. Transformace je typicky vytvořena objektem Transform3D, který není objektem grafu scény (Java3D.org).
Konstruktory třídy TransformGroup Objekty TransformGroup jsou držiteli transformací v grafu scény. TransformGroup()
Vytv{ří a inicializuje TransformGroup pomocí nulové transformace. TransformGroup(Transform3D t3D)
Vytv{ří a inicializuje TransformGroup z předaného objektu Transform3D. Parametry: t3D – objekt Transform3D Transformace z objektu Transform3D je zkopírov{na do objektu TransformGroup nez{visle na tom jestli je vytvořen objekt TransformGroup nebo pomocí metody setTransform(), (Java3D.org).
Metoda setTransform() třídy TransformGroup void setTransform(Transform3D t3D)
Nastavuje transformační složku tohoto objektu TransformGroup na hodnotu předané transformace. Parametry: t3D – transformace kter{ se m{ zkopírovat (Java3D.org)
Třída Vector3f Vector3f je matematick{ třída, kterou můžeme najít v balíčku javax.vecmath pro specifikov{ní vektoru použitím tří hodnot s plovoucí desetinnou tečkou. Objekty vektoru se často používají k určení posunu. Objekty Vector3f se přímo nepoužívají pro konstrukci grafu scény. Používají se k určení posunu, povrchových norm{l nebo jiných operací (Java3D.org).
2.2.3 Podpora Java3D Projekt Java3D je dílem společnosti Sun Microsystems. Je tedy podporov{n a udržov{n aktu{lní samotnými vývoj{ři společnosti Sun. Mimo to byl těmito vývoj{ři vytvořen několikadílný tutori{l, ve kterém se může program{tor sezn{mit se strukturou a chov{ním knihoven Java3D. To velmi usnadní pr{ci těm, kdo s 3D modelov{ním v jazyce Java začínají. Mimo ofici{lní podporu ze strany společnosti Sun, mohou vývoj{ři čerpat informace ze str{nek projektu j3d.org. To je projekt vytvořený dobrovolníky, kteří se zajímají o 3D modelov{ní v jazyce Java.
2 - Teoretická východiska práce
15
2.3 JOGL Jogl svazuje programovací jazyk Java s rozhraním pro programov{ní aplikací v OpenGL. Podporuje integraci javových platforem AWT a Swing, a z{roveň poskytuje jednoduché rozhraní, ošetřující mnoho problémů, spojených s vytv{řením vícevl{knových OpenGL aplikací. JOGL poskytuje přístup k nejnovějším OpenGL rutin{m, stejně jako hardwarově podporovanému renderov{ní, které je nez{vislé na platformě. V Jogl také najdeme některé z nejpopul{rnějších vlastností, které byly uvedeny v dalších propojeních Javy a OpenGL, jako jsou GL4Java, LWJGL a Magician. K z{kladnímu rozhraní pro programov{ní 3D scén pomocí OpenGLv jazyce C je přistupov{no pomocí Java Native Interface (JNI). Z tohoto důvodu musí systém podporovat OpenGL, aby JOGL fungovalo spr{vně. JOGL se liší od jiných n{strojů, pro napojení Javy na OpenGL tím, že se nesnaží mapovat OpenGL funkcionalitu na objektově orientované programov{ní, ale raději umožní program{torovi využívat metody jazyka C v několika tříd{ch jazyka Java. Navíc je většina kódu JOGL autogenerov{na z kódu jazyka C n{strojem pro konverzi, který se nazýv{ Gluegen. Ten byl vytvořen specificky za účelem vývoje JOGL. Tento design m{ jak výhody, tak nevýhody. Procedur{lní podstata OpenGL je nekonzistentní s typickými metodami pro programov{ní v Javě, ale přímočaré mapov{ní rozhraní, pro programov{ní 3D scén, pomocí OpenGL v jazyce C, ulehčuje konverzi st{vajících aplikací, vytvořených pomocí jazyka C a uk{zkové příklady jsou pro program{tory, přech{zejících k JOGL, z jazyka C, srozumitelnější. Nízk{ míra abstrakčnosti dan{ JOGL, způsobuje, že je běh programů efektivnější, ale z{roveň je jejich tvorba obtížnější ve srovn{ní s knihovnami, které implementují vysokou míru abstrakčnosti, jako například Jav3D. Protože je většina kódu autogenerov{na, jakékoli změny v OpenGL, je možné téměř okamžitě implementovat i do JOGL. Díky struktuře JOGL zde nejsou k dispozici třídy podobné tříd{m Java3D, jako například BranchGroup, TransformGroup a podobně. OpenGL funguje jako stavový automat, což v praxi znamen{, že vždy daným příkazem nějakou ud{lost spustíme a ta je spuštěna, dokud ji neukončíme nebo nezměníme. V JOGL pracujeme hlavně s maticemi, což sebou nese řadu specifik. Matice mají tu výhodu, že umožňují manipulovat s komplexnějšími systémy, než jsou čísla, přičemž st{le udržují vztahy mezi jednotlivými prvky. Například můžeme vzít bod x1,y1,z1 a přičíst k němu vektor x2,y2,z2. Výsledkem bude bod posunutý o daný vektor. Výsledný bod je bodem, který m{ souřadnice x, y, z, vztahy zůstaly zachov{ny, i když se změnila hodnota. Stejně tak jako je možné maticí popsat bod, je možné jí v JOGL popsat moment{lní transformace bodu (rotace, posun) najednou. Podobně je maticí pops{n i způsob, jak se bod ve fin{le jeví na obrazovce, případně i způsob, jak jsou mapov{ny texturovací koordin{ty. V OpenGL m{me k dispozici několik matic, podle toho, s jakým prostorem pracujeme (TUREK).
2 - Teoretická východiska práce
16
2.3.1 Jednotlivé třídy a metody JOGL Třída GL Třída GL je z{kladní třídou pro pr{ci s OpenGL v JOGL. Je vhodné ji nebrat jako třídu, ale jako z{kladní n{stroj, pomocí kterého pracujeme s metodami obsluhujícími OpenGL.
Třída GLU Tato třída je podobn{ třídě GL, ale ovl{d{me pomocí ní jiné metody, jako například vykreslov{ní quadriků a ovl{d{ní kamery.
Metoda gl.glMatrixMode() Pomocí této metody ud{v{me, se kterou prostorovou maticí chceme pracovat. Parametrem může být jedna z n{sledujících hodnot: GL_MODELVIEW GL_PROJECTION GL_TEXTURE GL_COLOR
Metoda gl.glLoadidentity() Metoda slouží pro vynulov{ní změn matice, se kterou pr{vě pracujeme. Matice je nastavena na jednotkovou matici.
Quadric Quadric je jednoduchou cestou k vykreslení komplexních objektů. Pomocí quadriků můžeme vytv{řet komplexní tvary, jako například v{lce, kužely, koule a podobně. Vytv{ření objektů probíh{ automaticky pomocí několika for cyklů. Výhodou quadriků je možnost jejich poměrně jednoduchého pokrytí texturou, díky metod{m, které dok{ží nastavit koordin{ty pro přichycení textury a norm{lové vektory ploch, které se použijí při výpočtu světelných map.
Metoda glu.gluLookAt() Abychom mohli nastavit pohled z perspektivy, použív{ se tato metoda. Jako parametry se ud{vají souřadnice umístění kamey, souřadnice bodu, na který bude pohled směřov{n a vektor, který ud{v{, kam bude směřovat horní stěna pohledu. Jedn{ se v podstatě o norm{lový vektor horní stěny kamery.
2 - Teoretická východiska práce
17
Metoda gl.glPushMatrix Při použití této metody, se aktu{lní matice uloží do paměti. Později je možné ji z paměti načíst.
Metoda gl.glPopMatrix() Metoda sloužící pro načtení dříve uložené matice.
Metoda gl.glTranslatef() Při vol{ní této metody se na st{vající matici aplikuje matice posunu. To zapříčiní, že matice m{ po provedené transformaci poč{tek souřadnic v novém bodě.
Metoda gl.glRotatef() Tato metoda funguje podobně jako metoda předchozí, ale matice se nen{sobí hodnotami úhlů, o které m{ být pootočena, ale hodnotami jejich goniometrických funkcí.
Metoda glu.gluCylinder() Tuto metodu použijeme v případě, že chceme vykreslit v{lec. Jako parametr ud{v{me objekt třídy quadric, hodnotu poloměru horní a dolní z{kladny a výšku. Poslední dva parametry ud{vají hodnotu segmentů, ze kterých bude objekt vykreslen. Pokud bychom například u v{lce zadali, že první počet segmentů bude čtyři, vykreslil by se v{lec, ale byl by složen ze čtyř stěn. To znamen{, že čím vyšší číslo zad{me, tím bude daný objekt detailněji vykreslený. Počet vykreslovaných segmentů ovlivňuje zatížení systému, a proto je potřeba najít optim{lní kompromis mezi kvalitou a výkonem.
Metoda gl.glVertex3f() Umožňuje zad{ní souřadnic vrcholu. Toho se využív{ při vykreslov{ní složitějších obrazců pomocí metody gl.glBegin.
Metoda gl.glBegin() Pokud chceme v JOGL vykreslovat body, č{ry nebo dvourozměrné plochy, použijeme k tomu tuto metodu. Podle parametru, který metodě zad{me, se bude odvíjet, co bude metoda vykreslovat. GL_POINTS Každý vertex je reprezentovaný jako samostatný bod. GL_LINES Každý p{r po sobě jdoucích vertexů definuje úsečku. GL_LINE_STRIP Spojí po sobě jdoucí vertexy v lomenou č{ru.
2 - Teoretická východiska práce
18
GL_LINE_LOOP Stejně jako GL_LINE_STRIP spojuje po sobě jdoucí vertexy, ale navíc ještě spojí první a poslední bod. GL_TRIANGLES Každ{ trojice po sobě jdoucích vertexů definuje trojúhelník. GL_TRIANGLE_STRIP První tři vertexy definují jeden trojúhelník, každý další bod definuje nový trojúhelník s využitím předchozích dvou vertexů jako vrcholy. GL_TRIANGLE_FAN Podobně jako GL_TRIANGLE_STRIP, všechny trojúhelníky využívají první vertex jako jeden svůj vrchol, druhý vrchol je předchozí vrchol. GL_QUADS Každ{ čtveřice vertexů definuje čtyřúhelník. GL_QUAD_STRIP První čtyři vertexy definují jeden čtyřúhelník a každé další dva vertexy definují nový čtyřúhelník, který využív{ předchozí dva vertexy. GL_POLYGON Jako GL_LINE_LOOP, ale s vyplněnou plochou.
Metoda gl.glNormal3f() Metoda slouží k zad{ní norm{lového vektoru pro plochu. Toho se využív{ při vypočít{v{ní světelných map. Pokud bychom například vytvořili kostku bez zad{ní norm{lových vektorů jednotlivých stran, byly by veškeré strany osvětleny stejně.
Metoda gl.glTexCoord2f() Pokud chceme mapovat na jednotlivé vrcholy plochy vrcholy textury, děl{me to pomocí této metody. Vrcholu, který je d{n trojrozměrnými souřadnicemi přiřadíme bod textury, daný pomocí dvourozměrných souřadnic.
2.3.2 Podpora JOGL Java OpenGL je neust{le se vyvíjející projekt, který umožňuje aplikovat metody jazyka C pro pr{ci s OpenGL v Javě. Proto je neust{le podporov{n širokou komunitou vývoj{řů. Hlavním zdrojem informací jsou str{nky projektu JOGL. Tyto str{nky jsou vedeny jako souč{st projektu Kenai, což je projekt společnosti Sun Microsystems pro podporu opensource projektů. Souč{stí projektu není ž{dný n{vod, který by program{torům usnadnil zač{tky s JOGL. Jelikož ale JOGL vych{zí z metod jazyka C pro pr{ci s OpenGL, lze použít NEHE tutori{ly, kde je velmi dobře pops{na pr{ce s OpenGL.
3 - Metodika
19
3 Metodika V teoretické č{sti jsme se sezn{mili se z{kladními znalostmi, nutnými pro vytvoření 3D scény pomocí jazyka Java.
3.1 Demonstrační model Jako příklad, pro demonstraci možností Javy, při tvorbě 3D scény, n{m poslouží jednoduchý model stolu. Model bude tvořen čtyřmi v{lci, které budou představovat nohy stolu a jedním kv{drem, který bude představovat desku stolu. Abychom scéně přidali na realističnosti, nastavíme u stolu optické vlastnosti materi{lu a pot{hneme ho texturou. Jako textura n{m poslouží fotografie skutečného dřevěného stolu v nízkém rozlišení. Pro jednoduchost použijeme jednu texturu pro potažení všech ploch na scéně. Scéna bude samozřejmě nasvícena bodovým světlem a bude použito i ambientní světlo.
3.2 Použité vývojové nástroje Pro vytvoření scény použijeme vývojov{ prostředí Java3D a JOGL. Tyto n{stroje budou dostačující pro demonstraci rozdílného přístupu k tvorbě 3D scény. V č{sti implementace nebude pops{n celý zdrojový kód, ale jen jeho č{sti. To n{m umožní soustředit se na popis problému a nebudeme zatěžov{ni věcmi, jako je například import jednotlivých tříd.
4 - Implementace
20
4 Implementace 4.1 Java3D 4.1.1 Vytvoření vesmíru N{sledující kód obsahuje z{kladní kroky, které potřebujeme k zobrazení 3D scény. 1. Nastavení konfigurace Canvas3D 2. Vytvoří virtu{lní vesmír obsahující naši scénu. 3. Vytvoří datovou strukturu obsahující skupinu s objekty (galaxii). 4. Umístí pozorovatele tak aby mohl vidět objekt na scéně. 5. Přid{ skupinu objektu do vesmíru. (Java3D.org) // Nastavení konfigurace Canvas3D setLayout(new BorderLayout()); GraphicsConfiguration config SimpleUniverse.getPreferredConfiguration(); Canvas3D canvas3D = new Canvas3D(config); add("Center", canvas3D); // Vytvoření grafu scény BranchGroup scene = createSceneGraph(); scene.compile(); // Vytvoření vesmíru s parametrem Canvas3D SimpleUniverse simpleU = new SimpleUniverse(canvas3D); // Přidání grafu scény do vesmíru simpleU.addBranchGraph(scene); Uvedený kód děl{ to, co bylo naším cílem. S{m o sobě by ale k ničemu nebyl, protože na obrazovce by nebyly ž{dné objekty a navíc není nastavena ViewingPlatform, kter{ umožňuje pohled na scénu. Abychom umožnili pohled na scénu, nastavíme ViewingPlatform a z{roveň připravíme proměnné pro nastavení rotace a posunu kamery později. Kameru hned posuneme, abychom na scénu viděli z větší vzd{lenosti. // Získání proměnné reprezentující ViewingPlatform a následně //proměnnou umožňující posunout ViewingPlatform dále od //počátku souřadnic. ViewingPlatform vp = simpleU.getViewingPlatform(); TransformGroup vpGroup = vp.getMultiTransformGroup().getTransformGroup(0); // Nastavení proměnných pro rotaci a posun Transform3D rotX = new Transform3D(); Transform3D rotY = new Transform3D(); Transform3D rotZ = new Transform3D(); Transform3D transform = new Transform3D(); Vector3f vector = new Vector3f(.0f,.0f, 3.0f); // Uplatníme posunutí ViewingPlatform o daný vektor transform.setTranslation(vector);
4 - Implementace
21
vpGroup.setTransform(transform);
4.1.2 Osvětlení Při pohledu na 3D scénu v tomto okamžiku bychom objekty st{le nebyli schopni vidět, protože na scéně není přítomný ž{dný zdroj světla. Abychom objekty přidané do scény byli schopni rozpoznat, přid{me zdroj světla. Objekty, které do scény přid{me, budou bílé (výchozí nastavení). Díky světlu, které může mít různou barvu, se však mohou jevit, jako barevné. Protože se bude jednat o směrové světlo (objekt třídy DirectionalLight), musíme také určit, jak daleko bude světlo svítit a v jakém směru. V našem programu bude světlo svítit na vzd{lenost 50 metrů směrem doleva, dolů a do obrazovky, jak je definov{no vektorem (-4f doleva, -3f dolů, -4f do obrazovky). Také můžeme vytvořit ambientní světlo (objekt třídy AmbientLight), které vlastně nesvítí ž{dným směrem, nebo kruhové světlo (objekt třídy SpotLight), pokud si chceme posvítit jen na určité místo na scéně. Abychom dodali scéně na realističnosti, použijeme AmbientLight. U tohoto druhu světla nemůžeme nastavit intenzitu svitu, proto nastavíme barvu světla pomocí odstínu šedé. Kombinace silného směrového světla a slabšího ambientního osvětlení d{v{ scéně přirozený vzhled. Světla v Java3D nevrhají stíny na jiné objekty. Přid{v{ní stínů je komplikovanější a metody umožňující vrh{ní stínů objekty musí program{tor vytvořit s{m. // Nastavení barev světel Color3f light1Color = new Color3f(1f, 1f, 1f); Color3f light2Color = new Color3f(0.3f, 0.3f, 0.3f); // Nastavení nasměrování a vzdálenosti světel od počátku souřadnic BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 50.0); Vector3f light1Direction = new Vector3f(-4f, -3f, -4f); // Vytvoření světtel DirectionalLight light1 = new DirectionalLight(light1Color, light1Direction); AmbientLight light2 = new AmbientLight(light2Color); light1.setInfluencingBounds(bounds); light2.setInfluencingBounds(bounds); // Přidání světel do scény objRoot.addChild(light1); objRoot.addChild(light2);
4.1.3 Rozmísťování objektů Při vytvoření objektu, je tento objekt umístěn v poč{tku souřadnic vesmíru. V Java3D se jednotliv{ umístění popisují standardně souřadnicemi X, Y a Z. Souřadnice se zvětšují směrem doprava na ose x, směrem nahoru na ose y a směrem do obrazovky na ose z.
4 - Implementace
22
Tomuto se řík{ pravoruký systém souřadnic, protože můžeme použít palec a dva první prsty pravé ruky jako jednotlivé osy souřadnic. Všechny vzd{lenosti jsou měřeny v metrech. Při rozmísťov{ní objektů na scéně začín{me v bodě (0, 0, 0) a poté je můžeme posouvat, kam chceme. Pohybov{ní objekty se řík{ transformace, takže třídy, které budeme používat, se jmenují TransformGroup a Transform3D. Objekty na scéně a objekty Transform3D přid{v{me do TransformGroup předtím než přid{me samotný objekt TransformGroup na scénu. Tento systém byl již použit pro umístění ViewingPlatform. V n{sledujícím kódu je demonstrov{no vytvoření a umístění objektů. Parametry primflags a ap n{m umožňují nastavit vzhled a textury objektů níže popsaným způsobem. // Vytvoření objektů Cylinder cylinder1 = new Cylinder(0.03f, 0.5f, primflags, Cylinder cylinder2 = new Cylinder(0.03f, 0.5f, primflags, Cylinder cylinder3 = new Cylinder(0.03f, 0.5f, primflags, Cylinder cylinder4 = new Cylinder(0.03f, 0.5f, primflags, Box box1 = new Box(0.6f, 0.01f, 0.6f, primflags, ap); Sphere sphere1 = new Sphere(0.2f, primflags, ap);
ap); ap); ap); ap);
//Vytvoření Transformgroup a Transformace a vektoru TransformGroup tg1 = new TransformGroup(); TransformGroup tg2 = new TransformGroup(); TransformGroup tg3 = new TransformGroup(); TransformGroup tg4 = new TransformGroup(); TransformGroup tg5 = new TransformGroup(); TransformGroup tg6 = new TransformGroup(); Transform3D transform = new Transform3D(); Vector3f vector = new Vector3f(); // Umístění objektů do dané TransformGroup s daným umístěním vector.set(0.5f, .0f, 0.5f); transform.setTranslation(vector); tg1.setTransform(transform); tg1.addChild(cylinder1); vector.set(0.5f, .0f, -0.5f); transform.setTranslation(vector); tg2.setTransform(transform); tg2.addChild(cylinder2); vector.set(-0.5f, .0f, 0.5f); transform.setTranslation(vector); tg3.setTransform(transform); tg3.addChild(cylinder3); vector.set(-0.5f, .0f, -0.5f); transform.setTranslation(vector); tg4.setTransform(transform); tg4.addChild(cylinder4);
4 - Implementace
23
vector.set(.0f, 0.255f, .0f); transform.setTranslation(vector); tg5.setTransform(transform); tg5.addChild(box1); // Přidání všech TransformGroup do jedné společné. // To umožňuje pracovat s danou skupinou objektů jako s jedním tg6.addChild(tg1); tg6.addChild(tg2); tg6.addChild(tg3); tg6.addChild(tg4); tg6.addChild(tg5); objRoot.addChild(tg6); Třída Transform3D m{ i další metody jako setScale(), umožňující změnu velikosti objektu a rotX(), rotY() a rotZ() sloužící pro rotaci kolem os (proti směru hodinových ručiček). Toho využijeme pro natočení ViewingPlatform pomocí předem připravených souřadnic. N{sledující kód je stejný jako kód pro posunutí ViewingPlatform d{le od poč{tku souřadnic, je však obohacen o otočení kolem osy X a Y. Protože Java3D neumožňuje postupně nastavit rotaci kolem jednotlivých os, využijeme jednotlivých proměnných, ze kterých n{sledně uplatníme jejich vektorový součin. ViewingPlatform vp = simpleU.getViewingPlatform(); TransformGroup vpGroup = vp.getMultiTransformGroup().getTransformGroup(0); // Vytvoření jednotlivých rotací Transform3D rotX = new Transform3D(); Transform3D rotY = new Transform3D(); Transform3D rotZ = new Transform3D(); Transform3D transform = new Transform3D(); // Nastavení rotací kolem jednotlivých os rotX.rotX(Math.toRadians(-15.0)); rotY.rotY(Math.toRadians(-55.0)); rotZ.rotZ(Math.toRadians(0.0)); // Uplatnění rotací pomocí vektorového součinu transform.mul(rotY); transform.mul(rotX); transform.mul(rotZ); Vector3f vector = new Vector3f(-3.0f, 1.0f, 2.0f); transform.setTranslation(vector); vpGroup.setTransform(transform);
4.1.4 Upravení vzhledu Existuje mnoho způsobů jak změnit vzhled objektů na scéně. Můžeme změnit jejich barvu, kolik světla odr{žejí, můžeme na ně aplikovat dvojrozměrné obr{zky nebo aplikovat textury na jejich povrch. Třída Appearance obsahuje metody, které umožňují dělat takové změny. Nyní tyto metody použijeme.
4 - Implementace
24
Nejjednodušším způsobem nastavení vzhledu, je určit barvu objektu a metodu stínov{ní. To funguje jednoduše, ale aby objekt vypadal realističtěji, musíme určit, jak se bude chovat na světle. To uděl{me vytvořením materi{lu. Změna faktoru lesku ovlivní nejen lesk objektu, ale i velikost plochy odlesku. Pro většinu objektů můžeme použít jednu barvu jak pro ambientní tak i difuzní složku a černou barvu pro emisní složku (většina věcí ve tmě nesvítí). Textury a materi{l Materi{ly způsobí změnu celého objektu, ale občas n{m ani toto nastavení nebude stačit. Přid{ním textury můžeme vyrobit zajímavější efekty jako třeba mramor nebo obalit celý objekt do dvourozměrného obr{zku. Třída TexturLoader n{m umožňuje použít obr{zek jako texturu. Rozměry obr{zku musí být mocninami čísla 2. Když načít{me texturu, můžeme také určit, jak chceme obr{zek použít. Například RGB načte obr{zek barevně, zatímco LUMINANCE ho zobrazí černobíle. Po načtení textury můžeme změnit vlastnosti objektu třídy TextureAttributes a říci zda chceme objekt nahradit tímto obr{zkem nebo zda jej budeme modulovat barvou. Můžeme jej také aplikovat jako otisk nebo jej prolnout s jakoukoli barvou. Pokud použijeme jednoduchý objekt, jako je v{lec nebo kv{dr, pak musíme také umožnit texturov{ní nastavením "primitivních konstant". Ty můžeme nastavit při vytv{ření objektu jako Primitive.GENERATE_NORMALS + Primitive.GENERATE_TEXTURE_COORDS.
// Vytvoření barev Color3f black = new Color3f(0.0f, 0.0f, 0.0f); Color3f white = new Color3f(1.0f, 1.0f, 1.0f); // Nahrání textur TextureLoader loader = new TextureLoader("stul.jpg", "RGB", new Container()); Texture texture = loader.getTexture(); // Nastavení vlastností textury texture.setBoundaryModeS(Texture.WRAP); texture.setBoundaryModeT(Texture.WRAP); texture.setBoundaryColor(new Color4f(0.0f, 0.0f, 0.0f, 0.0f)); TextureAttributes texAttr = new TextureAttributes(); texAttr.setTextureMode(TextureAttributes.MODULATE); // Nastavení vzhledu Appearance ap = new Appearance(); ap.setTexture(texture); ap.setTextureAttributes(texAttr); // Nastavení materiálu ap.setMaterial(new Material(white,black,white,black,1.0f)); int primflags = Primitive.GENERATE_NORMALS + Primitive.GENERATE_TEXTURE_COORDS;
4 - Implementace
25
Výslednou scénu vytvořenou pomocí Java3D vidíme na obr{zku. (obr{zek 3).
Obr{zek 3
4.2 JOGL 4.2.1 Vytvoření okna N{sledující kód vytvoří swingový JFrame, na který je umístěn canvas (kreslící pl{tno pro OpenGL renderov{ní) a spouští Animator, aby se automaticky překreslovala scéna. // Vytvoření nového plátna pro vykreslení scény pomocí OpenGL GLCanvas canvas = new GLCanvas(); // Zajištění automatického překreslování okna Animator animator = new Animator(); canvas.addGLEventListener(new Renderer()); animator.add(canvas); // Nastavení plátna this.getContentPane().add(canvas); this.setTitle("Lekce 3: Textury na krychli"); this.setSize(640, 480); this.setLocationRelativeTo(null); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); // Spuštění Animátoru animator.start();
4 - Implementace
26
Importujeme GL, což je z{kladní rozhraní pro zpřístupnění funkcí z OpenGL verze 2.0. GLAutoDrawable vych{zí z GLDrawable, který slouží k renderov{ní scén, ale podporuje vykreslov{ní pomocí ud{lostí, což přispív{ k vyššímu výkonu. GLU se využív{ k nastavení pohledu. private GLU glu = new GLU(); private GL gl;
Smažeme obrazovku a hloubkový buffer, aby se při každém vykreslení scéna spr{vně zobrazila. // Smazání obrazovky a hloubkového bufferu gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); // Reset matice gl.glLoadIdentity();
4.2.2 Osvětlení Abychom na scéně něco viděli, nastavíme osvětlení a zapneme je. Při pr{ci se světly je nutné nastavit ambientní světlo, které je všude a zdroj difúzního světla, u kterého je potřeba definovat i jeho polohu. Nesmí se zapomenout dodefinovat ke každé ploše, ze které bude vytvořena deska stolu norm{lové vektory, aby OpenGL vědělo, jestli je dan{ plocha osvětlen{ a pokud ano, tak jakou intenzitou. Přid{me čtyři nové atributy, kde nastavíme barvu ambientního světla, barvu a polohu difúzního světla. // Nastavení atributů světel float light_ambient[] = { 0.2f, 0.2f, 0.2f, 0.0f }; float light_diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f }; float light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f }; // Nastavení pozice difuzního světla float light_position[] = { 2.0f, 2.0f, 2.0f, 0.0f }; // Propojení atributů světel se světly gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, light_ambient, 0); gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, light_diffuse, 0); gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, light_specular, 0); gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, light_position, 0); // Zapnutí světel a našeho světla gl.glEnable(GL.GL_LIGHTING); gl.glEnable(GL.GL_LIGHT0);
4.2.3 Rozmístění objektů Po inicializaci, nastavení a osvětlení scény přistoupíme k samotnému vykreslení modelů. Nejprve postupně vykreslíme čtyři v{lce, které představují nohy stolu. Poté vykreslíme šest stěn desky stolu. Desku stolu musíme vykreslovat po jednotlivých stěn{ch, protože JOGL podporuje vykreslení celých objektů pouze pro jednoduché pravidelné tvary. Pokud bychom potřebovali umístit více kv{drů, tak bychom kód pro jejich vykreslení vložili do samostatné metody, kter{ by se volala. V našem případě to však není potřeba.
4 - Implementace
27
// Vytvoření proměnných, pro vykreslení čtyř válců GLUquadric qobj1; GLUquadric qobj2; GLUquadric qobj3; GLUquadric qobj4; qobj1 = glu.gluNewQuadric(); qobj2 = glu.gluNewQuadric(); qobj3 = glu.gluNewQuadric(); qobj4 = glu.gluNewQuadric(); // pomocné proměnné pro uložení rozměrů a umístění válců float d = 0.03f; float h = 0.5f; float sirka = 0.5f; // pomocné proměnné pro uložení rozměrů a umístění desky stolu float adeska = 0.6f; float deskaup = 0.02f; float deskadown = 0.0f; // Vyresetování matice a nastavení pohledu kamery gl.glLoadIdentity(); glu.gluLookAt(-2.4, 0.60, 1.6, 0, -0.25, 0, 0, 1, 0);
Před vykreslením každého z v{lců v dané pozici musíme uložit současnou matici, kter{ ud{v{ střed a otočení souřadného systému. Kdybychom to neprovedli, tak by každ{ další transformace vych{zela z předem provedených transformací. To by zapříčinilo, že poloha v{lců by nebyla ud{na vzhledem k poč{tku souřadnic *0,0,0+, ale každý další v{lec by byl umístěn vzhledem k v{lci, který byl vykreslen před ním. Po uložení st{vající matice posuneme střed vykreslov{ní tam, kde chceme mít objekt vykreslen a otočíme o pravý uhel kolem osy x, protože v{lec by se jinak vykreslil naležato. Vykreslíme objekt a načteme uloženou matici, takže se poč{tek souřadnic vr{tí do stavu, v jakém byl před vykreslením. // Uložení matice gl.glPushMatrix(); // Upravení pozice a rotace gl.glTranslatef(sirka, 0.0f, sirka); gl.glRotatef(90.0f, 1.0f, 0.0f, 0.0f); // Vykreslení válce glu.gluCylinder(qobj1, d, d, h, 32, 1); // Načtení uložené matice gl.glPopMatrix();
Tento krok opakujeme pro každé vykreslení v{lce. Po vykreslení v{lců přistoupíme k vykreslení desky stolu. To uděl{me pomocí postupného vykreslov{ní čtyřúhelníků. Pro každý vrchol zad{me současně i koordin{ty pro texturov{ní. To n{m umožní jednotlivé stěny později pokrýt texturou. // Zapneme vykreslování čtyřúhelníků gl.glBegin(GL.GL_QUADS);
4 - Implementace
28
// Nastavení normálového vektoru dané stěny gl.glNormal3f(0.0f, 0.0f, 1.0f); // Vykreslení čtyřúhelníku gl.glTexCoord2f(0.0f, 0.0f); // souřadnice textury gl.glVertex3f(-adeska, deskadown, -adeska); // Souřadnice vrcholu čtyřúhelníku gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(adeska, deskadown, -adeska); gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(adeska, deskaup, -adeska); gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(-adeska, deskaup, -adeska); // následuje vykreslení zbylých pěti čtyřúhelníků, které zde není uvedeno. // Ukončení vykreslování čtyřúhelníků gl.glEnd();
4.2.4 Upravení vzhledu Pro upravení vzhledu objektů a realističtějšímu dojmu použijeme nastavení materi{lu a n{sledně objekty otexturujeme. Nastavení materi{lu provedeme tak, že si hodnoty připravíme do několika polí, které reprezentují barevné složky RGBA. Tyto hodnoty n{sledně aplikujeme a vytvoříme tak realističtější materi{l. // Nastavení hodnot jednotlivých typů materiálu float mat_ambient[] = { 0.4f, 0.4f, 0.4f, 1.0f }; float mat_diffuse[] = { 0.6f, 0.6f, 0.6f, 1.0f }; float mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f }; float mat_shininess[] = { 50.0f }; // Aplikace připravených hodnot gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, mat_ambient, 0); gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, mat_diffuse, 0); gl.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, mat_specular, 0); gl.glMaterialfv(GL.GL_FRONT, GL.GL_SHININESS, mat_shininess, 0); Dalším krokem je načtení textury a její n{sledné použití při vykreslení objektů. Načtení textur je v JOGL komplexní z{ležitostí a proto zde budou pops{ny jednotlivé metody, které se na načít{ní podílejí. Zavol{me vlastní metodu pro vygenerov{ní textur (pops{na níže) a pomocí metody glEnable s parametrem GL_TEXTURE_2D je nutné zapnout použití textur. Bez toho by se zobrazila pouze bíl{ krychle. public void init(GLAutoDrawable gLDrawable)
// Zapneme používání 2D textur gl.glEnable(GL.GL_TEXTURE_2D);
4 - Implementace
29
// Použijeme metodu pro vygenerování textury generateTextures(); V metodě generateTextures() nejprve zavol{me metodu glGenTextures(), které před{me jako parametr, kolik textur chceme generovat, proměnnou, do které se budou ukl{dat textury a offset textury. Pomocí GL_TEXTURE_2D definujeme, že jde o 2D texturu a pomocí glBindTexture() řekneme, se kterou texturou chceme pracovat. Pak načteme a vytvoříme texturu (viz níže). Poslední dva ř{dky definují, jaké filtrov{ní se m{ použít na texturu, pokud je menší (MIN), respektive větší (MAG) než skutečný obr{zek, který načít{me ze souboru. Filtr nemusíme použít ž{dný (GL_NONE), ale textura není na pohled hezk{ z blízka, protože přes celou obrazovku se zobrazí jen několik m{lo velkých pixelů z původního obr{zku. Další možné filtry jsou GL_NEAREST, GL_LINEAR. Chceme-li použít mipmapping, využív{me kombinací předchozích dvou filtrů, například GL_NEAREST_MIPMAP_NEAREST nebo GL_LINEAR_MIPMAP_NEAREST a podobně. Filtr GL_LINEAR je n{ročnější na výkon hardware, ale výsledn{ textura vypad{ hezky z blízka i z d{lky. Při zvětšení jsou pixely rozmaz{ny, takže nejsou vidět ostré hrany mezi pixely. private void generateTextures() { // Počet texturur, které chceme vygenerovat gl.glGenTextures(texturesCount, texture, 0); // Definujeme, že textura bude 2D a se kterou texturou chceme pracovat gl.glBindTexture(GL.GL_TEXTURE_2D, texture[0]); Texture newTexture = null; try { // Načtení textury newTexture = readTexture(textureFile); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } makeRGBTexture(gl, glu, newTexture, GL.GL_TEXTURE_2D); // Filtrování textur gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); } Metody readTexture(), readImage() a getResourceAsStream() obstar{vají spr{vné nalezení souboru s texturou buď v JAR archívu, nebo na disku a načtený
4 - Implementace
30
obr{zek předají metodě readPixels(), kter{ zprostředkuje načtení obr{zku po jednotlivých pixelech. private Texture readTexture(String filename) throws IOException { BufferedImage bufferedImage = readImage(filename); return readPixels(bufferedImage); } private BufferedImage readImage(String resourceName) throws IOException { return ImageIO.read(getResourceAsStream(resourceName)); } public InputStream getResourceAsStream(final String filename) throws IOException { // Načtení souboru z JARu InputStream stream = ClassLoader.getSystemResourceAsStream(filename); // Pokud se nezdaří, tak z disku if (stream == null) { return new FileInputStream(filename); } Else { return stream; } }
Metoda readPixels() slouží ke čtení jednotlivých pixelů z obr{zku, který je uložen jako BufferedImage. Obr{zek postupně proch{zí po jednotlivých pixelech a do ByteBuffer ukl{d{ hodnoty jednotlivých složek RGB pro každou barvu. Vrací objekt Texture. private Texture readPixels(BufferedImage img) { // Uvažujeme 3 barvy (RGB) int bytesPerPixel = 3; // Pole o velikosti textury int[] packedPixels = new int[img.getWidth() * img.getHeight()]; // Vytvoří PixelGrabber a výsledek uloží do packedPixels PixelGrabber pixelgrabber = new PixelGrabber(img, 0, 0, img.getWidth(), img.getHeight(), packedPixels, 0, img.getWidth());
4 - Implementace
31
try { pixelgrabber.grabPixels(); } catch (InterruptedException e) { throw new RuntimeException(); } // Alokace ByteBuffer pro uložení všech pixelů všech barev ByteBuffer unpackedPixels = BufferUtil.newByteBuffer(packedPixels.length * bytesPerPixel); // Postupné procházení celého obrázku a generování bodů for (int row = img.getHeight() - 1; row >= 0; row--) { for (int col = 0; col < img.getWidth(); col++) { int packedPixel = packedPixels[row * img.getWidth() + col]; unpackedPixels.put((byte) ((packedPixel >> 16) & 0xFF)); unpackedPixels.put((byte) ((packedPixel >> 8) & 0xFF)); unpackedPixels.put((byte) ((packedPixel >> 0) & 0xFF)); } } // přetočení bufferu unpackedPixels.flip(); return new Texture(unpackedPixels, img.getWidth(), img.getHeight()); } Metoda makeRGBTexture() vytv{ří texturu ve form{tu, který se použív{ při přiřazení textury na objekt. private void makeRGBTexture(GL gl, GLU glu, Texture img, int target) { gl.glTexImage2D(target, 0, GL.GL_RGB, img.getWidth(), img.getHeight(), 0, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, img.getPixels()); }
4 - Implementace
32
Priv{tní třída Texture, kter{ uchov{v{ informace o vygenerované textuře do doby, než se textura skutečně vytvoří pomocí glTexImage2D() v metodě makeRGBTexture(). Obsahuje výšku, šířku a jednotlivé pixely obr{zku. private class Texture { private ByteBuffer pixels; private int width; private int height; public Texture(ByteBuffer pixels, int width, int height) { this.height = height; this.pixels = pixels; this.width = width; } public int getHeight() { return height; } public ByteBuffer getPixels() { return pixels; } public int getWidth() { return width; } } (TICHAVA) Výslednou scénu vytvořenou pomocí JOGL vidíme na obr{zku. (Obr{zek 4)
4 - Implementace
33
Obr{zek 4
4.3 Podpora 3D modelů 3D modely reprezentují 3D objekty pomocí množiny bodů v 3D prostoru, které jsou spojeny různými geometrickými entitami, jako trojúhelníky, křivky, zvlněné plochy a podobně. Protože je 3D model množinou bodů a dalších informací, může být vytvořen ručně, pomocí algoritmu, skenov{ní, podle dat získaných měřicím přístrojem z re{lného světa nebo na z{kladě počítačové simulace. 3D modely jsou široce rozšířeny kdekoliv, kde je použív{na 3D grafika. Jejich použití dokonce předch{zelo rozšíření 3D grafiky na osobních počítačích. Mnoho her používalo předrenderované obrazy 3D modelů v době, kdy počítače neměly výkon na renderov{ní 3D scén v re{lném čase. Dnes jsou 3D modely využív{ny v mnoha oblastech. V medicíně jsou použív{ny detailní modely org{nů. Filmový průmysl je využív{ při tvorbě animovaných postav nebo objektů i v hraných filmech. Další oblasti, kde se 3D modely využívají, jsou herní průmysl, věda, architektonické n{vrhy a konstrukční modely. Protože 3D modely reprezentují objekty, které se vyskytují v re{lném světě, setk{me se s nimi v podstatě v každé oblasti lidské činnosti. V posledních letech se začaly tvořit 3D modely nejen běžných předmětů každodenního života, ale jsou použív{ny i k tvorbě modelů, které reprezentují krajinu a geologickou strukturu Země. Jedním z nejběžnějších modelů, se kterými se můžeme setkat při studiu 3D modelov{ní, je model konvice. Ten vytvořil roku 1975 Martin Newell. Všechny 3D modely mohou být rozděleny do dvou kategorií. Podobně jako v ploše lze předměty popsat buď jejich geometrií (vektorov{ grafika) nebo je naopak rozdělit na element{rní obrazové elementy (rastrov{ grafika), lze i v prostoru těleso
4 - Implementace
34
buď popsat jeho povrchem, nebo objemem rozděleným na objemové elementy. Tyto dvě metody se nazývají hraniční reprezentace a objemov{ reprezentace.
4.3.1 Hraniční reprezentace Většina současných modelovacích programů pracuje s prostorovými tělesy, jejichž geometrické vlastnosti jsou pops{ny jejich povrchem, přesněji řečeno hranicí mezi vnějším a vnitřním prostorem. Proto se tomuto způsobu popisu těles řík{ hraniční reprezentace. Tento způsob popisu těles m{ několik předností. Především relativně malé n{roky na paměť vzhledem k objemové reprezentaci a také masivní a především dostatečně levnou podporu ze strany grafických akceler{torů, které dovedou v re{lném čase vystínovat a zobrazit i velmi složitou prostorovou scénu.
4.3.2 Objemová reprezentace Popis těles objemem je metodou kvalitativně a kvantitativně odlišnou od hraniční reprezentace. Při použití této metody například neexistují, na rozdíl od dnes častěji používané hraniční reprezentace, problémy při rozhodov{ní, zda daný bod v prostoru leží uvnitř či vně tělesa. U popisu těles objemem navíc prakticky odpadají potíže při vyhodnocov{ní průchodu světla průhlednými objekty ve scéně. Při analytickém popisu se také snadno zjišťuje norm{lový vektor k povrchu tělesa v každém bodě povrchu, kter{ je v mnoha algoritmech potřebn{ pro výpočet osvětlení, viditelnosti či pro různé simulace. Objemov{ reprezentace je založen{ na objemových elementech (voxelech). Objemové reprezentace se využív{ například u počítačové tomografie a magnetické rezonance. Tato zařízení produkují trojrozměrn{ data ve formě rovinných řezů tělesem, které lze přímočarým způsobem převést na objemové elementy (TIŠNOVSKÝ). 4.3.3 Struktura 3D modelů Při vytv{ření některých 3D modelů se postupuje tak, že je model vytvořen pomocí z{kladních 3D objektů. Kombinací těchto objektů vznikne výsledný model, který ale není vyj{dřen pomocí hraniční reprezentace. Proto se provede převod, kdy je povrch vyj{dřen pomocí trojúhelníků, a jsou odstraněny nadbytečné plochy uvnitř modelu. Trojúhelníkům, které jsou použity pro vyj{dření povrchu, se řík{ polygony. U hraniční reprezentace nejčastěji vych{zíme z předpokladu, že plošky tvoří uzavřen{ tělesa. Při porušení tohoto předpokladu, například vlivem chyby při editaci tělesa, může doch{zet k různým anom{liím, a to jak při vlastní editaci, tak i při n{sledném vykreslov{ní. Jednou z největších výhod polygon{lní reprezentace je skutečnost, že algoritmy pro zobrazov{ní konvexních plošných polygonů, resp. pouze trojúhelníků, vedou k použití několika line{rních interpol{torů, které je poměrně snadné programově a především technicky realizovat. To je také důvod, proč většina dnes používaných grafických akceler{torů podporuje vykreslov{ní těles reprezentovaných polygony.
4 - Implementace
35
4.3.4 Popis vybraných 3D modelů Obj Form{t OBJ byl vyvinut společností Wavefront Technologies pro popist prostorové geometrie. Jedn{ se o otevřený form{t a byl převzat dalšími tvůrci 3D aplikací. Jedn{ se o většinou univerz{lně akceptovaný form{t. Form{t OBJ je jednoduchý a reprezentuje samotnou 3D geometrii. Ud{v{ pozici každého bodu pozici texturových koordin{tů a norm{lových vektorů. Soubory OBJ umožňují nastavit vyhlazovací parametry pro zaoblené předměty a použití materi{lů.
3ds 3DS je jedním z form{tů, který použív{ software pro 3D modelov{ní, Autodesk 3ds Max. Tento form{t byl původně hlavním form{tem podporovaným prvními verzemi softwaru Autodesk 3d studio DOS. Protože je tento form{t použív{n v oblasti 3D modelov{ní od devades{tých let, stal se v podstatě standardem při přen{šení 3D modelů mezi různými programy pro 3D modelov{ní nebo pro jejich archivaci.
Dxf Vektorový grafický form{t DXF byl navržen firmou AutoDesk, pro její pilotní a pravděpodobně i nejčastěji používanou aplikaci AutoCAD. Původně se mělo jednat o form{t, kterým by mezi sebou na souborové úrovni komunikovaly jednoduché CAD aplikace, které se v té době na počítačích PC začaly objevovat. Postupem času se však form{t DXF začal používat i v mnoha pokročilejších CAD aplikacích a modelovacích programech, a to nejenom na platformě PC. Mezitím vyspěl i samotný AutoCAD (TIŠNOVSKÝ). 4.3.5 Princip načítání 3D modelů Form{ty v sobě mohou nést řadu informací o polygonech, barv{ch, textur{ch materi{lech a podobně. Protože neexistuje ž{dný jednotný form{t ukl{d{ní 3D modelů, probíh{ načít{ní jednotlivých form{tů odlišně. Pokud chceme načítat 3D model uložený v souboru s daným form{tem musíme zn{t jeho strukturu. Díky odlišnosti form{tů musíme s každým typem form{tu pracovat jiným způsobem. Aby program{tor nemusel složitě vytv{řet vlastní metody pro načít{ní 3D modelů, využív{ se takzvaných loaderů. Každý loader slouží k načít{ní určitých typů form{tů a usnadňuje tím program{torům pr{ci. S 3D modelem, který je nahr{n pomocí loaderu potom může program{tor pracovat jako s každým jiným objektem, který m{ na scéně. Mezi Java3D a JOGL jsou z{sadní rozdíly, ale načít{ní modelů probíh{ podobně. Je to způsobeno tím, že program{tor nenačít{ modely s{m, ale využív{ k tomu pr{vě připravené loadery. Protože je načít{ní modelů podobné, uvedeme zde pouze obecný postup, kterým se modely načítají. Konkrétní implementace se pro každý loader bude lišit. Abychom mohli cokoliv vykreslit na obrazovku, musíme vytvořit třídu pro vykreslov{ní. V této třídě vytvoříme objekt typu daného loaderu. Samotný objekt loaderu k ničemu není, a proto použijeme metodu pro načtení modelu. Tato metoda
4 - Implementace
36
načte n{mi zadaný model a uloží ho do proměnné. Pomocí této proměnné můžeme k modelu přistupovat. Je možné, že model bude uložen jako hierarchicky uspoř{daný strom menších modelů. Pokud je tomu tak, m{me možnost s jednotlivými č{stmi modelu pracovat samostatně a pomocí algoritmů vytvořit animaci modelu. Tento přístup může být použit například pro vytvoření pohybujícího se hmyzu, kdy jednotlivé č{sti modelu budeme vykreslovat s různou rotací a posunem. To může být využito zejména v Java3D, kde se s hierarchickým uspoř{d{ním objektů na scéně počít{. Jak bylo řečeno výše, každý form{t modelu a každý loader je jiný. Proto je nutné se sezn{mit s konkrétní dokumentací dříve, než přistoupíme k načít{ní 3D modelů. Mezi form{ty, které lze pomocí leaderů načíst patří například 3DS, AC3D, ASE, COLLADA, LWO, MD2, MD3, MD5, MS3D, OBJ/MT, VRML, DXF a další. Lze tedy říci, že podpora importu 3D modelů je na dobré úrovni.
5 - Závěr
37
5 Závěr Teoretick{ č{st uk{zala, že při tvorbě 3D scény pomocí jazyka Java, m{me k dispozici dva přístupy. První možností je využív{ní takového n{stroje, který ctí objektový přístup. To se hodí, pokud program{tor s tvorbou 3D scén začín{ a z{roveň m{ zkušenosti s jazykem Java. Druhou možností je využití takového rozhraní, které pouze přebír{ metody pro pr{ci s 3D scénou z jiného programovacího jazyka. To je vhodné, pokud m{ program{tor zkušenosti s jiným jazykem, ale potřebuje, aby jeho program byl naps{n v jazyce Java. Obě metody byly demonstrov{ny v č{sti implementace. Při tvoření scény se stolem, pomocí Java3D a JOGL, jsme postupovali stejně. Vytvořili jsme plochu, kam jsme scénu vykreslili, a n{sledně vytvořili světla a otexturovaný povrch objektů. Zejména při implementaci metod, pro potažení objektů texturami, pomocí JOGL, se uk{zal hlavní rozdíl. Metody, které jsou v Java3D již naps{ny, jsme si museli vytvořit sami. Podpora načít{ní 3D modelů pomocí Javy, je u obou přístupů na dobré úrovni a není třeba se uchylovat k Java3D nebo JOGL jen proto, že potřebujeme pracovat s 3D modely. Pr{ce uk{zala, že pomocí Javy jsme schopni vytvořit kvalitní 3D scénu. V ž{dném případě se ale nejedn{ o podrobný n{vod, popisující celou problematiku. Pr{vě naopak. Cílem bylo uk{zat program{torům klady a z{pory jednotlivých možností. Toho bylo dosaženo. Pokud bychom chtěli pr{ci d{le rozšířit, mohli bychom se podrobněji zabývat jedním z přístupů, popsat jej do větší hloubky a demonstrovat jeho možnosti na komplexním příkladu, který by například využíval modely, animace a podobně.
6 - Literatura
38
6 Literatura Sun Microsystems, Inc. Java3D API [online]. http://java.sun.com/javase/technologies/desktop/java3d/.
2009.
URL:
Sun Microsystems, Inc. Java3D API Tutorial. [online]. 2000. URL: http://java.sun.com/ developer/ onlineTraining/java3d/. Java3D.org. Java3D pro http://www.java3d.org/czech.html
zač{tečníky.
[online].
2009.
URL:
Programujte. Seri{l Programov{ní v OpenGL. [online]. 2010. URL: http://programujte.com/?rubrika=26&sekce=84&kategorie=329&subkategorie=332. KAI, Ruhl. JOGL (Java OpenGL) Tutorial. [online]. 2009. URL: http://www.land-ofkain.de/docs/jogl/. TICHAVA, Jan. OpenGL v Javě. [online]. 2008. URL: http://jogl.tichava.cz/javaopengl.php TUREK, Michal. NeHe OpenGL Tutori{ly. [online]. 2008. URL: http://nehe.ceskehry.cz/tut_obsah.php DALTON, Filho, 3D Model Interaction with Java3D. http://java.dzone.com/news/3d-model-interaction-java-3d. JOUVIE, Jérôme. OpenGL. [online]. Forum/index.php?category=2. J3D.ORG. File Loader utilities/loaders.html.
Archives.
2000.
[online].
URL: 2006.
[online].
2008.
URL:
http://jerome.jouvie.free.fr/ URL:
http://java3d.j3d.org/
TIŠNOVSKÝ, Pavel. Zobrazení objemových dat v POV-Rayi. [online]. 2000. URL: http://www.root.cz/clanky/zobrazeni-objemovych-dat-v-pov-rayi/. TIŠNOVSKÝ, Pavel. Vektorový grafický form{t DXF. http://www.root.cz/clanky/vektorovy-graficky-format-dxf/.
[online].
2000.
URL:
KUŽELKA, O. Java a 3D grafika. [online]. 2004. URL: http://interval.cz/serialy/java-a3d-grafika/. SELMAN, D. Java3D Programming. [online]. 2002. URL: http://www.tecgraf.pucrio.br/~ismael/Cursos/Cidade_CG/labs/Java3D/Java3D_onlinebook_selman/onlinebo ok.html. Project Kenai. Java™ Binding for the OpenGL® API Wiki. [online]. 2009. URL: http://kenai.com/projects/jogl/pages/Home