Miskolci Egyetem Gépészmérnöki és Informatikai Kar Általános Informatikai Tanszék
Modern Grafikus effektek OpenGL környezetben Diplomamunka (GEIAL-2/2016/Msc) Név: Vereckei Csanád Neptunkód: O7OTD4 Cím: 3528 Miskolc, Takta utca 1. Szak: Ménrök Informatikus Msc Alkalmazásfejlesztő szakirány
Modern grafikus effektek OpenGL környezetben
Tartalomjegyzék
Tartalom 1.
Bevezető ..................................................................................................................................... 4
2.
Irodalom feldolgozás .................................................................................................................. 6
3.
Rendelkezésre álló technikák, technológiák ............................................................................ 10 3.1.
3.1.1.
A C nyelv................................................................................................................... 11
3.1.2.
A C++ nyelv............................................................................................................... 12
3.1.3.
A C# nyelv................................................................................................................. 13
3.2.
Grafikus segédkönyvtárak ............................................................................................... 14
3.2.1.
OpenGL..................................................................................................................... 14
3.2.2.
Direct3D ................................................................................................................. 16
3.3.
Fejlesztőkörnyezetek ....................................................................................................... 18
3.3.1.
Code::Blocks............................................................................................................. 18
3.3.2.
Dev-C++ .................................................................................................................... 19
3.3.3.
Visual C++ ................................................................................................................. 20
3.4.
Modellező eszközök ........................................................................................................ 21
3.4.1.
Autodesk Maya ........................................................................................................ 21
3.4.2.
Blender ..................................................................................................................... 22
3.5.
Platformfüggetlen segédkönyvtárak .............................................................................. 24
3.5.1.
SDL, SDL2 .................................................................................................................. 24
3.5.2.
GLUT ......................................................................................................................... 25
3.5.3.
GLFW ........................................................................................................................ 26
3.6.
4.
Programozási nyelvek...................................................................................................... 11
Grafikus interfészek ......................................................................................................... 27
3.6.1.
CEGUI........................................................................................................................ 27
3.6.2.
AntTweakbar ........................................................................................................... 28
Felhasznált technikák, technológiák ................................................................................ 29 4.1.
Programozási nyelv ......................................................................................................... 29
4.2.
Grafikus könyvtár ............................................................................................................ 31
4.3.
Fejlesztőkörnyezet ........................................................................................................... 33
4.4.
Modellezőeszköz ............................................................................................................. 35
4.5.
Segédkönyvtárak ............................................................................................................. 36
4.6.
Grafikus interfész ............................................................................................................. 38 1
Modern grafikus effektek OpenGL környezetben 4.7. 5.
Egyéb programok ............................................................................................................. 39
A feladatok megoldása ............................................................................................................. 40 5.1.
SDL2-t használó cross-platform alkalmazás alapjai........................................................ 41
5.2.
OpenGL immediate mód ................................................................................................. 42
5.3.
Buffer objektumok használata ........................................................................................ 43
5.4.
Modellbetöltő segédosztály ............................................................................................ 44
5.5.
CameraManager osztály .................................................................................................. 46
5.6.
GLSL alapok ...................................................................................................................... 47
5.7.
Irányított fény (Nap) ........................................................................................................ 49
5.8.
Pontszerű fényforrások (lámpák).................................................................................... 51
5.9.
Spotlight (reflektor) ........................................................................................................... 53
5.10.
Specular mapping (tükröződési térkép)...................................................................... 55
5.11.
Normal mapping (felületnormálisok térképe)............................................................ 57
5.12.
Parallax mapping (látószög elhajlási térkép) .............................................................. 61
5.13.
A végső alkalmazás rövid ismertetése ............................................................................... 69
6.
Összegzés ................................................................................................................................. 70
7.
Summary .................................................................................................................................. 73
8.
Irodalomjegyzék ....................................................................................................................... 75
2
Modern grafikus effektek OpenGL környezetben
EREDETISÉGI NYILATKOZAT
Alulírott ……………………………………………………….; Neptun-kód:………………… a Miskolci Egyetem Gépészmérnöki és Informatikai Karának végzős ……………. szakos hallgatója ezennel büntetőjogi és fegyelmi felelősségem tudatában nyilatkozom és aláírásommal igazolom, hogy ……………………………………………………………………………………………… című szakdolgozatom/diplomatervem saját, önálló munkám; az abban hivatkozott szakirodalom felhasználása a forráskezelés szabályai szerint történt. Tudomásul veszem, hogy szakdolgozat esetén plágiumnak számít: szószerinti idézet közlése idézőjel és hivatkozás megjelölése nélkül; tartalmi idézet hivatkozás megjelölése nélkül; más publikált gondolatainak saját gondolatként való feltüntetése. Alulírott kijelentem, hogy a plágium fogalmát megismertem, és tudomásul veszem, hogy -
plágium esetén szakdolgozatom visszautasításra kerül.
Miskolc,.............év ………………..hó ………..nap
…….……………………………….… Hallgató
3
Modern grafikus effektek OpenGL környezetben
1.Bevezető A diplomamunkámhoz az alapötlet, miszerint valamilyen háromdimenziós alkalmazást, esetleg egy konkrét játékot szeretnék csinálni már a mesterképzésem elkezdésekor világos volt számomra. Annak idején azért választottam az informatikai képzést, mert mindig is számítógépes játékokkal szerettem volna foglalkozni. Sajnos a Bsc képzésem alatt nem igazán jutott idő rá, hogy komolyabban belevethessem magamat a témába, hiszen az alapképzés jelentős része azzal telt, hogy addig még nem ismert technológiákat kellett elsajátítanunk. A diplomám megszerzése után egy évet kihagytam tanulmányaimból, elmentem dolgozni, hogy komolyabb, valódi tapasztalatokat szerezhessek. Ez idő alatt szabadidőm jelentős részét kétdimenziós játékkezdemények fejlesztésével töltöttem. Írtam egy egyszerű platformer alkalmazást Javaban, egy felülnézetes, űrhajós játékot, szintén Javaban, valamint, mivel akkoriban webfejlesztőként dolgoztam és abban az időben kezdett teret hódítani a HTML5, az új lehetőségeket kipróbálandó csináltam egy szintén felülnézetes kalandjátékot, mely a HTML5 canvas elemét és Javascriptet használt. Mindezek a hobbiszintű, be nem fejezett fejlesztések tökéletesek voltak arra, hogy a grafikus programozás alapjait megtanulhassam, átlássam, hogyan működnek a legalapvetőbb dolgok, mint a sprite alapú animációk vagy a tileset-ek, illetve, hogy alapvető játéklogikákkal is megismerkedjek, mint az ütközésérzékelés vagy például egyszerűbb mesterséges intelligenciák alkalmazása. Az Msc képzésem második félévében aztán kapcsolatba léptem konzulensemmel, Dr. Mileff Péterrel, akivel megbeszéltük, hogy szeretnék megismerkedni a háromdimenziós grafikus alkalmazások alapjaival is. Elsősorban az ő tanácsára választottam végül az OpenGL-t és nem pedig a DirectX-et. Azt, hogy konkrétan mit is kellene megvalósítani, mi lehetne a diplomamunkám témája ekkor még mindig nem tudtam, de köszönhetően az ő iránymutatásainak sikerült egy-két modernnek nevezhető grafikus megoldást is elsajátítanom és megvalósítanom a programomban, melyek lényegében mind a fény modellezésének témakörébe sorolhatóak. Ennek megfelelően választottam meg végül a témát, mely a „Modern grafikus effektek OpenGL környezetben” címet kapta. A képzésem utolsó félévének elején így már a kitűzött célok is többé-kevésbé tiszták voltak. Azt szerettem volna elérni, hogy a lehető legmodernebb grafikus effektek közül ismerhessek meg és implementálhassak párat amellett, hogy egy, a későbbiekben is használható, lehetőleg platform független alap alkalmazást, egy úgynevezett game engine-t hozzak létre. Ezen célok elérése mellett célom volt az is, hogy az egyetemi tanulmányaim során sajnos meg nem ismert C++ programozási nyelvet is minél jobban elsajátíthassam, mivel, ha az egyetem után is a játékiparban szeretnék dolgozni, mindenképpen
4
Modern grafikus effektek OpenGL környezetben
szükségem lesz rá. Bár a munkám során túl mélyen nem sikerült beleásnom magam a C++ témakörébe, az alapjait úgy érzem sikerült jól elsajátítanom. A programommal szemben támasztott elvárások elsősorban a megvalósított effektek minőségére vonatkoztak, hiszen azoknak nem csak látványosnak és működőképesnek, de lehetőleg még optimalizáltnak is kellett lenniük egyrészt azért, hogy a demo lehetőleg már gyengébb gépeken is futhasson, másrészt azért, hogy az optimalizálási feladatokkal is megismerkedhessek, mivel a későbbiekben, az iparban ezekre igen nagy hangsúlyt fektetnek. A platformfüggetlenség is elvárás volt, egyrészt, mivel én a diplomamunkámat Windows alatt írtam, de konzulensem Linux felhasználó, másrészt, mert bár a valódi játékfejlesztések során nem minden esetben fektetnek rá hangsúlyt (sok az egyetlen célplatformot támogat játék a piacon), én szerettem volna az ebben rejlő lehetőségekkel és buktatókkal is megismerkedni. Annak érdekében, hogy a minta alkalmazás ne csak egy beégetett demo környezet megjelenítésére legyen képes, egy modellbetöltőt is implementálnom kellett, amely .obj fájlok beolvasására, illetve az azokban tárolt adatok kiolvasására és azok kirajzoláshoz való előkészítésére alkalmas. Használhattam volna erre a célra már meglévő, ingyenes osztályokat, könyvtárakat, de minél inkább szerettem volna megérteni a folyamat legapróbb lépéseit is, ezért és mert ennek az implementálása nem igényel túlzottan sok időt, úgy döntöttem, hogy ezt magamnak szeretném leprogramozni. Végül elvárás volt az is, hogy a diplomamunkám védésekor bemutatott demo a lehető leginkább felhasználóbarát legyen, ezért egy egyszerű GUI-t is be kellett építenem a programba, amely segítségével könnyen és egyértelműen lehet az implementált effekteket váltogatni vagy például a fényforrásokat manipulálni. Sajnos ez már a fejlesztési folyamat és a félév végéhez közeledve merült fel, így főleg az időhiánya miatt ezt már nem én implementáltam, hanem kerestem egy ingyenesen hozzáférhető segédkönyvtárat.
5
Modern grafikus effektek OpenGL környezetben
2.Irodalom feldolgozás A videojátékokban használt technikák egy igen gyorsan fejlődő iparág részei, ahol minden évben megfigyelhetőek kisebb-nagyobb előrelépések. Ez a fejlődési folyamat egyaránt magában foglalja újabb és újabb grafikus eljárások megszületését, illetve a meglévők folyamatos fejlesztését, finomhangolását. Mivel ez azt jelenti, hogy jelentős mennyiségű grafikus effekt közül kellett választanom, hiszen a határidők betartása végett csak pár technikát tudtam megvalósítani, fontos volt, hogy tisztában legyek ezek eredetével és életútjukkal. Mint az a dolgozatom későbbi fejezeteiből is ki fog derülni, figyelmemet elsősorban a fények szimulációjára, manipulálására fordítottam. Ennek megfelelően a munkám elején utánajártam, miként fejlődtek az évek során a 3D-s világokban megjelenő fényforrások, illetve néhány „mapping” eljárás. A 3D-s játékok első megjelenése nagyon korlátozott volt a rendelkezésre álló hardver miatt. Egyáltalán nem, vagy csak nagyon korlátozott formában beszélhettünk fény szimulációról, a felépített világ minden egyes eleme, objektuma sokszor ugyanolyan fényviszonyok mellett volt látható. Ez ugyan jelentősen kisebb erőforrás igényeket jelentett, azonban az így kapott végeredmény egyáltalán nem volt valósághű, hiszen többek között nem számított, az egyes tárgyak milyen messze vannak egy adott fényforrástól vagy, hogy takarásban vannak-e. A kirajzolt modellek végső színe csupán attól függött, a hozzájuk kötött textúrában milyen értékek voltak megadva.
1. ábra: A Wolfenstein 3D játék képernyőképe, amely az első FPS játékként van számon tartva. Jól látható a fény szimulációk hiánya. A játékobjektumok nem valódi 3D formák, hanem a kamera irányába fordított 2D-s elemek. 6
Modern grafikus effektek OpenGL környezetben
A hardverek gyors fejlődésének köszönhetően azonban meglehetősen hamar lehetőség nyílt rá, hogy ne csak ráilleszthessünk egy textúrát egy adott felületre, hanem arra is, hogy az így kapott szín értékeket a kirajzolás előtt módosítsuk. Ezzel elérhetővé vált, hogy egy objektum egy bizonyos pontjának a színe ne csak a textúra fájlban tárolt értékektől függjön, hanem egyéb tényezőktől is, mint például egy fényforrástól való távolsága, vagy az azzal bezárt szöge. Ezen eljárások alapját a vektorszámítások képezték, amelyek segítségével meghatározhatóak az említett módosító tényezők. Természetesen ez idő tájban, az 1990-es évek elején még messze nem voltak olyan teljesítményű hardverek, mint manapság, így az elért eredmények jóval durvábbak, „csúnyábbak” voltak a modern megfelelőiknél. Mégis, ezen időszaknak köszönhető, hogy létrejött a ma is használatos három fényforrás, a directional, a point- és a spotlight, valamint, hogy olyan modelleket vezessenek be a játékok világába, mint a Phong féle világítási modell.
2. ábra: A Doom (1993) grafikája. Látható, hogy, bár durva felbontású a számítás, a fényviszonyok több tényező függvényében változnak a kirajzolt világon belül.
A fény szimulációk folyamatos fejlődésének köszönhetően olyan technikák is napvilágot láttak, mint például a statikus, vagy a későbbiekben már dinamikus árnyékok vagy az olyan felületek, objektumok, amik bizonyos alapelvek szerint áteresztik a fény egy részét vagy egészét, miközben valamilyen módon megváltoztatják annak egyes tulajdonságait. A fények mellett nagy hangsúlyt kaptak még a lehető legaprólékosabb részletek minél valósághűbb megjelenítése is. Kezdetben ez azt jelentette, hogy a hardverek képességeit maximálisan kihasználva igyekeztek a lehető legtöbb poligont kirajzolni és így valóban részletesebb modelleket megjeleníteni. Természetesen ez a 7
Modern grafikus effektek OpenGL környezetben
megközelítés egy meglehetősen meredeken növekedő teljesítmény igényt jelentett, amely mögött egy idő után kezdett lemaradni az elérhető eszközök teljesítménye. Emiatt új eljárások kidolgozására volt szükség, melyek segítségével anélkül növelhető a modellek látszólagos részletessége, hogy az azokat alkotó csúcspontok számát nem kell olyan nagy mértékben többszörözni. Ezen megoldások elsősorban a shaderek megjelenésének köszönhetőek, amik segítségével a programozók sokkal nagyobb kontrollt kaptak afelett, hogy az egyes csúcspontok és akár képpontok milyen módon legyenek feldolgozva, módosítva. Az egyik ilyen eljárás, amelyet én is megvalósítottam, az 1998-ban publikált normal mapping volt. Az eljárás lényege, hogy egy textúra segítségével egy felületnek nem csak egyetlen normális vektort adunk, hanem a textúra felbontásának megfelelően többet is, amelyek segítségével elérhető, hogy a beérkező fény az egyes pontokról más-más irányba verődjön vissza, ezzel keltve a részletesség illúzióját.
3. ábra: Egy képernyőkép a Crysis nevű játékból (2007). Az első játékok egyike volt, amely steap parallax mappinget és normal mappinget is használt, akkoriban sosem látott részletességet elérve ezzel.
A másik technológia a parallax mapping, mely az előző eljárás egy továbbfejlesztett változata, amit 2001-ben publikált Tomomichi Kaneko és, amely annyival nyújt többet, hogy nem csak a beérkező fényekkel való számításokat, de a megjelenítendő textúrák koordinátáit is módosítja. Ezzel elérhető, hogy míg a normal mapping esetén a felületre kis szögben tekintve könnyen érzékelhető, hogy az valójában sík, addig ezzel az eljárással az valóban egy aprólékosan kidolgozott, „göröngyös” felszín képét mutatja.
8
Modern grafikus effektek OpenGL környezetben
A shader programozás segítségével az általam körüljárt eljárásokon felül rengeteg egyéb effekt megvalósítására van lehetőség, mint például a cell shading, amely olyan, nem fotorealisztikus renderelési eljárás, ami igyekszik a részletességet elfedni, ezáltal rajzfilm szerű színeket, színátmeneteket generál vagy a bevésés shading, amellyel egy textúrát felhasználva egy faragott felszín látszata hozható létre. Ezeket, az elsősorban nem a valósághű kinézetet szem előtt tartó eljárásokat főleg azon független fejlesztők szokták alkalmazni, akiknek sem a hardveres háttér, sem az emberi erőforrások nem állnak rendelkezésükre, hogy aprólékos grafikával ruházzák fel a programjukat és/vagy a művészi megnyilvánulás részét képezik az egyedi effektek. Mégis, én igyekeztem olyan effekteket kiválasztani a munkám során, melyek mára széles körben elterjedtek, jól dokumentáltak és kellőképpen kiforrottak. Mivel ezeket a modern játékok túlnyomó többségében, szinte mindegyikében használják valamilyen formában, lehetőségem volt rá, hogy sok példán keresztül tapasztaljam meg, milyen eredmények elérését kell célomul kitűznöm, valamint, hogy milyen fejlődésen mentek keresztül első megjelenésük óta.
9
Modern grafikus effektek OpenGL környezetben
3.Rendelkezésre álló technikák, technológiák Az elmúlt években jelentősen megnőtt a kereslet a játékfejlesztést segítő fejlesztőeszközökre, segédkönyvtárakra és kiegészítő alkalmazásokra. Köszönhető ez többek között az úgynevezett crowd funding vagy startup vállalkozások és ezzel a független, indie játékfejlesztők számának rohamos növekedésének, valamint annak, hogy manapság egyre több eszköz van a piacon, amely alkalmas rá, hogy játékokat futtassanak rajtuk. Mivel mindezen okok miatt mondhatjuk, hogy egyre nagyobb piaca van a digitális játékoknak, egyre többen adják játékfejlesztésre a fejüket, ám emiatt egyre több az olyan fejlesztő, aki csak minimálisan ért konkrétan játékok fejlesztéséhez így egyre nagyobb az igény olyan eszközökre, technológiákra, melyek nagyban megkönnyítik ezt a folyamatot. Ezen igényeket kielégítendő mára bőven találni alternatívákat mind SDK-k (Software Development Kit), IDE-k (Integrated Development Environment) terén, mind a szabadon felhasználható, mások által közzétett segédkönyvtárak terén. A fejlesztés során tehát célszerű körültekintően szétnézni, hogy mindenki megtalálja a maga számár legmegfelelőbbet. A következőkben a teljesség igénye nélkül szeretném bemutatni azokat az alternatívákat, amik szóba jöttek vagy azért, mert nagyon sokan használják őket, nagy a támogatottságuk vagy mert valaki ajánlotta őket. Természetesen az itt felsoroltak mellett nagyon sok egyéb lehetőséget talál az, akinek ezek közül egyik sem nyeri el a tetszését és hajlandó időt fordítani arra, hogy megtalálja az igazán neki valót, de szerintem ezek az opciók azok, amik a legnépszerűbbeknek, leghatékonyabbnak mondhatóak, ezáltal bizonyos szempontok alapján a legjobb választást jelentik.
10
Modern grafikus effektek OpenGL környezetben
3.1.
Programozási nyelvek
A legelső feladat az volt, hogy eldöntsem, melyik programozási nyelvet szeretném használni a fejlesztés alatt. Már a kezdetektől egyértelmű volt, hogy lényegében csak három alternatíva jöhet szóba, a C, a C++, valamint a C#. Ugyan a legelterjedtebb, legmodernebb nyelvek közé tartozik a Java is, több okból kifolyólag fel sem merült ennek használata, melyeket röviden úgy lehetne összefoglalni, hogy az Android alkalmazások kivételével a játékfejlesztésekhez használt játékmotorok, könyvtárak és általánosságban minden a C nyelvek valamelyikét támogatja, illetve azok a szempontok, amik kiemelkedővé teszik a Javat az üzleti szektorban, éppen hátrányként foghatóak fel egy játék fejlesztésekor. Ezzel szemben a C és az arra épülő két másik nyelv kifejezetten alkalmasak teljesítmény igényes programok fejlesztésére, mivel többé-kevésbé gépközeli nyelvekről van szó, amikkel nagyon hatékonyan lehet többek között a memóriát menedzselni.
3.1.1. A C nyelv A C egy általános célú, strukturált programozást és rekurziót támogató procedurális programozási nyelv, azaz a program által végrehajtandó feladatot a programozó lépésről lépésre implementálja, minden fázisban egy-egy kisebb feladatot kiadva a gépnek, mint memória területek foglalása, értékadások vagy értékegyezőségek vizsgálata. Low-level hozzáférést biztosít a memóriához, ezáltal jóval nagyobb kontrollt ad a rendszer felett, valamint, mivel így egy gépközeli nyelvről van szó, olyan instrukciókkal dolgozik, melyeket a gép könnyen és gyorsan tud végrehajtani. Mivel nagyon hatékonyan kezeli a tipikusnak mondható gépi instrukciókat, gyorsan elterjedt, mint a basic nyelv egy alternatívája és mára sok területen át is vette a helyét, így például operációs rendszerek fejlesztésénél vagy beágyazott rendszerek rutinjainál. 1969 és 73 között fejlesztette Dennis Ritchie a Linux alá és mára az egyik legelterjedtebb programozási nyelvvé vált, mivel majdnem minden operációs rendszer és architektúra rendelkezik C fordítóval. Ennek köszönhetően platformfüggetlen alkalmazásokat is viszonylag könnyen lehet készíteni, csupán egy-két változtatásra lehet szükség a kódban, ha egy másik rendszeren szeretnénk futtatni. Mind az ANSI (American National Standards Institute), mind az ISO (International Organization for Standardization) szabványosította megjelenése óta. Legnagyobb hátrányaként talán azt lehet megemlíteni, hogy egy kifejezetten régi programozási nyelv, így manapság már elavultnak minősíthető, főleg olyan összetett alkalmazások esetén, mint a játékok. Ezeknél a programoknál ugyanis kiemelten fontos az objektum orientált programozás paradigmája, melyet a C egyáltalán nem támogat. Manapság főként beágyazott rendszerek programozására használják. 11
Modern grafikus effektek OpenGL környezetben
Sok modern programozási nyelvnek szolgált alapul. Merített ötleteket belőle többek között a Java is. [1], [2]
3.1.2. A C++ nyelv Szintén általános célú, magas szintű programozási nyelv, amely már támogatja az objektum orientált, a procedurális és generikus programozást, valamint az adatabsztrakciókat is. Igyekszik megtartani a C nyelv előnyeit, de ki is egészíti azokat, hogy a programozás gördülékenyebben mehessen, könnyebb legyen újrahasznosítható komponenseket készíteni és karbantartani a kódot. A tervezésénél előtérbe helyezett koncepció, miszerint beágyazott, korlátozott erőforrásokkal rendelkező, esetleg nagy rendszerek programozására szánták, melyeknél kritikus szempont a hatékonyság, kiválóan alkalmassá teszik játékok fejlesztésére. Mivel a technológia fejlődésével egyre több féle hardver jelenik meg és így egyre szélesebb skálán mozog az architektúrák teljesítménye, fontos, hogy a lehető legjobban optimalizált kódot lehessen elkészíteni, mely lehetőleg minél több konfiguráción képes futni. A C++ is az ISO szabvány alá tartozik. Fejlesztése 1979 óta a mai napig is tart és sok modern programozási nyelvnek szolgált alapul, mint például a Java vagy a C#, de a C modernebb (1988 utáni) verziói is merítettek belőle ötleteket. Megalkotója Bjarne Stroustrup volt, aki egy olyan gyors és hatékony nyelvet akart, mint a C, de magasabb szintűt, ezért kiegészítette azt többek között osztályokkal, származtatással és erős típusokkal. Az első kereskedelmi forgalomban is elérhető verziója 1985-ben jelent meg. Legutolsó verziója a 2014-es C++14, de már most is sok információ található az interneten a 2017-es verzióval kapcsolatban. A C++ filozófiájának két pontját emelném ki, melyek miatt különösen jól használható játékfejlesztésre. Ezek szerint a nyelvnek biztosítania kell a programok jól definiált egységekbe szervezését, melyeket külön-külön lehet fejleszteni, aztán a fejlesztés bizonyos fázisaiban egyet alkalmazássá gyúrni azokat, valamint együtt kell tudjon működni más nyelvekkel. Mivel egy játékszoftver általában meglehetősen sok kisebb komponensből áll, valamint sokszor használ külső könyvtárakat, erőforrásokat, fontos, hogy egy olyan nyelvet használjunk, ami képes könnyedén kielégíteni ezeket az igényeket. [3],[4]
12
Modern grafikus effektek OpenGL környezetben
3.1.3. A C# nyelv Szintén egy általános célú nyelvről van szó, amely már teljesen objektum orientált. A három közül ez nevezhető a legmodernebbnek, mivel olyan paradigmákat foglal magában, mint az erősen típusosság, a generikus, a funkcionális és a komponens orientált programozás. Általában, ha C#-ról beszélünk, a Microsoft Visual C#-ot értjük alatta, amely nyílt forrású. Természetesen egyéb implementációk is léteznek, mint például a Mono vagy a DotGNU. A .NET framework fejlesztése során merült fel az igény egy új, C alapú nyelv létrehozására, mely objektum orientált, így született meg a Cool (C-like Object Oriented Language). A később C#-ra keresztelt nyelvet 2000-ben mutatták be és sok kritika érte, mivel akkoriban még sokan hasonlították a Java-hoz. Ám a C# 2.0 óta a két nyelv egyre inkább eltérő jelleget mutat. Az évek során fejlesztés közben törekedtek arra, hogy egyensúlyt tartsanak a programozó nyelvi szabadsága és a programozási folyamat egyszerűsége között. A C# kódok hordozhatóságát tekintve a nyelv specifikációjában nincs utalás arra, hogy milyen elvárásokat kellene a fordítónak teljesíteni a kódgenerálás során. Maga a nyelv erősen támaszkodik az alatta fekvő CLI-re, azaz a Microsoft által jegyzett Common Language Infrastructure-re, így elméletben akár olyan gépi kódot is lehet generálni, mint egy hagyományos C++ fordító segítségével. Sajnos azonban maga a nyelv erősen támaszkodik a .NET keretrendszerre, amelynek portolása egyéb operációs rendszerekre még mindig elég kezdetlegesnek mondható, így sosem terjedt el, mint platformfüggetlen programozási nyelv. A Javaban megismert csomagok által megvalósított izolációhoz nagyban hasonlító megoldást nyújtanak a C++ -ból is ismert namespace-ek, melyek olyan kényelmi funkciókat tesznek elérhetővé, mint például annak a lehetősége, hogy különböző namespace-ek alatt ugyanazon osztályneveket használhassuk. Még egy fontos tulajdonsága, amely alkalmassá teszi játékok programozására, hogy objektum orientált programozási nyelv lévén saját szemétgyűjtővel rendelkezik, ami felügyeli a már nem használatos objektumok memóriából való eltávolítását, ezzel is jelentős terhet véve le a programozó válláról, hiszen, a Javahoz hasonlóan felügyeli az allokált memóriaterületeket és amennyiben egy adott részre nincs további hivatkozás a programban, felszabadítja azt. Ezáltal segít elkerülni az úgynevezett memory leak-et, amely során a már használaton kívüli, de fel nem szabadított memóriaterületek eltelítik a rendszer teljes memóriáját. [5]
13
Modern grafikus effektek OpenGL környezetben
3.2.
Grafikus segédkönyvtárak
Mivel egy háromdimenziós grafikus alkalmazás elkészítése volt a célom, szükségem volt grafikus könyvtárra is. Ezen a téren nem volt sok választási lehetőségem, mivel csak két alternatíva jöhetett szóba, az OpenGL és a DirectX (annak is egy korábbi verziója, mint a 10-es vagy akár a 9-es). Természetesen nem csak ez a két könyvtár létezik, rengeteg más alternatívát lehet találni az interneten, mint a Vulkan, a Mantle vagy éppen az Apple sajátja, a Metal, de játékokhoz elsősorban ezeket, vagy ezek alapelveire épülő megoldásokat használnak, ezért egyértelmű volt, hogy a kettő valamelyikével kell megismerkednem.
3.2.1. OpenGL Az OpenGL egy platform és nyelv független API kétdimenziós és háromdimenziós vektorgrafikus feladatok elvégzéséhez. Feladata elsősorban az, hogy kommunikáljon a GPU-val (Graphics Processing Unit), ezáltal hardveresen támogatott és gyorsított megjelenítést tesz lehetővé. Ezeknek köszönhetően főként a CAD rendszereknél, játékoknál, virtuális valóság rendszereknél szoktál használni, ahol fontos a lehető legjobb minőségű megjelenítés és a teljesítmény optimalizációja. 1991-ben kezdték meg fejlesztését a Silicon Graphics-nál és 1992-ben jelent meg az 1.0-ás verziója, míg a legfrissebb verzió a 2014-es 4.5. Napjainkban a Khronos Group menedzseli a projektet. Fejlesztése aktívan zajlik, egyre újabb és újabb lehetőségeket adva az API-hoz. Ezen felül a hardvergyártók is szoktak úgynevezett extension-öket (kiegészítéseket) szállítani a GPU-k mellé, amelyek új funkcionalitást, esetleg új konstansokat mutatnak be, melyek segítségével segítik az OpenGL alkalmazások optimalizálását az adott hardveren. (Az összes ilyen kiegészítés megtalálható az OpenGL Registry-ben.) Specifikációjának megfelelően egy absztrakt API-val rendelkezik, mely lehet teljes egészében szoftveresen implementálva, mégis az a jellemző, hogy nagy részben vagy teljesen hardveres a megvalósítása. Bár az OpenGL függvényhívások a C nyelv szintaktikáját követik, sok programozási nyelvhez található mögötte megvalósítás, így ugyanazon függvényhívás értelmezve van többek között C++, Java vagy C# nyelveken is. A felhasznált vertex koordináták, transzformációk és textúrák által elkészítendő képet csővezeték szerű algoritmussal állítja elő, melynek jól meghatározott fázisai vannak. Ezen fázisok között vannak, melyek elhagyhatóak a renderelési folyamat során, továbbá, a GLSL shader programok segítségével bizonyos részei programozhatóak.
14
Modern grafikus effektek OpenGL környezetben
4. ábra: Programozható grafikus csővezeték (forrás: https://www.inf.uszeged.hu/~vargalg/oktatas/fgraf_14t/02_OpenGL_csovez.pdf)
Egyik nagy hátránya, hogy lényegében csak a megjelenítési folyamatokat tekintik feladatául, így az azokhoz szükséges kontextusok, valamint a perifériás eszközök kezelését a mögötte fekvő operációs rendszerre, illetve az alkalmazás programozójára bízzák. Népszerűségének egyik oka a kiemelkedő támogatottsága. Egyaránt találni hozzá leírásokat és oktató anyagokat az interneten, illetve néhány, mára alapműnek nevezhető könyvben, mint az Orange Book vagy a Red Book. Mindezek ellenére egyáltalán nem könnyű az elsajátítása, egyrészt az állapotgépként működő csővezetékes megoldás miatt, ami eltér a modern programozási nyelvekhez szükséges gondolkodásmódoktól, valamint a megfelelő használathoz szükséges matematikai ismeretek szükségessége miatt. [6], [7]
15
Modern grafikus effektek OpenGL környezetben
3.2.2. Direct3D A DirectX részét képezi, ami nem más, mint API-k gyűjteménye, melyek elsődleges célja multimédiás alkalmazásokhoz, elsősorban játékokhoz kapcsolódó feladatok kezelése Microsoft rendszereken. Ezen kis bevezetőből már látni is egyik komoly hátrányát, amely a platformfüggősége. A DirectX-et használó játékok csak a Microsoft konzolain (Xbox), Windowst használó PC-ken, illetve az egyre inkább elterjedő Winwods Phone-okon futtathatóak. Noha léteznek emulátorok, amik segítségével elindíthatóak ezek a programok, de ezek kimondottan optimalizálatlanok, játékok élvezhető futtatására alkalmatlanok. Első sorban olyan teljesítmény igényes, háromdimenziós grafikus alkalmazásoknál használják, mint a játékok. Amennyiben a fizikai eszközök lehetővé teszik, hardveres gyorsítást használ. Nagyon jól ki lehet segítségével használni a modern grafikus eszközök képességeit, úgy, mint a Z-buffering (mélység bufferelés), textúrák keverése, alfa keverés, modern atmoszferikus effektek és a programozható shaderek, azaz az HLSL (High Level Shader Language). Fejlesztését 1992-ben kezdték meg, akkor még a Microsofton kívül, később aztán felvásárolták a Windows 95-höz és 1996-ban jelent meg először a DirectX 2.0 részeként. Legutóbbi verziója a 12-es, melyet 2016-ban adtak ki. Az úgynevezett immediate módot támogatja, mely annyit jelent, hogy a kliens minden hívása azonnali kirajzolást eredményez a hardver részéről. Emiatt a program nem menti el az API-ban a modelleket és azok állapotait, minden egyes kirajzolás előtt elő kell azokat készíteni, függetlenül attól, hogy változott-e bármi a két iteráció között eltelt időben. Ez persze nem zárja ki a double buffering-et, amikor is a soron következő képet a hardver nem közvetlenül a megjelenítő eszközre rajzolja, hanem ideiglenesen lementi, majd, amikor azt ténylegesen meg kell jeleníteni, akkor küldi át a monitorra. Ez nagyfokú kontrollt ad a fejlesztők kezébe. Az OpenGL-hez hasonlóan szintén csővezetéket használ a renderelési folyamathoz, amely fázisok többsége teljes mértékben programozható az említett shaderek segítségével. A korábbi verziókkal ellentétben a 11-es megjelenésével elérhetővé vált a többszálú programok írása is, azaz a multi-threading. [8]
16
Modern grafikus effektek OpenGL környezetben
5. ábra: Direct3D csővezeték. Az egyik legjelentősebb különbség az OpenGL-hez képest a „Stream-Output Stage”, amely fázisban visszatölthetőek az adatok a memóriába, így könnyebben ellenőrizhetőek a shaderekben lezajló folyamatok.
17
Modern grafikus effektek OpenGL környezetben
3.3.
Fejlesztőkörnyezetek
Noha egy ilyen kisméretű projekt esetén nagyon jelentős előnyökkel, illetve hátrányokkal nem jár az egyes fejlesztőkörnyezetek használata, a célnyelv kiválasztása után el kellett döntenem, melyik programot szeretném használni. Lévén a C++ nyelvet választottam, lényegében csak három alternatíva jöhetett szóba, melyek a Code::Blocks, a Dev-C++ és a Visual C++ voltak. A következőkben ezeket szeretném röviden bemutatni.
3.3.1. Code::Blocks A Code::Blocks egy ingyenes, nyílt forrású IDE (Integrated Development Kit), mely elsősorban C, C++ és Fortran fejlesztésekhez ajánlott, de más programozási nyelvekkel való munka is támogatható vele, bár nem kimagasló szinten. Többféle fordítót is hozzárendelhetünk, mint például a GCC (GNU Compiler Collection, mely több programozási nyelvet is támogat, C, C++, Objective-C, Java, stb…), Clang vagy a Visual C++. Magát az IDE-t C++ nyelven fejlesztik és a wxWidgets könyvtárat használja a platformfüggetlen grafikus interfész megjelenítéséhez, amelynek köszönhetően mind a három jelentős operációs rendszeren, Windowson, Linuxon és Mac OS X-en is elérhető, használható. Plugin arhitechtúrát használ, így a működését, valamint ez elérhető funkciókat elsősorban a telepített pluginek (kiegészítők) és azok beállításai határozzák meg. Ennek köszönhetően az alap program könnyen bővíthető, személyre szabható az ingyenesen beszerezhető kiegészítőkkel, de akár sajátot is lehet hozzá fejleszteni, hogy teljesen a saját igényeinkhez szabhassuk az IDE-t. A programot 2005-ben kezdték fejleszteni és 2008-ig nem is adtak ki belőle stabil verziót, csak folyamatosan bővítették a funkcióit, illetve javították a hibákat. Ezeket a frissítéseket nightly build-eknek nevezik. Az első stabil verzió 2008 februárjában jelent meg, azóta folyamatos fejlesztés alatt áll a projekt. A jelenlegi legfrissebb verzió a 16.01, mely 2016 januárjában jött ki, de ha valaki szeretne teljesen naprakész verziót használni, továbbra is elérhetőek a nightly build-ek, valamint az egész forráskód elérhető SVN-en keresztül, noha azok nem feltétlenül olyan stabilak, mint a 16.01-es verzió. A 13.12-es verzió óta a wxSmith-nek köszönhetően már egy GUI szerkesztő is fellelhető a programban, amellyel könnyen átszerkeszthető a meglevő grafikus felhasználói felület. Az egyéb fejlesztői környezetekben létrehozott projektek importálása is támogatott, így akár Dev-C++ -ban vagy Microsoft Visual C++ -ban elkezdett, elkészített projektek is könnyedén migrálhatók. A fejlesztői felülete meglehetősen egyszerű, letisztult, azok számára, akik dolgoztak már más fejlesztőkörnyezetben, mint az Eclipse vagy a Visual Studio semmilyen problémát nem jelenthet a használata. [9] 18
Modern grafikus effektek OpenGL környezetben
3.3.2. Dev-C++ A Dev-C++ szintén egy ingyenes fejlesztőkörnyezet, melyet a GNU General Public License alatt adtak ki. Delphi nyelven írták és kizárólag C, valamint C++ programnyelveket támogat. Bármilyen GCC fordítóval használható, bár alap esetben a MinGW fordító van hozzácsomagolva. A projekt a SourceForge-on érhető el. Ez a program jelenleg csak Windows rendszereken működik, bár akadnak próbálkozások arra vonatkozólag, hogy egy Linux alatt futó verzió is készüljön belőle. Ezen IDE működése és kinézete is meglehetősen jól személyre szabható, hála az úgynevezett DevPaks-oknak, melyek egységbe csomagolt kiegészítőket takarnak. Ezek kiegészítő könyvtárakat, eszközöket és sablonokat foglalnak magukba. Ilyen csomagokból érhetőek el például olyan GUI segédprogramok, mint a GTK+, mely egy platformfüggetlen modul eszközkészlet grafikus felületek tervezéséhez, vagy a már említett wxWidgets is. De a GUI működését, kinézetét befolyásoló csomagoknál komolyabbak is jelen vannak, amik befolyásolhatják a Dev-C++ funkcionalitását, de akár a hatáskörét is, így például a tömörítési eljárásokat, az animációkat vagy a hangtámogatást is. Természetesen itt is van rá lehetőség, hogy a felhasználók maguk hozzanak létre ilyen csomagokat, melyeket ingyen közzé is tehetnek a közösség részére. A projekt 2005-ben indult és nagyjából 2011-ig nem történtek jelentős változások. Ekkoriban még rendszeres frissítések nem jelentek meg a programhoz. Azóta két jelentős verzió érhető el, a wxDev-C++, valamint az Orwell. A wxDev-C++ csapat verziójának legjelentősebb tulajdonsága a többféle fordító támogatása. Az Orwell verzió egyetlen ember, Johan Mes munkája, melynek első verziója, a 4.9.9.3 2011-ben jelent meg és rengeteg bugfixet, Windows SDK-t (Software Development Kit) és naprakészebb GCC-t foglalt magában. A legfrissebb verzió az 5.7.1-es, mely saját SourceForge oldallal rendelkezik. [10]
19
Modern grafikus effektek OpenGL környezetben
3.3.3. Visual C++ A Visual C++ vagy MSVC (Microsoft Visual C++) a Microsoft alternatívája a C, C++ és C++/CLI programnyelvekkel végzett fejlesztésekhez. Mivel a Microsoft jegyzi, természetesen nem nyíltforrású IDE-ről van szó. Eredetileg önálló programként létezett, de mára már a Visula Studio része lett. Mind ingyenes próbaverziója, mind ingyenes verziókja elérhető. C++ kódok fejlesztéséhez és debuggolásához kiemelkedően jó fejlesztőkörnyezet, főleg, ha egy Windows API-hoz (Application Programming Interface), DirectX-hez, esetleg .NET-hez írt kódról van szó, mivel a Microsoft hajlamos úgymond a saját malmára hajtani a vizet, azaz olyan projektek esetén jobb támogatást nyújt, ami aztán az ő rendszereiken fog megjelenni. Sok alkalmazás esetén felmerülhet az igény újrahasznosítható komponensekre, melyeket akár több alkalmazás is használhat ugyan azon forrásból, azaz a szükséges könyvtárat, rendszerkomponenst elegendő csupán egyszer telepíteni a céleszközre, az összes azon futó program ezt az egy példányt fogja használni. A Visual C++ különösen nagy hangsúlyt fektet ezen komponensek támogatására. A Visual C++ hosszú utat járt be. Röviden összefoglalva a fejlesztése még a Microsoft C/C++ -al kezdődött 1983-ban. Ez az úgynevezett 16-bites verzió volt. Az első, ténylegesen 32-bites verzió még a Windows 95 megjelenése előtt kijött és Visual C++ 2.0 volt a neve. Később, 2005-től már mind 32-bites, mind 64-bites verziója létezik a programnak, melyet akkor Visual C++ 2005-nek hívtak, azóta pedig minden verzió a kiadási évet viseli magán. Ennek megfelelően a jelenlegi legfrissebb verzió a Visual C++ 2015. Fontos megemlíteni, hogy, mint az a Microsoft termékeinél gyakran előfordul, itt is akadhatnak kompatibilitási problémák. A különböző verziói például különböző C runtime library-vel (futásidejű könyvtárak) érkeznek. Ennek köszönhetően a felhasználók bármelyik elérhető könyvtár segítségével fejleszthetik és fordíthatják a projektjüket, ám ez problémát okozhat, ha ugyanazon programon belül különféle komponenseket, például .dll-eket vagy .exe-ket használnak. [11]
20
Modern grafikus effektek OpenGL környezetben
3.4.
Modellező eszközök
A fejlesztési folyamat első fázisainak egyike az volt, hogy az OpenGL segítségével ne csak úgynevezett „beégetett” alakzatokat tudjak megjeleníteni, hanem valamilyen külső fájlból beolvasva dinamikusan rajzoljon ki a programom különféle objektumokat. Noha az interneten elég sok ingyenes 3D-s modell található, fontos volt, hogy a folyamat minden egyes lépését megismerjem, ezért szükségem volt egy olyan programra, amivel én magam hozhattam létre egyszerűbb modelleket. Szerencsére erre a feladatra is több, ingyenesen hozzáférhető alkalmazást találtam, melyek közül kettőt szeretnék kiemelni. 3.4.1. Autodesk Maya Az Autodesk egy amerikai cég, amely különféle, főleg CAD (Computer Assisted Deisgn) rendszerű szoftverek fejlesztésével foglalkozik. Leghíresebb termékük az AutoCAD, melyet nagyon széles körben használnak az iparban gépek, eszközök tervezéséhez, szimulációjához. Lévén 3D-s tervezőeszközökkel foglalkoznak, jelen vannak a multimédiás piacon is és van termékük többek között filmes effektek vagy játékokban használt modellek készítéséhez is. A két legismertebb ezek közül talán a Maya és a 3ds Max. Számomra inkább a többszörösen is díjnyertes Maya volt vonzó, amely elérhető Windowson, Linuxon és Mac OS X-en is. Hála az erős alapoknak, amelyeket a projekt kezdetekor más szoftverekből emeltek át, valamint a később beépített, kényelmesen személyre szabható GUI-nak, széles körben elterjedt, főként a filmiparban és egy könnyen kezelhető felületen keresztül lehet nagyon részletgazdag, látványos modelleket, effekteket készíteni vele. Nagy hátránya, hogy a program csak, mint oktatási segédeszköz használható ingyenesen tanárok és diákok számra, ráadásul akkor is csak egy 6 hónapos licensz jár hozzá, amelynek elméletileg lehet kérni a megújítását, de semmilyen garancia nincs erre vonatkozóan. A szerkesztés során úgynevezett csomópont gráf architektúrát használ, amelyben az egyes elemek csomópontokat alkotnak, melyek között értelmezve vannak bizonyos viszonyok, interakciók, illetve mind rendelkezik saját konfigurációs állománnyal. Többek között az animációk során ezek adják az alapját az olyan technikáknak, mint az ütközés érzékelés vagy az egyes modellek csontváza. Nagy erősségeként megemlítendő a Fluid Effects komponense, mely egy kiemelkedő minőségű, non-elastic (nem rugalmas) folyadék szimulátor, ami segítségével füstöt, tüzet, felhőket lehet életszerűen modellezni, vagy a Bifröst, mely élethű folyadék szimulációra képes. Persze a program képességei nem merülnek ki a folyadékok modellezésében, sok más valósághű modellezési eljárással rendelkezik többek között szőr, haj, részecskék és egyéb megjelenítéséhez, animációjához. 21
Modern grafikus effektek OpenGL környezetben
Fontosnak érzem még kitérni a MEL-re (Maya Embedded Language), ami egy script nyelv külön a Mayához fejlesztve. Segítségével a vizuális munkafolyamat mellett lehet platformfüggetlen scripteket írni, mellyel a program funkcionalitása befolyásolható, lényegében plug-ineket lehet készíteni hozzá. Ezekhez a pluginekhez van egy hivatalos közösségi platform, ahol bárki közzé teheti a sajátját, illetve könnyen találhat olyat, ami kielégítheti az igényeit. [12], [13]
3.4.2. Blender A Blender egy ingyenes, nyílt forrású, professzionális grafikus szerkesztő program. Egyaránt használják vizuális effektek, valamint 3D-s nyomtatásokhoz és játékokban használt modellek készítéséhez is. Sok egyéb mellett az elérhető funkciók között megtalálható a modellezés, textúrázás, folyadék és füst szimuláció, animáció és a renderelés, továbbá egy beépített játékmotorral is rendelkezik, bár nem ez az, ami miatt olyan nagy népszerűségnek örvend. 1995-ben hozták létre a Neo Geo berkein belül, mint egy belső alkalmazást és csak később, egy viszontagságos életutat bejárva, 2003-ban lett ingyenesen elérhető, hála egy közönségi finanszírozásnak, mely megmentette a csődbe ment cég projektjét. A szoftver a három nagy operációs rendszer mindegyikén elérhető és létezik 32-, valamint 64-bites változatban is, de ezen kívül olyan egyéb operációs rendszerekre is elkészült mint az AmigaOS, MorphOS, AROS, stb. Egyik nagy előnye, főleg a kezdő felhasználók számára, hogy a hivatalos weboldalán tekintélyes mennyiségű és jó minőségű oktató anyag található, illetve, hogy köszönhetően népszerűségének nagyon komoly közösséggel rendelkezik, ahol bárki könnyen találhat segítséget, ha problémába ütközne. Szintén ennek a közösségnek köszönhető, hogy a YouTube-on rengeteg oktató videót is fel lehet lelni, így bárki könnyen elsajátíthatja a program használatát. A felhasználói felülete első ránézésre kicsit kuszának, bonyolultnak tűnhet, de ez azért van, mert nagyon nagymértékben személyre szabható, több komponensből alakítható ki, valamint, mert elsősorban nem a GUI elemeinek használatára, sokkal inkább a gyorsbillentyűkre helyezi a hangsúlyt. Ezek miatt nehézkesebb kiismerni magát a GUI-t, mint a konkrét modellezési funkciókat. Nagy előnye, hogy sok különféle fájltípust támogat mind importálás, mind exportálás során, ráadásul kimondottan egyszerű az exportálás felparaméterezése, hogy a végső objektum file tényleg (csak) azokat az adatokat tartalmazza, amikre szükségünk van. [14]
22
Modern grafikus effektek OpenGL környezetben
6. ábra: A Blender felhasználói felülete. Látható, hogy rengeteg kisebb részre osztható a GUI, minden egységben a felhasználó által kiválasztott nézetekkel, amely által nagymértékben személyre szabható az alkalmazás.
23
Modern grafikus effektek OpenGL környezetben
3.5.
Platformfüggetlen segédkönyvtárak
A kezdetektől fogva szerettem volna elérni, hogy az alkalmazásom platformfüggetlen legyen. Mivel maga az OpenGL egyáltalán nem rendelkezik kontextus, ablak vagy éppen I/O eszközök kezelésére alkalmas mechanizmusokkal és mert a feladat jellege miatt főként a grafika programozására szerettem volna koncentrálni, ezért szükségem volt egy külső könyvtárra, amelyet integrálva a projektembe könnyen megvalósíthatom a platformfüggetlenséget. Szerencsére ezen a téren is találni jó pár ingyenes alternatívát az interneten.
3.5.1. SDL, SDL2 Az SDL, azaz a Simple Direct Media Layer könyvtár célja, hogy olyan low-level (alacsony-szintű) hardver absztrakciós réteget biztosítson a multimédiás rendszerkomponensekhez, amely segítségével nagy teljesítményű multimédiás alkalmazásokat, játékokat lehet készíteni, amik futtathatóak a manapság elérhető operációsrendszerek többségén, például Amiga-n, BSD-ken, Androidon, iOS-en, Linuxon, Windowson vagy Mac OS X-en is. A program és az operációs rendszer között helyezkedik el és lekezeli az I/O eseményeket, videó, audió hívásokat és még sok egyéb eseményt, valamint rendelkezik az alkalmazástól függően OpenGL vagy Direct3D kontextussal is, ezért alkalmas 3D-s grafikus programokhoz is. Ingyenes és nyílt forrású, C nyelven írták, de a célplatformtól függően C++ vagy Objective-C elemeket is tartalmaz. Az alap SDL könyvtár mellett elérhetőek kiegészítő könyvtárak is, mint például az SDL2_image, ami kifejezetten képek feldolgozására alkalmas vagy az SDL2_ttf, ami a true-type fontok kezelésére alkalmas. Szintén egy széles körben elterjedt könyvtárról van szó, melyet több, mint 700 játékszoftvernél használtak. Népszerűségének köszönhetően kifejezetten jó támogatottsággal, sok oktató anyaggal és egy remek közösséggel rendelkezik, így viszonylag könnyű megtanulni a használatát, illetve megoldást találni az esetlegesen felmerülő problémákra. Sajnos azonban az előforduló bugok esetén meglepően rossz a támogatottsága. Ha a hibára, amibe belefutunk nem található megoldás az interneten, akkor két lehetőség áll rendelkezésünkre. A bugot jelenthetjük az SDL oldalán, fórumain és reménykedhetünk benne, hogy valakinek már van megoldása a problémára, esetleg hamar megoldják azt. A másik opció, hogy megpróbáljuk magunk kijavítani a hibát. Továbbá a fejlesztése sem halad kimondottan gyorsan, így folyamatosan el van maradva a modernnek nevezhető technológiáktól, amilyen például a Directx 11.1 (azóta már a DirectX 12 is megjelent).
24
Modern grafikus effektek OpenGL környezetben
Sam Lantinga nevéhez fűződik, aki egy Windows alkalmazás Macintosh rendszerre való portolása közben állt elő az ötlettel és a legelső verziót 1998-ban adta ki. Legfrissebb verziója a 2013-ban kiadott SDL 2.0.0. Fejlesztését a mai napig főként egyetlen ember végzi, egyre jobban koncentrálva az Android támogatottságára is. [15]
3.5.2. GLUT Kizárólag OpenGL alkalmazásokhoz készített segédkönyvtár, mely rendszerszintű I/O műveleteket végez a gazda operációsrendszeren. Elsősorban ablakok és beviteli eszközök (billentyűzet, egér) kezeléséhez használatos, de néhány geometriai primitív kirajzolásához is tartalmaz rutinokat. Célja, hogy az egyes operációs rendszerek között viszonylag könnyen hordozható alkalmazásokat lehessen készíteni, illetve, hogy segítse az OpenGL tanulását. Az eredeti OpenGL Utility Toolkitet Mark Kilgard készítette és az X Window Systemet támogatta. Sajnos a projektet magára hagyták és a licenszelése nem tette lehetővé, hogy újra felhasználják, így, amikor később felmerült az igény a használatára, az egészet az alapoktól kellett újrakezdeni. Az első ilyen könyvtár, amely sikeresen implementálta az eredeti GLUT funkcionalitását a FreeGLUT lett, ami elég pontosan másolta le elődjét, kiegészítve azt néhány új funkcióval. Mivel az eredeti GLUT-nak voltak komoly hibái, hiányosságai, ezért sok fejlesztő készített különféle, nem összefüggő patch-eket is, amiket a különféle oldalakon fellelhető GLUT csomagokban lehet megtalálni. Ezek a patchek valószínűleg sosem fognak egy egységet alkotva bekerülni az eredeti GLUT könyvtárba, ezért fontos ügyelni arra, hogy a megfelelő csomagot szerezzük be, ha mindenképpen ragaszkodunk az eredeti GLUT könyvtárhoz. Egyik nagy hátránya, hogy nem egy úgynevezett standard komponens, ami azt jelenti, hogy nem csak a fejlesztőnek kell beszereznie a programozáshoz, de a felhasználóknak is a program futtatásához. Ez ugyan nem egy áthidalhatatlan probléma, de kényelmetlenné teheti a fejlesztést. Emiatt elsősorban kicsi és főleg saját felhasználású projektekhez ajánlott a használata. [16]
25
Modern grafikus effektek OpenGL környezetben
3.5.3. GLFW A GLFW egy lightweight (pehelysúlyú) C könyvtár, szintén kifejezetten az OpenGLhez, ami lehetővé teszi a fejlesztőknek, hogy menedzseljék az ablakokat, kontextusokat, valamint az I/O eszközöket, mint az egér vagy akár egy joystick. Támogatja a többmonitoros és több videó módos megjelenítést. Azon alkalmazások számára, amelyek kizárólag az OpenGL-t használják a grafikus megjelenítéshez, egy vékony absztrakciós réteget is biztosít, ami lehetővé teszi a multi-platformos alkalmazások készítését. Fontosnak érzem kiemelni, hogy a GLFW a fent említett mechanizmusokon kívül lényegében semmi mást nem nyújt, így többek nem alkalmas képek, szövegek vagy bármi egyéb kirajzolására, de audió kezelésére és GUI elemek létrehozására sem. Habár a könyvtárat C nyelven írták, nagyon sok egyéb programozási nyelv APIjához van binding (kötés), így lehet használni többek között C#, Java, Python és még sok más programozási nyelv alatt is. Legutolsó stabil verziója a 2015-ben kiadott 3.1.2-es. [17]
26
Modern grafikus effektek OpenGL környezetben
3.6.
Grafikus interfészek
Noha a feladatkiírással egyértelművé vált, hogy nem egy teljes értékű játék elkészítése lesz a cél, így nem kell olyan elemeket implementálnom, mint például egy főmenü, az is nyilvánvaló volt, hogy az elkészült alkalmazás bemutatása során szükség lesz valamiféle grafikus felületre, amin keresztül könnyen szabályozható a grafikus effektek működése, illetve a háromdimenziós világban elhelyezett objektumok attribútumai. Mivel ezt az interfészt sem szerettem volna magam implementálni, szintén ingyenes könyvtárakat kerestem, amiket beépíthetek az alkalmazásomba. 3.6.1. CEGUI A Crazy Eddie’s GUI egy kifejezetten C++ alkalmazásokhoz, azokon belül is játékokhoz készített grafikus felhasználói felület könyvtár, bár ettől függetlenül nem csak játékokhoz lehet használni. Legnagyobb erőssége, hogy nagymértékben személyre szabható és a többi alternatívához képest jelentősen több widget-el (eszközzel) rendelkezik. Az így kialakítható interfész fejlesztését igyekeznek megkönnyíteni a CEED (CEGUI Unified Editor) segítségével, amely egy szerkesztő eszköz és aminek segítségével kikerülhető a grafikus felület megjelenítését biztosító xml állományok kézzel való szerkesztése. Nincs közvetlen interakciója az alatta fekvő rendszerrel, nem tudja kezelni például az I/O eseményeket, sem erőforrásokat betölteni. Ezen feladatokat rábízza a programozóra, habár elérhető néhány segédkönyvtára, amik segítik ezek implementációját. Ez a megközelítés alkalmassá teszi arra, hogy platformfüggetlen legyen, hiszen, ha egy olyan vékony réteget helyezünk el közte és az operációs rendszer között, mint például az SDL, akkor minden további nélkül fut a különféle rendszereken. A grafikus megjelenítést sem maga végzi, csupán modulokat biztosít a Direct3D, OpenGL és Ogre rendszerekhez. Beépített animációs rendszerrel is rendelkezik, ami sok olyan alap átmenetet valósít meg, mint az ablakok átméretezése, színek váltása. Ezeket is az XML állományban kell definiálni és különböző események hívhatják meg őket. Az alap CEGUI funkciók használatához semmilyen külső függőséget nem kell kielégíteni, bár vannak funkciói, amikhez már szükséges kiegészítő könyvtárak telepítése. 2004 óta érhető el, akkor jelent meg az 0.1.0-ás verziója és jelenleg a 2016-ban kiadott 0.8.5-ös verziószámnál tart. [18] 27
Modern grafikus effektek OpenGL környezetben
3.6.2. AntTweakbar Egy másik alternatíva az AntTweakbar egy sokkal kisebb, sokkal szűkebb funkcionalitást biztosító C, C++ könyvtár. Első sorban OpenGL-t és DirectX 9-et, valamint afeletti verziókat támogat. Működésének alapja, hogy a programkódban használt változókat hozzá kell kötni a tweakbar által definiált eseményekhez, így azok értékei grafikus felületen, futásidőben változtathatóak. Mivel ezen funkciók kielégítésére elég csupán néhány modul, jóval kevesebb lehetőség áll a programozó rendelkezésére. Emiatt elsősorban csak a fejlesztések alatt szokták használni, a kész alkalmazásokon belüli GUI-k elkészítésére nem alkalmas. Célja, hogy a demozáshoz szükséges felületet minimális háttérismerettel és ráfordított energiával lehessen létrehozni. Ennek érdekében sok feladatot automatikusan végez, így például a numerikus értékekhez kapcsolt grafikus elemhez automatikusan létrehoz egy RotoSlider elemt is, aminek segítségével az egér mozgatásával lehet az adott változó értékét növelni, csökkenteni. Nem tartalmaz sok függvényt és egy könnyen érthető, példákkal ellátott leírást ad hozzá a saját weblapján. Támogatja a callback függvényeket, így nem csak numerikus és logikai típusú változók értékének változtatására alkalmas, de bonyolultabb műveletek elvégzésére is, illetve ezáltal nagyobb kontrollt ad a programozónak. Mind előnyként, mind hátrányként felfogható, hogy a megjelenített elemek elrendezésére lényegében semmilyen hatása nincs a programozónak, azokat csak hozzá kell adni a tweakbar-hoz, ami automatikusan rendezi el őket. Hogy ezek mégse csak ömlesztve jelenjenek meg, úgynevezett horizontális választóelemek létrehozhatóak, valamint lehetőség van a csoportosításokra, így a valamilyen logika mentén egy egységet alkotó kezelőelemek egy jól elkülöníthető, kinyitható/becsukható szekcióba szervezhetőek. Fejlesztésekor előtérbe helyezték a teljesítményt, így a gazda alkalmazás renderelési idejének növekedése elhanyagolható. [19]
28
Modern grafikus effektek OpenGL környezetben
4. Felhasznált technikák, technológiák A feladat megoldásához kiválasztott programok, könyvtárak részletes összehasonlítása és a végső kiválasztás szempontjainak ismertetésekor feltételezem, hogy az olvasó tisztában van az előző fejezetben ismertetett tényekkel, rövidítésekkel. Mint ahogyan később, a fejlesztési folyamat során is végig fontos volt a platformfüggetlenség, illetve egy jól támogatott alkalmazás elkészítése, úgy a technológiák és programok kiválasztása során is nagyon fontos szerepet töltött be az említett két szempont.
4.1.
Programozási nyelv
A programozási nyelvet illetően a három lehetőség közül az elsőt, azaz a C nyelvet meglehetősen hamar elvetettem. Ennek elsősorban az volt az oka, hogy a másik kettővel szemben nem objektum orientált programozási nyelv, továbbá, mint azt már korábban is említettem, egy elég öregnek mondható programnyelv, amellyel ugyan sok gépközeli utasítást lehet hatékonyan és gyorsan végrehajtani, a manapság divatos, modern megoldások támogatottsága nem mondható túl jónak. Abban az esetben, ha az alkalmazásom minden részét magam implementáltam volna, külső könyvtárak felhasználása nélkül, valószínűleg nehezebb lett volna ez a döntés, így azonban hamar egyértelművé vált, hogy a C++ és C # közül kell választanom. Az egyik lényeges szempont a döntésem meghozatala során a két nyelv eltérő nehézsége volt. Mivel egyik programnyelvvel sem volt a fejlesztés megkezdése előtt jelentős tapasztalatom, ezért próbáltam utánajárni, melyik tanulható könnyebben, melyikkel lehet gyorsabban, látványos eredményeket elérni. Ebből az összehasonlításból majdnem egyértelműen a C # került ki győztesen. Ennek több oka is van. Először is maga a nyelv szintaktikája úgymond barátságosabb a kezdők számára, főleg azok számára, akik előtte foglalkoztak Java fejlesztéssel. Rendelkezik olyan beépített funkciókkal, mint például a szemétgyűjtő, amik nagyban megkönnyítik a programozó dolgát (nem csak a kezdőkét). Továbbá hozzáférést biztosít a Microsoft XNA környezethez, amely még több terhet vesz le a fejlesztő válláról. Mindezeknek köszönhetően a C # használatával elég az olyan dolgokra koncentrálni, mint a konkrét játéklogika, játék ciklusok megírása, valamint a dizájn. Természetesen ennek a kényelemnek megvannak a hátulütői is. Ezek közül kiemelném a jelentősen megnövekedő teljesítmény igényt, ami miatt az igai, ipari játékokhoz nem szokták használni, hiszen azoknál nagyon fontos szempont a lehető legoptimálisabb futása a programnak. Mint azt az előző fejezetben is említettem, mivel a C++ -t fejlesztésekor fontos szempont volt, hogy bonyolult, nagy teljesítményigényű rendszerek és programok 29
Modern grafikus effektek OpenGL környezetben
optimalizált futása legyen megvalósítható vele, sokkal alkalmasabb játékszoftverek írására, noha a nyelv használata messze nem olyan kényelmes, mint a C # -é. Mivel azonban én nem csak általánosságban a játékfejlesztés témakörét szerettem volna körbejárni, hanem konkrétan azt a fajta fejlesztési folyamatot, amit az iparban is használnak, inkább a C++ mellett döntöttem. A C++ mellett szólt az az érv is, hogy annak használatával úgymond sokkal közelebb kerülhetek a számítógéphez, nem rejt el annyi funkciót, mint a C #. Persze emiatt nehezebb a nyelv elsajátítása, több dologra kell odafigyelni a programozás során, de ha az embernek sikerül átvergődnie magát ezeken a nehézségeken, sokkal jobb, sokkal részletesebb betekintést nyer az egész folyamatba és ezáltal jobb (játék)fejlesztő válhat belőle. A C++ mellett szól az is, hogy több fordító létezik hozzá, melyek több operációs rendszeren (is) futnak, így a C++ -ban megírt kódot valamivel egyszerűbb platformok között mozgatni, mint a C # -ot, ami elsősorban a .NET-től való nagymértékű függése miatt sosem lett igazán platformfüggetlen programozási nyelv. Végezetül az sem volt elhanyagolható tény, hogy a konzulensem is inkább a C++ t részesíti előnyben, így azáltal, hogy én is amellett döntöttem, jobban a segítségemre tudott lenni a fejlesztési folyamat során. Nem egyszer fordult elő, hogy komolyabban elakadtam a programozásban, de ő, hála a több éves tapasztalatának jóformán azonnal tudta a megoldást, így minimális időveszteséggel tudtam folytatni a munkámat.
30
Modern grafikus effektek OpenGL környezetben
4.2.
Grafikus könyvtár
A grafikus könyvtárakat illetően viszonylag egyszerű és egyértelmű volt a döntés meghozatala. Az elsődleges szempont ez esetben is a platformfüggetlenség megléte volt, ami az OpenGL esetében teljes mértékben teljesül. Sajnos a Direct3D és általánosságban a DirectX, mint azt már korábban is ismertettem, csak Microsoft alapú rendszereken érhető el és noha folyamatosan találni próbálkozásokat, hogy más rendszereken is elérhetővé tegyék, a tény, hogy maga a DirectX nagyon sok Microsoft komponenstől függ, nagyon megnehezíti ezt a folyamatot és a mai napig nincs igazán jó megoldása a problémának. Bár engem elsősorban a PC játékok érdekelnek, azok fejlesztésével szeretnék a későbbiekben foglalkozni és amik javarészt csak Windows operációs rendszerekhez készülnek, mégis szerettem volna először egy általánosabb megoldással megismerkedni. Továbbá ez esetben is felmerült, hogy én és konzulensem más-más rendszereket használunk és egy platformfüggetlen alkalmazással volt ezt a legegyszerűbb áthidalni. Természetesen nem a fent említett szempont volt az egyetlen, amit figyelembe kellett vennem a döntés meghozatalakor. Az egyik érv, amely az OpenGL ellen szólt, az a hardverrel való kommunikációja. Míg a Direct3D API-ja egy DDI-vel (Device Driver Interface) kommunikál és az éri el közvetlenül a grafikus eszközt, addig az OpenGL esetében az egész API a hardveren van implementálva. Ebből kifolyólag a különböző videókártyák esetében előfordulhat többé-kevésbé eltérő viselkedés, hiszen az adott OpenGL hívást nem feltétlenül ugyan úgy értelmezik. Ha a két könyvtár legfrissebb verzióit tekintjük a fejlesztési folyamat egyszerűségét, hatékonyságát illetően, közel azonosaknak lehet őket nevezni. Ez azonban közel sem volt mindig így. A Direct3D korai verziói bosszantóan sok, feleslegesnek nevezhető munkát tett a fejlesztők vállára. Míg az OpenGL esetén a különbőz állapotváltozásokhoz a legtöbb esetben elegendő egyetlen függvényhívás, a Direct3D esetén ezen műveletek objektumok létrehozását, beállítását és elküldését követelték meg. Ez a korai verzióknál fellelhető probléma azért volt fontos számomra, mert bármelyik lehetőség mellett döntök is, szerettem volna valamelyik régebbi verziójával foglalkozni, ami minél kevesebb funkcionalitást rejt el a fejlesztő elől, mivel fontos volt számomra, hogy ne csak magával a fejlesztési folyamattal, de a háttérben zajlóeseményekkel is minél alaposabban megismerkedjem. Szeretném megemlíteni, hogy amennyiben nem csak a választott feladat megoldása és a kitűzött céljaim elérése lett volna a cél, az egyik fontos szempont, amely az OpenGL irányába terelhetett volna az a hardvertámogatottság. A grafikus könyvtárakba bekerülő új funkciók grafikuskártyákon való megjelenése ugyanis teljesen ellentétes irányú utat követ. A Microsoft esetén a legfrissebb DirectX funkciókhoz igyekeznek minél hamarabb alkalmazkodni a hardvergyártók, míg az OpenGL funkcionalitása a kiadott hardverek képességeihez igazodik. Ezen két megközelítésből könnyen látható, hogy a legfrissebb Direct3D használata esetén könnyebb olyan alkalmazást írni, amely nem képes adott hardveren megfelelően futni, mint ugyanezt OpenGL-el elérni. Ugyanakkor figyelni kell arra is, hogy 31
Modern grafikus effektek OpenGL környezetben
előfordulhat olyan funkcionalitás, amelyet a grafikuskártya gyártók már jó ideje implementáltak, ezért elérhető lenne, noha a hivatalos OpenGL dokumentcáiókban még nem jelent meg. Továbbá az is problémát jelenthet, hogy mivel az OpenGL API-t a hardvergyártók maguk implementálják, előfordulhat, hogy ugyanazon vagy nagyon hasonló funkcionalitást a különböző gyártók más-más API-kkal kínálnak. Ezzel szemben a Direct3D-t egyetlen gyártó specifikálja, ezért egy sokkal konzisztensebb API-t eredményez, de elveti a gyártóspecifikus szolgáltatások lehetőségét. Már a kezdetektől fogva megfigyelhető a Microsoft és a Khronos Group között a versengés, hogy a lehető legjobb teljesítményt érjék el. Ez a versengés sok különféle megoldást hívott életre az évek alatt, melyek közül manapság a legjelentősebbnek a hardware overhead csökkentését célzót emelném ki. Ennek lényege, hogy igyekeznek a grafikus alkalmazás által jelentett terhelést a lehető legjobban szétosztani a CPU és egy vagy több GPU között. Ebben a versenyben jelen állás szerint az OpenGL vezet, amely jelentősen gyorsabb lehet a Direct3Dhez képest. Bár az itt felsorolt szempontok főleg az OpenGL-t helyezik előnybe, ha megfigyeljük a játékipart, érdekes módon azt vesszük észre, hogy inkább a Direct3D dominál, főleg a PC játékok között. Ennek oka egyrészt a Microsoft API-jának konzisztenciája, valamint a mára jóval kényelmesebbé vált fejlesztés és a tény, hogy a legtöbb játék fejlesztése nem igényel olyan professzionális API-t, mint az OpenGL-é. Ezzel szemben az OpenGL-t sokkal jobban kedvelik a filmiparban és a professzionális grafikus alkalmazásoknál (CAD-ek). Összegezve a fentebb olvasottakat, döntésem meghozatalakor a fenti szempontok közül a platformfüggetlenség volt az elsődleges, illetve az OpenGL jobb teljesítménye.
32
Modern grafikus effektek OpenGL környezetben
4.3.
Fejlesztőkörnyezet
Mivel a C++ nyelv jóformán sztenderdizált, egységesített, ezért a három fejlesztőkörnyezet között első ránézésre nem találni igazán jelentős különbségeket. Ha azonban az ember jobban utánajár az említett programoknak, lehet találni apróságokat, amik az egyik vagy másik felé dönthetik a mérleg nyelvét, attól függően, milyen jellegű fejlesztésre szeretnénk használni. A Dev-C++ volt az első, amit elvetettem. Elsősorban azért, mert bár az Orwell verziója a mai napig támogatott, az eredeti már a 2000-es évek elején „elhalt”, tehát a karbantartások és fejlesztések ellenére sem kimondottan naprakész. Ennek egyébként az is az oka, hogy magát a fejlesztőeszközt Delphi-ben írták, nem C++ban, így még ha valaki komolyan használja is a programot, nagy valószínűséggel nem akar, sőt nem is tud belenyúlni annak forráskódjába, így az esetlegesen felmerülő hibákra, igényekre nagyon nehezen lehet megoldást találni, frissítéseket pedig még ritkábban. Az sem elhanyagolható tény, hogy mivel az eredeti Dev-C++ már nagyon régi és az Orwell verzió is csak próbálja tartani a lépést, előfordulhat, hogy más fejlesztőeszközökben alapvetőnek vett funkciók nem találhatóak meg benne. Mégis, talán a legnagyobb hibája a beépített debugger, ami meglehetősen primitív, mondhatni „szűkszavú”, tehát nem igazán nyújt elegendő információt a fellépő fordítási vagy futásidejű hibákról. Maga a debugger egyébként még hibás, bugos is, ami csak tovább ront a használhatóságán, márpedig, ha valaki foglalkozott valaha OpenGL fejlesztéssel, tudhatja, hogy az OpenGL alkalmazások debuggolása eleve nehéz feladat, nem jó, ha emellett még az alap C++ szintaktikai hibákat is úgy kell keresni, mint tűt a szénakazalban. A Dev-C++ -al szemben mind a Code::Blocks, mind a Visual C++ aktív fejlesztés alatt áll, folyamatosan megjelenő frissítésekkel és aktívan működő hibabejelentő felületekkel, amik miatt funkcionalitásuk folyamatosan bővül és még akkor is könnyen találunk megoldást a bugokra, ha mi magunk nem is vagyunk hajlandóak „benézni a motorháztető alá”. A Microsoft Visual C++ mellett szól, hogy talán kiemelkedően annak van a legjobb debuggere, amely egyszerűen és nagymértékben személyre szabható és, ami nagyon részletes információkkal szolgál. Továbbá jelentősen több programozási nyelvet támogat és el van látva olyan kényelmi funkciókkal, mint az új projekt, fájl létrehozásánál tipikus könyvtár struktúra kialakítása vagy az adott típusú fájlban egy-két tipikusan előforduló, esetleg kötelező blokk generálása. Sajnos azonban ezen funkciók teljesen biztosan csak a Microsoft Visual Studio Professional kiadásában érhetőek el, ami egy fizetős szoftver. Az ingyenesen használható Express verzióban megtalálható funkciók mennyisége, minősége eltérhet ezektől. Emiatt az iparban érthető módon a Professional kiadást használják, ám én nem szerettem volna csak ezen a feladat miatt megvenni egy professzionális fejlesztőeszközt.
33
Modern grafikus effektek OpenGL környezetben
Hátrányként lehet még megemlíteni, hogy a Visual Studio és a Visual C++ is jelentősen nagyobb tárhely és erőforrás igénnyel rendelkezik, mint a Code::Blocks. Továbbá nincs Linux alatt futtatható verziója, ami ugyan nem nagy hátrány, mivel én Windowson fejlesztettem, de mint említettem, fontos volt a platformfüggetlenség a fejlesztési folyamat minden pontjára nézve annak érdekében, nehogy a fejlesztési folyamat egy későbbi szakaszában derüljön ki, hogy az addig elkészült projekt valami oknál fogva nem migrálható át egy másik operációs rendszer alá. Végső soron a Code::Blocks mellett döntöttem. Ennek két fő oka is volt. Az egyik, hogy több C++ fordító közül is lehet választani a használata mellett, többek között a Visual C++ fordítója is ide tartozik, míg a Visual Studio esetén csak az érhető el. A másik a platformfüggetlensége. Maga a fejlesztőkörnyezet is platformfüggetlen, illetve, hála a GNU GCC fordítónak, az elkészített, lefordított kód is biztosan fog futni például Linuxon. Fontos szempont volt az is, hogy bár nem teljesen nyújt a Visual Studio Professionalével megegyező funkcionalitást, nagyon közel áll ahhoz, ráadásul mindezt ingyen. Végül, noha ez sokkal inkább egy személyes preferencia, mintsem egzakt összehasonlítás, de számomra a Code::Blocks közössége segítőkészebbnek, hasznosabbnak tűnik, mint a Visual C++ -é. Magáról a program telepítéséről nem írnék, hiszen meglehetősen egyszerű és gördülékeny. Amit talán megemlítenék, hogy fontos odafigyelni a nightly buildekre, amennyiben az ember tényleg naprakészen szeretné tartani a programot. Szerencsére ezek telepítése sem tart többől, mint néhány fájl felülírása.
34
Modern grafikus effektek OpenGL környezetben
4.4.
Modellezőeszköz
Az alkalmazásom demójához készítendő modellek szerkesztéséhez egy olyan programra volt szükségem, ami lehetőleg a legkevesebb előismeretet igényli, valamint aminek könnyen elsajátíthatóak az alapjai, ugyanakkor, professzionális programként is megállja a helyét, ha esetleg a későbbiekben is szeretném hasznát venni. Ezeket az igényeket a két említett program többé-kevésbé kielégítik. Mivel képességeiket tekintve nincs túl nagy különbség a két program között, a döntés meghozatala elsősorban azon alapult, hogy a Blender teljesen ingyenes, míg a Maya-nak csak próbaverziója érhető el ingyen, a teljes értékű programért fizetni kell, illetve, hogy a Blender használata valamivel barátságosabb, könnyebben tanulható. Mivel meglehetősen bonyolult programokról van szó, minden egyszerűsítés nagy segítség tud lenni, főleg kezdők számára. És bár a Mayanak létezik egy úgynevezett light verziója, ami kifejezetten a játékfejlesztőknek készült és sok „felesleges” funkciót kivettek belőle, a Blender általánosságban még mindig könnyebben tanulható. Vegyük például az úgynevezett Edit Moe-ot. Abban az esetben, ha csomópontokat, éleket, esetleg felületeket szeretnénk kiválasztani és módosítani, ezt a módot használjuk. Ami kényelmesebbé teszi a Blenderben, az az, hogy átkapcsolva erre a módra, továbbra sem lehet más objektumokból kiválasztani ezeket az elemeket, míg a Mayában ezt külön be kell állítani, így összetett modellek esetén könnyű véletlenül például rossz éleket hozzáadni a kiválasztáshoz, ami miatt széteshet a modell. A Maya egyik előnye, abban az esetben, ha az ember a Unity motort használja játékfejlesztésre, hogy közvetlenül a Unity forrásainak könyvtárába importálhatóak a projektek, amiket azonnal fel is lehet használni. Mivel azonban én saját motor írását tűztem ki célul, ez a funkció számomra felesleges volt. Mint már említettem, a Blender mellett szól az is, hogy nagyon figyelemreméltó közösséggel és támogatottsággal rendelkezik, így az amúgy is könnyen megérhető modellezési eljárások mellett rengeteg oktató anyagot találni az interneten, illetve könnyű segítséget kérni, ha elakadnánk valahol. Bár a Blender beépített játékmotorja nem nevezhető kiemelkedőnek és nem is igazán találkozni a segítségével készített játékokkal, arra tökéletesen megfelel, hogy az ember a modellezés közben leellenőrizhessen alap koncepciókat, így menet közben kilehet szűrni a téves elképzeléseket, nem kell sok fáradságos munkaórát kidobni az ablakon és elölről kezdeni a tervezést. Mindezeket figyelembe véve, abban az esetben, ha a szoftver ára egyáltalán nem jelent gondot, a két program között jóformán csak a személyes preferenciák segíthetnek dönteni. Mivel mindkét alkalmazással elérhetőek ugyanazon eredmények, csak más-más utat bejárva, tényleg csak az azt használó fejlesztő ízlésétől függ, melyiket helyezi előtérbe.
35
Modern grafikus effektek OpenGL környezetben
4.5.
Segédkönyvtárak
Annak érdekében, hogy ne csak az alkalmazásom alapjai, de az egész program platformfüggetlen legyen olyan értelemben, hogy ne csak elinduljon, de minden extra implementáció nélkül működjenek az I/O eszközök is szükségem volt egy segédkönyvtárra, amely egyetlen interfész segítségével elrejti előlem az operációs rendszerek ilyen jellegű különbségeit. Az első ilyen megoldás, amellyel összefutottam keresgéléseim során, a GLUT volt, illetve, mivel a projektet magára hagyták, az utódja, a FreeGLUT. Noha jelen alkalmazásomhoz nem volt szükségem egyébre, mint az ablakok, illetve a billentyűzet és egér menedzselése, a GLUT sajnos ennél többet nem nyújt. Ezzel szemben az SDL2 és kiegészítő könyvtárai jóval nagyobb funkcionalitást biztosítanak. A GLUT ellen szólt még az is, hogy az SDL2 kifejezetten játékok fejlesztéséhez lett tervezve, míg a GLUT nem, emiatt, bár kezeli az egyes operációsrendszereken az I/O eseményeket, nincs olyan jól optimalizálva. Márpedig egy játék esetén kiemelkedően fontos, hogy egy adott inputra a lehető leggyorsabban és például az egér esetén a legpontosabban reagáljon a program. A GLFW használata valamivel egyszerűbb, mint az SDL-é, viszont funkcionalitása ennek sem olyan széleskörű. Nagy hiányossága, hogy kikerültek belőle a képek kezelésére alkalmas metódusok, így annak érdekében, hogy használhassam a modellek textúráit, saját megoldást kellett volna implementálnom. Hátránya továbbá, hogy callback függvényekkel operál, amelyek kissé bonyolultabbá teszik a fejlesztést, valamint a program is úgymond szemetesebbé válik tőle, mivel a callbackekhez szükséges globális változók könnyen elszaporodhatnak. Az SDL2 esetén ezzel szemben eseményvezérelt programozás érvényesül, így például a különböző billentyűk lenyomásait egyetlen switch blokkal le lehet kezelni. Az sem elhanyagolható tény, hogy a GLFW-t bár több platformra is portolták, ezek csak a három „asztali” operációs rendszert, a Windowst, a Linuxot és az iOS-t fedik le, míg az SDL ténylegesen platformfüggetlennek nevezhető. Amennyiben valaki veszi a fáradságot és utánajár, azt azonban észreveheti, hogy a GLFW fejlesztése valamivel aktívabb, mint az SDL2-é. Ez persze nem jelenti azt, hogy az SDL el lenne hanyagolva, csupán valamivel lassabban reagál az újabb és újabb technológiák megjelenésére, valamint kissé nehézkesebben megy a bugok javítása is. Egy ilyen, a google-ben gyakran megjelenő probléma például a HOME billentyű, mely nem mindig működik, sem SDL sem GLFW használata mellet. A GLFW fejlesztője azonban már dolgozik a problémán, míg az SDL oldaláról semmilyen, ezt a hibát célzó kezdeményezést nem tapasztalni. Az SDL2 telepítését illetően sajnos elég könnyű problémákba ütközni és bár, ha már működik, viszonylag egyszerű a használata, okozott pár kellemetlen pillanatot az integrálása. 36
Modern grafikus effektek OpenGL környezetben
Az egyik problémát oka a Code::Blocks működése okozta, amiben ugyanis be lehet állítani a fordítás során használatos könyvtárakat egyrészt a program saját beállításai között, amely esetben globális beállításokról beszélünk, amik csak az adott gépen érvényesek, valamint magának a projektnek a beállításaiban. Ekkor csak az adott projektre lesznek érvényesek a beállítások, de azzal együtt migrálhatóak azok más rendszerekre. Ez kezdetben nagy problémát okozott, mikor az asztali gépen elkezdett projektet a laptopon szerettem volna továbbfejleszteni, ám az azon található Code::Blocks nem tudta azt fordítani. Egy másik bosszantó probléma volt, hogy a csomagban letöltött .dll állományok közül nem egy hibás volt, de legalább is windows 10-es rendszeren futásidejű hibát okoztak. Szerencsére ezen állományokhoz viszonylag könnyen találtam javított verziókat az SDL közösségeiben. Végül megemlíteném még, hogy az SDL2-höz beszerezhető segédkönyvtárakat, mint például a képkezelőjét sem volt egyszerű integrálni, lévén egyáltalán nem mindegy, melyik verziót szerezzük be hozzá. Jóformán minden adott SDL verzióhoz egy adott kiegészítő könyvtárat kell beszerezni, ami további felesleges keresgéléshez vezet.
37
Modern grafikus effektek OpenGL környezetben
4.6.
Grafikus interfész
Ami a grafikus interfész létrehozásához szükséges segédkönyvtárakat illeti, noha végül az AntTweakbart választottam, a CEGUI egy jóval bővebb funkcionalitást nyújtó, professzionálisabb megoldás lett volna. Hogy végül miért nem amellett döntöttem, a következőkben részletezném. A CEGUI segítségével, mint arra már ki is tértem korábban, a lehetőségek jóformán végtelenek. Rengeteg interfész elemet biztosít, melyekhez implementálva vannak benne alapvető animációk, mint például az áttűnések, beúszások. Ezen elemek elrendezését is sokkal szabadabban lehet megvalósítani, ezáltal teljesen személyre szabható grafikus felületeket lehet kialakítani. Ennek a nagyfokú szabadságnak azonban ára is van. Ebben az esetben ez azt jelenti, hogy jóval nehezebb megtanulni és használni a CEGUI nyújtotta lehetőségeket. Bár a mára már elérhető szerkesztő valamelyest könnyít ezen a helyzeten, az igazán részletekbe menő szerkesztéshez még mindig javasolt az xml állomány kézzel való szerkesztése, ami nem egyszerű feladat egy kezdő számára. Mivel azonban én tényleg csak egy, a bemutató során használandó minimális GUIt szerettem volna, közel sem volt szükségem a CEGUI komoly funkcionalitására. Sokkal fontosabb volt, hogy a lehető legkönnyebben és leggyorsabban hozhassam létre a grafikus interfészt. Erre a feladatra egyértelműen az AntTweakbar volt jobb, hiszen sokkal kevesebb funkcióval rendelkezik, így csupán a felületen szereplő elemenként egy-egy sor kódra volt szükségem, illetve esetenként pár callback függvény megírására. Sajnos a CEGUI ellen szól az is, hogy eléggé nehézkes a beüzemelése, ha egyáltalán sikerül. Én első körben szerettem volna kipróbálni, mire képes, azonban hiába próbálkoztam több verzióval is, egyiket sem sikerült működésre bírnom. Volt, amelyik nem támogatta az SDL2-t, volt amelyik a Windows házirendjében ütközött problémába, így nem fért hozzá a kívánt memóriatartalomhoz. Ezekre a problémákra találtam ugyan félmegoldásokat, hosszas keresgélés után sem sikerült tényleges megoldást találni. Ezután egyértelmű volt a döntés, hiszen az AntTweakbar-t az oldalán található leírás alapján telepítve már az első próbálkozásra megfelelően működött. Az AntTweakbar egyik jelentős hátránya viszont, hogy már évek óta nincs fejlesztve és karbantartva, így modern alkalmazások készítéséhez szinte teljesen alkalmatlan. Szerencsére, mivel még a fejlesztés elején úgy döntöttem, hogy az OpenGL korábbi verzióját szeretném használni, nem ütköztem kompatibilitási problémákba, de amennyiben modern OpenGL-t használtam volna, valószínűleg ez sem lett volna használható megoldás. Ami a telepítését és használatát illeti, a saját weblapján található egyszerű leírások és a kínált példák bőven elegendőek és jól érthetőek ahhoz, hogy probléma nélkül integrálhassuk a projektünkbe és elkezdjük használni.
38
Modern grafikus effektek OpenGL környezetben
4.7.
Egyéb programok
A fejlesztés során felhasználtam még kisebb-nagyobb feladatok elvégzésére egyéb segédprogramokat is, melyek között voltak hagyományos, telepítést igénylőek és online felületen keresztül használhatóak is. Ezeket nem sorolnám fel mindet, de hármat mindenképpen szeretnék kiemelni közülük. Az első a gDEBugger, amely egy elég komoly OpenGL, OpenCL debugoló, profilozó és memória analizáló eszköz, ami mára már teljesen ingyenesen használható. Aki foglalkozott már OpenGL programozással, tudhatja, hogy az OpenGL és főleg a hozzá kapcsolódó shader programok debugolása alap esetben nagyon nehézkes, sokszor pedig szinte lehetetlen. Fontos volt ezért, hogy találjak valami olyan könyvtárat, esetleg programot, ami valamelyest megkönnyíti ezeket a folyamatokat. A gDEBugger mindezt kifejezetten felhasználóbarát módon teszi, ráadásul az elérhető funkcionalitása is kielégítően bő és személyre szabható. Segítségével a program futása alatt valós időben, illetve a programot megállítva az egyes iterációk között leellenőrizhetünk olyan dolgokat, mint például a bufferek tartalma, az aktív textúrák, a program memória, CPU és egyéb erőforrásigénye, statisztikák az OpenGL függvényhívásokról vagy például az aktív shaderek kódja és változóik aktuális értéke. Ezen segédprogramnak hála sok, főleg a shader programozás során felmerült hiba megoldásának ideje jelentősen csökkent. A másik alkalmazás a CrazyBump nevű textúra generátor. Sajnos nem teljesen ingyenes a program, viszont az én igényeimet kielégítő próbaidőszakot biztosított. Segítségével a modellekhez felhasznált alap textúrából egy barátságos GUI-n keresztül, néhány egyszerű beállítást elvégezve lehet legeneráltatni meglepően jó minőségű normal, specular és depth mapeket, amelyeket a későbbiekben ismertetésre kerülő effektek során használtam fel. Végezetül a glsLangValidator-t említeném meg. Ez a Khronos Group által kiadott hivatalos OpenGL shader nyelv fordító. Segítségével a létrehozott shader programokat lehet egy parancssorból lefordíttatni, illetve, hiba esetén egy kellőképpen részletes leírást kapni. Ezt arra használtam, hogy a shaderek szintaktikai ellenőrzését elvégezzem, mivel sajnos abban az esetben, ha azok valamelyikét nem lehetett lefordítani, annak okáról sajnos sehonnan máshonnan nem tudtam megfelelő információkat összeszedni.
39
Modern grafikus effektek OpenGL környezetben
5. A feladatok megoldása Mivel az alkalmazást úgymond a nulláról kezdtem el megírni, a fejlesztési folyamat néhány jól elkülöníthető részre bontható. Az első fázis egy minimális SDL2-t használó alkalmazás készítése volt, amely más operációs rendszereken is futtatható. Miután ez elkészült, belevetettem magam az OpenGL elsajátításába és a grafikus motor implementálásába. Kezdetben a régi immediate (azonnali) kirajzolást valósítottam meg, majd áttértem a buffer objektumokra. Mivel az alkalmazásom már képes volt háromdimenziós jelenetek kirajzolására, el kellett készítenem egy segédosztályt, ami egy meghatározott fájltípusból képes objektumokat betölteni és renderelésre előkészíteni. Végül, a shader programozás elkezdése előtt implementálnom kellett még egy kamerát, hogy a létrehozott háromdimenziós világban körbe lehessen járni az objektumokat. Ezek után már csak a kiválasztott grafikus effektek programozása volt hátra, melyekhez a már említett shader programozást kellett elsajátítanom. A következőkben ezeket a fejlesztési fázisokat fogom részletezni, ismertetve az eredményeket és az azok eléréséhez felhasznált irányelveket, megoldásokat.
40
Modern grafikus effektek OpenGL környezetben
5.1.
SDL2-t használó cross-platform alkalmazás alapjai
Mint a bevezetőben is kitértem rá, a fejlesztés legelső lépése az volt, hogy integráljam a platformfüggetlenség alapját képező SDL2 segédkönyvtárat. Fontos volt, hogy ezt még a konkrét OpenGL specifikus kódok arása előtt megtegyem, hiszem az SDL szolgáltatja a kontextust az OpenGL számára. Köszönhetően az interneten fellelhető sok oktatóanyagnak és az SDL könnyű, eseményvezérelt használatának, a fejlesztés ezen fázisát sikerült viszonylag hamar teljesítenem. A beépített függvényeinek hála elegendő volt csupán néhány függvényhívást beépítenem a programom inicializációs fázisába, mint az SDL_CreateWindow() melyet felparaméterezve az SDL már el is készítette az alkalmazás ablakát, vagy az SDL_GL_CreateContext() amellyel az OpenGL-es megjelenítéshez szükséges contextust lehet létrehozni. Ezeken felül szükségem volt még a billentyűzet és egér eseményeinek elkapására és lekezelésére. Ehhez az SDL_PollEvent() függvényhívásra volt szükségem, amelynek visszatérési objektumához, azaz az eseményhez elegendő volt egy egyszerű switch blokk implementálása. Ezen blokkon belül külön kezeli a program az egér és a billentyűzet által generált eseményeket. A konkrét billentyűértékek kezelését nagyban elősegíti az SDL_Keycode, amely segítségével az SDLK_* kifejezésekkel (ahol a * a billentyű értékét jelöli, például az „a” billentyűt a case SDLK_a: elágazással lehet elkapni) lehet adott billentyűkhöz adott funkcionalitást hozzárendelni. Fontos odafigyelni azonban arra, hogy mind a billentyűzet, mind az egér esetén külön kezeli a „lenyomás” és „felengedés” eseményeket, ami jóval nagyobb szabadságot ad a funkcionalitás kialakításakor, de ami miatt nagyobb figyelmet igényel, hogy a megfelelő pillanatban a megfelelő eseményt kezeljük le. Az így létrehozott SDLManager és EventManager osztályok segítségével egy olyan programot sikerült implementálnom, amely ugyan még semmi látványos dologra nem volt képes, de ugyanúgy reagált a beviteli eszközök eseményeire Windows és Linux rendszereken is, tovább minden további programozási feladat elvégzés nélkül létrehozta az OpenGL számára szükséges ablakokat és kontextusokat.
41
Modern grafikus effektek OpenGL környezetben
5.2.
OpenGL immediate mód
Mivel korábbi tanulmányaim, munkáim során még nem volt szerencsém megismerkedni az OpenGL-el, ezért az alapjainál kellett elkezdenem a grafikus könyvtár megismerését. Emiatt eleinte a célom csak annyi volt, hogy a programom kirajzoljon valamit a monitorra, függetlenül attól, hogy milyen hatékonyan teszi azt vagy hogy a megírt kód mennyire számít naprakésznek. A fentieknek megfelelően első körben az immediate módot használtam a legegyszerűbb objektum, egy háromszög megjelenítéséhez. Ez a módszer már hosszú évek óta elavultnak számít, viszont nagyban segíti az OpenGL működési elvének megismerését. Lényege, hogy a kirajzolandó modellt alkotó primitívek, mint a pontok, vonalak és felületek két OpenGL függvényhívás között kell, hogy elhelyezkedjenek. A két említett függvény a glBegin(primitív típus) és a glEnd(). Ez a két függvény jelzi a rendszernek, hogy renderelési feladat elvégzése következik. A nyitó és záró függvényhívás között kell megadni a primitívekhez tarozó koordinátákat, aminek megfelelően, az előzőekben megadott sorrendiséget követve jeleníti meg a program a glBegin() paramétereként megadott primitíveket. Bár ezzel a megközelítéssel jól megérthető az OpenGL csővezeték jellegű működése, meglehetősen teleszemetelheti a kódot a koordináták egyesével történő megadása, valamint a program futását is lassítja, főleg, ha több glBegin() és glEnd() hívás is történik az alkalmazás egy iterációja során. Az immediate mód segítségével létrehozott „Hello world!”-nek megfelelő OpenGL alkalmazás által megjelenített kép az alábbi ábrán látható.
7. ábra: Immediate móddal kirajzolt háromszög 42
Modern grafikus effektek OpenGL környezetben
5.3.
Buffer objektumok használata
A következő lépés az volt, hogy az immadiate módot elhagyva áttérjek a buffer objektumok használat. Ezek speciális OpenGL objektumok, amelyek formázás nélküli tömböket tárolnak és amiket az OpenGL kontextus allokál. Elsősorban vertex koordináták és textúra koordináták tárolására használják őket. Nagyban növelik a teljesítményt, mivel a bennük tárolt adatokat közvetlenül a grafikuskártyára tölti fel a program, ezáltal a renderelési folyamat során nem kell a CPU segítségével lekérni azokat a rendszer memóriájából. Lévén OpenGL objektumokról van szó, a szokásos glGen*** és glDelete*** paradigmát követi a létrehozásuk és a törlésük. Az objektum létrehozása csak annak hivatkozását adja vissza, ahhoz, hogy valóban beállítsuk annak tartalmát, előbb hozzá kell kötni a kontextushoz a glBindBuffer(enum target, uint bufferName) hívással, majd fel kel tölteni a kívánt adatokkal a glBufferData() segítségével. Miután letároltuk a kívánt adatokat, a renderelési folyamat elején ismét hozzá kell kötni az adott buffer objektumot a kontextushoz a már ismertetett módon. Ezután értesíteni kell a kontextust, hogy milyen típusú buffer objektumról van szó, illetve, hogy abban milyen típusú adatok és hogyan szerepelnek. Egy vertexeket tároló buffer esetén például a glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* pointer) hívást használjuk, ahol a size értéke határozza meg, hogy hány darab egymást követő koordinátát tartalmaz egyeten vertex, a type valamilyen mumerikus típus, általában float (lebegőpontos), a stride azt jelöli, hány darab adat szerepel két vertexet alkotó koordináta csoport között, végül a pointer az első vertex első koordinátájának helyét jelzi a tömbön belül. Ha az adatok elő vannak készítve a kirajzolásra, akkor a glBegin() és glEnd() hívásokat elhagyva a glDrawArrays(GLenum mode, GLint first, GLsizei count) segítségével rajzoltatjuk ki egyetlen lépésben a bufferekben letárolt vertexeket, felületnormálisokat vagy bármi egyebet. Az említett függvényhívás attribútumai közül a mode a kirajzolandó primitívet jelöli, ennek megfelelően lehet például GL_TRIANGLES vagy GL_POINTS. A count adja meg a darabszámot, azaz, hogy hányat kell az adott primitívből kirajzolni, míg a first értelemszerűen a legelső elem indexét adja meg. Bár a programkód jelentősen szebb, átláthatóbb lesz az említett változtatásoknak köszönhetően, ráadásul a teljesítmény még egy ilyen kis alkalmazás esetében is érezhetően nő, köszönhetően az OpenGL rendkívül rossz debugolási lehetőségeinek, nagyon nagy figyelmet igényel a buffer objektumok és a glDraw*** függvények helyes paraméterezése, máskülönben a megjelenített objektumok szétesnek, de az is előfordul, hogy SIGSEGV szegmentációs hibával leáll a program futása.
43
Modern grafikus effektek OpenGL környezetben
5.4.
Modellbetöltő segédosztály
Ezen a ponton az alkalmazásom már képes volt háromdimenziós objektumok megjelenítésére, ám azok ekkor még csak néhány vertexkoordinátából álló, minimális alakzatok voltak, amiknek ráadásul a koordinátái a program kódjába voltak égetve. Annak érdekében, hogy ezek az adatok ne foglalják feleslegesen a helyet, valamint, hogy bonyolultabb modelleket is megtudjak jeleníteni, szükségem volt egy objektumbetöltőre, ami adott kiterjesztésű fájlból beolvassa ezeket az adatokat. Ehhez a feladathoz először kikellett választanom, hogy melyik fájlformátumot szeretném támogatni a programomban. A választásom a Blender által is támogatott .obj típusra esett. Azért választottam ezt a kiterjesztést, mert egy egyszerű szövegszerkesztővel, mint például a Notepad++ is olvasható és szerkeszthető a tartalmuk és mert azok meglehetősen egyszerű, emberek által is könnyen értelmezhető módon vannak benne tárolva. Ezen tulajdonságainak hála teljesen az ellenőrzésem alatt tarthattam a folyamatokat az adatok lépésről lépésre való ellenőrzésével, ráadásul a háromdimenziós objektumok tárolási struktúrájának alapelveivel is megismerkedhettem. Ennek az osztálynak elsődlegesen az volt a dolga, hogy az előzőleg megadott fájlokat beolvassa, majd azok tartalmát strukturált módon letárolva előkészítse a modelleket a renderelési folyamathoz. Tudni kell a .obj fájlokról, hogy nagyon egyszerűen, egy-egy speciális sorral tagolva tárolják az egyes objektumokat és azokon belül a vertexek, felület normálisok, textúrák és felületek koordinátáit, adattagjait. Az adatok importálásához tehát beolvastattam az adott fájl sorról-sorra, mindegyik esetén ellenőrizve, hogy az adott soron szerepel-e valamelyik speciális jelölő elem, mint például az egyes objektumok kezdetét jelölő „o” karakter. Ezután attól függően, hogy éppen milyen jelölő elem előzte meg a konkrét értékeket más-más módon dolgoztam fel az adatokat. A „v” kezdetű sorok vertex koordinátákat jelöltek, az „vn”-esek normálisokat, a „vt”-sek textúra koordinátákat, az „f”-esek pedig a felületeket jelölték. Ezen sorok tartalmát kettő, illetve 3 részre bontva kaptam meg az X, Y és Z koordinátákat, amiket egy-egy ideiglenes tömbben tároltam el. A felületek esetén valamivel bonyolultabb volt a helyzet, mivel ezen sorokban a három darab értékhármas a felületet határoló három vertex, illetve az azokhoz tartozó normálisok és textúra koordináták indexét adták meg, ennek megfelelően tehát a tényleges értéket az ideiglenes tömbökből még kikellett keresnem. Az adatok beolvasása közben az egyes objektumok végére érve aztán mindegyikhez létrehoztam egy-egy új t3DObject típusú struktúrát, amiket felparamétereztem, illetve legeneráltattam a buffer objektumokat. A .obj fájlok tartalmaznak még egy speciális sort, az „mtllib” kezdetűt, mely a modellhez tartozó material fájlt jelöli. Ennek felépítése nagyon hasonló a .boj fájléhoz, csak ennek a tartalma az objektumok anyagainak leírását foglalja
44
Modern grafikus effektek OpenGL környezetben
magában, amely megadja az egyes anyagok olyan tulajdonságait, mint a fényesség, szórt fény értéke vagy a hozzárendelt textúra elérési útja. Mivel a .mtl fájlt is ez az osztály dolgozza fel, ebben implementáltam az SDL2-t felhasználó képbetöltő metódust is, amely beolvassa a megadott textúrafájlokat és azokat elhelyezi egy-egy bufferben. A későbbi fejlesztések során felmerült még az igény néhány extra funkció meglétére, melyeket szintén ez az osztály foglal magában, de ezeket majd azon részeknél részletezem, melyek programozása során létrehoztam őket. Az ObjectLoader osztály segítségével olyan bonyolultabb modelleket is megtudtam ezek után jeleníteni, mint az a 6. ábrán látható.
8. ábra: Az ObjectLoader osztállyal, egy .obj fájlból beolvasott összetett modell
45
Modern grafikus effektek OpenGL környezetben
5.5.
CameraManager osztály
Az OpenGL programozásnál az egyik felmerülő probléma, hogy nincs hagyományos értelemben vett kamera objektum értelmezve. Emiatt, annak értelmében, hogy a létrehozott háromdimenziós térben lehessen mozgatni, illetve forgatni a nézetet, implementálnom kellett még egy segédosztályt, ami a különféle inputokra reagálva imitálja a kamera mozgását. Ezt úgy értem el, hogy a világ objektumait az inputtól várt eseménnyel ellentétesen transzformáltam. Ha például az input az „a” billentyű, ami a nem létező kamerát balra mozgatná, az objektumokat tolja el a program jobbra, így keltve a mozgás illúzióját. A fent említett funkcionalitás implementálásához felhasználtam az OpenGL Mathematics, azaz glm könyvtárat, amely nagyban megkönnyíti a vektorokkal és mátrixokkal való műveletek elvégzését. A folyamat során minden iterációban három darab háromdimenziós vektort kell az osztálynak elkészítenie. Ezek jelölik a „kamera” pillanatnyi helyét, az irányát, illetve az irányára merőleges, felfelé mutató vektort. Ezeket felhasználva aztán a gluLookaAt(kamera_pozíció, kamera_iránya, felfelé_mutató_vektor) függvényt meghívva lépnek életbe a módosítások. A megfelelő látvány kirajzolásához végül még kikellett választanom egy perspektíva mátrixot. Döntésem a glFrustum()-ra esett, mivel egy jól paraméterezhető, gyakran használt megoldás, ami egy csonkagúla alakú térrészt transzformál a monitorra. Ennek segítségével a valósághoz igen közel álló perspektivikus megjelenítést lehet elérni.
46
Modern grafikus effektek OpenGL környezetben
5.6.
GLSL alapok
Az OpenGL Shading Language egy C szerű programozási nyelv, melynek segítségével az OpenGL csővezeték működését lehet módosítani. Kétféle shadert különböztetünk meg, a vertex és fragment shadereket. Az előbbivel a modelleket felépítő csúcspontok szintjén lehet módosítani a rederelési folyamatot, míg utóbbival pixelek szintjén. Mivel az így elkészült kódok egy-egy kisebb OpenGL programot eredményeznek, még egy további segédosztályra volt szükségem, ami egyszerű szöveges fájlokból beolvassa azok tartalmát, majd ezeket felhasználva regisztrálja a programot a grafikuskártyán. Az így létrehozott azonosítókat felhasználva aztán már az alkalmazás futása közben is változtatható, mikor melyik shaderben implementált algoritmus legyen érvényes. Sajnos korábban a shaderekkel sem foglalkoztam még, így ezen a téren is sok mindent kellett pótolnom. Emiatt a legelső kódjaim csupán a modellek kirajzolására voltak alkalmasak, felhasználva az OpenGL nyújtotta transzformációs mátrixokat, valamint már kezdetektől fogva ezek segítségével oldottam meg a textúrák használatát is. A korábban említett bufferekben definiált koordinátákat és a glModelViewMatrix, valamint glModelViewPrjoecionMatrix összeszorozva a vertex shader végzi el az objektumok csomópontjainak a megfelelő háromdimenziós térbe való transzformálását. Ezek után az említett értékek a fragment shader-hez kerülnek, ahol a textúra koordináták és az előzőleg a shaderhez kötött textúra fájl segítségével a megfelelő színvektort rendeli a vertexek által közrefogott térrész egyes pixeleihez. A textúra shader-hez kötése az úgynevezett uniform változók segítségével történik, melyek lényegében a shader programok inputjaként szolgálnak. Ezeken keresztül lehet megvalósítani a kommunikációt az alkalmazás és a shader-ek között. Ezen változók vektorok, valamint numerikus értékek lehetnek. A konkrét értékük beállításának módja, hogy a glGetUniformLocation() segítségével lekérjük az adott változó címét, majd ezt felhasználva a címhez rendeljük a kívánt adatokat. Textúrák esetén ez az érték a glActiveTexture(GL_TEXTUREID) hívással generált azonosítót jelenti, mely tulajdonképpen a grafikuskártya egy memóriacíme, ahová előzőleg feltöltöttük a textúra fájl tartalmát. Végül, az adott pixelhez a megfelelő színvektort a GLSL texture2D() metódusa határozza meg, mely egy vec4(r,g,b,a) típusú értéknégyest ad vissza, ahol az egyes értékek a piros, zöld, kék és alfa színek 0.0-1.0 értéktartományon értelmezett mennyiségét jelölik. Az ismertetett minimális shader program segítségével kirajzolt és textúrázott modellek megjelenítésére a 7. ábrán lehet látni egy példát.
47
Modern grafikus effektek OpenGL környezetben
9. ábra: Az első shader program segítségével textúrázott modell
48
Modern grafikus effektek OpenGL környezetben
5.7.
Irányított fény (Nap)
Miután az előző bevezető programnak köszönhetően megismerkedtem a shaderek működési elvével és a velük történő kommunikáció alapjaival, nekiláthattam a grafikus effektek, azokon belül is a fények programozásának. A legelső típusú fény, amit implementáltam, az úgynevezett directional (irányított) fény volt. Ennek a fénytípusnak az a sajátossága, hogy a létrehozott háromdimenziós világon belül tulajdonképpen a végtelenben helyezkedik el és emiatt a fényforrásból kiinduló fénysugarak irányvektorai párhuzamosak. Emiatt ezt a fajta fényforrást szokták a Nap szimulációjára használni, hiszen az is olyan távol található a Földtől, hogy a fénysugarai jóformán párhuzamosan érkeznek el hozzánk. Mint a későbbiek folyamán ismertetett többi fényforrás esetén, itt is meg kellett határozni néhány alapvető értéket, mint a belőle kiinduló fénysugarak színét, azok csillapítását, irányát, valamint, a helyzetét. A fentebb említett „végtelenben” való elhelyezkedését a szokásos X, Y és Z helyzetkoordinátákon felül egy negyedik, „W” érték megadásával jelöljük, melynek értéke, irányított fény esetén mindig 0.0. Ezeket az értékeket a már ismertetett uniform változók segítségével küldi át az alkalmazás a shadereknek: uniform vec4 lightPositions [MAX_LIGHTS] Ezután két lehetőség is van a feldolgozásukra. Ez a két módszer abban különbözik, hogy a vertex vagy fragment shaderen belül történnek meg a számítások. Ha ugyanis a vertex shader végzi el a fény szimulációját, akkor jelentősen csökken a megjelenített kép minősége, hiszen ebben az esetben például a fényforrástól számított távolságot csak vertexenként levetítve adja meg, ami azt jelenti, hogy egy döntött felület esetén, amelynek egyik vége jóval távolabb esik a fényforrástól, az egész felület ugyan olyan árnyalatot kap, hiszen az azt határoló vertex koordináták változatlanok maradnak. A fragment shaderben ennél jóval pontosabb eredmények érhetőek el, hiszen ilyenkor az egyes pixelekhez tartozó felületdarabokra a GPU automatikusan interpolálja a távolságot. Emiatt én már a kezdetekben is az úgynevezett per-pixel, azaz pixelenkénti megoldást használtam. Megemlíteném még, hogy az OpenGL felfogás szerint a renderelt világ végső fényviszonyait három tényező kombinálásával kapjuk. Ezek az ambient, diffuse és specular értékek. Az ambient, vagyis a környezeti fény azt hivatott szimulálni, amely egy adott környezetbe belépve, a környezet elemein sokszor visszaverődve egy minimális, teljesen homogén megvilágítást eredményez. Ez fogható fel úgy, mint amikor egy kivilágított szobában például a szekrények hátulja sem teljesen megvilágítatlan, noha a lámpák fénye közvetlenül nem éri őket. A diffuse, azaz szórt fény a felületeket közvetlenül érő fényt jelenti, melynek erőssége függ a beérkező fénysugarak dőlésszögétől is. Végül a specular, vagyis tükröződő fény a tárgyakról visszavert fény erősségét határozza mag. Mindhárom összetevő esetén figyelembe kell venni, hogy mind a fényforrásoknak, mind a különböző anyagoknak, mind magának a felépített világnak meglehet határozva, hogy milyen módon befolyásolják az értéküket. 49
Modern grafikus effektek OpenGL környezetben
A fenti alapelveket figyelembe véve az irányított fényforrás megvilágítását az egyes pixelekre lebontva egy viszonylag egyszerű metódussal ki lehet számítani, amihez csupán alapvető vektorműveletek elvégzésére van szükség, mint a fényforrásból érkező fénysugár beesési szögének számítása esetén a vektorok normalizálása vagy skaláris szorzásuk. Ezek segítségével meghatározható, hogy milyen beesési szöghöz milyen erős megvilágítás rendelhető, illetve, hogy milyen mértékben és irányban érvényesüljön a fényvisszaverődés a felületről. A beesési szög kiszámítására használtam fel a korábban említett felület normális vektorokat, amik megmutatják, hogy az adott felületre milyen irányból érkezik merőlegesen a fény, illetve ennek felhasználásával azt, hogy ettől a merőlegestől mennyire tér el. A beeső fénysugár irányának kiszámítására az alábbi sor szolgál: vec3 lightDir = normalize(vec3(modelViewMatrix * lightPositions[lightIndex])); Mint látható, a lightIndex által meghatározott fényforrás pozícióját helyezzük át a modelltérbe, majd ennek vesszük a normalizált alakját. Az így megkapott vektort felhasználva a diffuse érték a következőképpen számítható: float diff = max(dot(normal, lightDir), 0.0); , ahol a dot() függvény a normálvektor és a fénysugár irányvektorának skaláris szorzata. A max() metódusra azért van szükség, hogy ne kaphassunk negatív értékeket. A specular tényezőhöz szükséges a visszaverődési irány meghatározása a reflect() függvényhívással, valamint a felülethez rendelt fényességi érték, a materialShininess, mely megadja, mennyire fényes annak anyaga, milyen mértékben fókuszált a visszaverődés képe. A specular számítás így a következőképpen végezhető: vec3 reflectDir = reflect(-lightDir, normal); float spec = pow(max(dot(viewDir, reflectDir), 0.0), materialShininess); Végül, az így kapott értékekkel beszorozzuk a fényforrás színét, majd ezek összege adja az adott fragment végső megvilágítási értékét. vec3 ambient = vec3(lightColors[lightIndex]); vec3 diffuse = vec3(lightColors[lightIndex]) * diff; vec3 specular = vec3(lightColors[lightIndex]) * spec; return (ambient + diffuse + specular);
50
Modern grafikus effektek OpenGL környezetben
5.8.
Pontszerű fényforrások (lámpák)
A pontszerű fényforrások (point light) annyiban különböznek a fentebbi irányítottól, hogy ezek a kirajzolt tér egy adott, belső pontjában helyezkednek el, továbbá, hogy a fénysugaraik a középpontból kiindulva minden irányban terjednek, azaz nem párhuzamosak. Fontos különbség még, hogy ezeknél a fényforrásoknál már figyelembe kell venni a fény csillapításának mértékét is, hiszen, a valódi lámpák fénye sem tesz meg akkora utat, mint a Napé, ezért már jóval kisebb távolságon érzékelhető a fényerősség csökkenése. Az előző fejezetben ismertetett számítás, mely megadja a beeső fénysugár irányát, az alábbiak szerint módosult a fragment pozícióját felhasználva: vec3 lightDir = normalize(vec3(modelViewMatrix * lightPositions[lightIndex]) fragPos); A csillapítás a valóságban arányos a fényforrástól mért távolság négyzetének reciprokával, tehát a feladat egy hasonló formula implementálása volt. Ez a kezdeti képlet annyiban módosult, hogy a nullával való szorzásból eredő problémák kiküszöbölés végett az 1/távolság2 helyett 1/1+távolság2-et használtam, továbbá, hogy az egyes fényforrásokhoz más-más csillapítást lehessen rendelni, kiegészítettem 1/1+k*távolság2-re, ahol „k” jelöli az adott lámpa saját csillapítási értékét 0.0 és 1.0 között. Végül, a különböző oktatóanyagok között találtam egy formulát, mely a valóságos fényerőcsökkenéssel nagyban megegyező görbét generál és ami háromféle csillapítást is megkülönböztet, így a végső formula: K = 1.0/(konstant_csillapítás + lineáris_csillapítás*távolság + négyzetes_csillapítás*távolság*távolság) Ez a megközelítés nem csak élethű fényviszonyokat eredményezett, de lehetővé tette azt is, hogy az egyes fényforrások erősségét futásidőben is lehessen módosítani, így interaktív módon kikísérletezni, milyen értékek a legoptimálisabbak az adott modellekhez. Természetesen a csillapítás a fentebb ismertetett három összetevő közül a környezeti fényekre nem érvényes, hiszen azoknak pont az a lényege, hogy egy minimális megvilágítás mindig legyen a világban, sose legyen egyetlen objektum sem teljes sötétségben. Annak érdekében, hogy a bemutatóm során jól lehessen szemléltetni az ismertetett effekteket, már kezdettől fogva úgy valósítottam meg ezeket a fényforrásokat, hogy mind a helyzetük, mind a színűk és a három csillapítási együtthatójuk könnyen, akár futásidőben változtathatóak legyenek. A pontszerű fényforrások által generált fényviszonyokat a 8. ábrán látható kép szemlélteti.
51
Modern grafikus effektek OpenGL környezetben
10. ábra: pontszerű fényforrások GLSL alapú szimulációja
52
Modern grafikus effektek OpenGL környezetben
5.9.
Spotlight (reflektor)
Az előzőekhez nagyban hasonlító fényforrás volt az utolsó típus, amit leprogramoztam. Az egyetlen különbség a pontszerűekhez képest, hogy bár az ebből kiinduló fénysugarak is a tér minden irányába mutatnak, rendelkezik egy levágási szöggel, amin kívül már nem érvényesül a megvilágítás. Ezzel a megoldással olyan effekteket lehet létrehozni, mint például egy zseblámpa fénye. Ennek megfelelően egy olyan metódust kellett implementálnom, amely a szórt és visszavert fényeket csak egy meghatározott kúpon belül veszi figyelembe. Ennek a kúpnak a leírásához, mivel a csúcsa a fényforrás közepén helyezkedik el, csak a kúp palástja és középvonala által bezárt szög meghatározására volt szükség. Ahhoz, hogy meghatározhassam, az említett kúp merre mutat a térben, módosítanom kellett pontszerű fényforrások metódusát úgy, hogy az figyelembe vegye a fény irányát. Ehhez szükségem volt a fényforrás középpontja és az adott pont közötti irányvektor, valamint az előre definiált irányvektor skaláris szorzatára. Ennek segítségével könnyen kiszámítható, hogy az aktuális pont beleesik-e a kúpba vagy sem, meg van-e világítva vagy sem: float spotEffect = dot(normalize(lightDirections[lightIndex]),normalize(-lightDir)); if (spotEffect < lightConeAngles[lightIndex]) { … Annak érdekében, hogy az így kialakított megvilágítás minél valóságosabb legyen még egy apró simításra volt szükség. Ez annyit tett, hogy a pont helyét nem binárisan, benne van/nincs benne értékekkel jellemeztem, hanem a kúp palástjához közeledve a smoothstep() GLSL függvény segítségével egy keskeny, de folyamatos átmenetet képező szegélyt adtam a fénynek. (Az említett függvény egyébként Hermite interpolációt valósít meg két határérték között.) smoothstep(lightConeAngles[lightIndex]-0.002, lightConeAngles[lightIndex], spotEffect); Egy zseblámpa szerű fényforrás megvalósítása látható a 9. ábrán.
53
Modern grafikus effektek OpenGL környezetben
11. ábra: „Spotlight”-al megvilágított felület. Mivel minden egyéb fény ki van kapcsolva, csak a zseblámpa kúpján belüli terület van megvilágítva
54
Modern grafikus effektek OpenGL környezetben
5.10. Specular mapping (tükröződési térkép) Az eddigiekben a valósághű megjelenítés kimerült abban, hogy az egyes felületekre, lapokra „ráfestette” a program a textúrákat. Ennek köszönhetően távolabbról és a megfelelő szögből nézve az objektumokat, megjelent a részletesség minimális illúziója, azonban közelebbről megvizsgálva őket könnyen felismerhető, hogy ezek továbbra is nagyon alacsony részletességgel rendelkező, sima felületek. Ezen felül a tükröződési érték kiszámítása során csupán az objektumokhoz definiált anyagok szintjén volt értelmezve egy-egy érték. Emiatt, hiába szerepelt egy textúrán érezhetően két eltérő tulajdonságú anyag, például fém és fa, mindkettő esetén ugyan olyan mértékű csillogás jelentkezett, ha ugyan azon anyaghoz voltak hozzárendelve. Ezt orvosolandó létezik az úgynevezett specular mapping, melynek lényege, hogy az alap textúra mellett a program használ még egyet, ami azonban nem egy szokásos, színes kép, hanem többnyire egy feke-fehér árnyalatos. Ennek oka, hogy ezen textúrát nem arra használjuk, hogy valamilyen képet fessünk a felületekre, hanem csupán arra, hogy egy-egy textúrához meghatározhassuk, annak melyik részéhez milyen mértékű tükröződés rendelhető. Ezt egyszerűen úgy érjük el, hogy minél kevésbé szeretnénk, hogy visszaverje a fényt az adott terület, annál sötétebb színnel töltjük ki a specular map megfelelő részét. Ennek megfelelően a teljesen fekete területek egyáltalán nem, míg a fehérek teljes mértékben visszaverik a rájuk eső fényt, azaz az általuk jelölt színvektorok a (0,0,0), illetve (1,1,1) értékeket kapják meg. Természetesen ez az értékadás sem bináris jellegű, a színvektor mindhárom tagja a 0-1 intervallumon értelmezett.
12. ábra: Textúra és a hozzá tartozó specular map
55
Modern grafikus effektek OpenGL környezetben
Ezt a textúrát is a már ismertetett módon tölti be és továbbítja a shaderekhez a program, amiken belül szintén a texture2D() segítségével nyerjük ki a megfelelő értéket. Ezt aztán a számított fények specular összetevőjénél használjuk fel, lecserélve az előzőekben használt értéket, amely anyagonként volt meghatározva. Ezzel a technikával anélkül növelhető a részletesség látszata, hogy a háttérben valóban részletesebb modelleket hoznánk létre, amelyek jóval komolyabban visszavethetnék a teljesítményt. Ennek a megoldásnak egy alternatíváját képezi, amikor nem egyszerű fekete-fehér specular map-et generálnak, hanem erre a feladatra is színes textúrát használnak. Az így kapott tükröződéseknek ezáltal nem csak az intenzitása, de a színe is meghatározható. Az ok, amiért én nem ezt a megoldást választottam, hogy a valóságban is elsősorban a megvilágító fény színe határozza meg a tárgyakról visszaverődő fény színét, így nem lett volna elég realisztikus a megjelenítés különböző színű fények esetén. A munkám során minden felhasznált „map” textúrát a már ismertetett CrazyBump programmal generáltattam az eredeti textúrát felhasználva. Bár ezzel az eljárással nem mindig kapjuk meg a lehető legjobb végeredményt, még így is szép effekteket lehet velük létrehozni, amik ezen feladat megoldása során megfelelő eredményeket produkáltak.
13. ábra: A specular mappingnek köszönhetően csak a doboz szélén található fémes csík csillog 56
Modern grafikus effektek OpenGL környezetben
5.11. Normal mapping (felületnormálisok térképe) A háromdimenziós világok minden esetben poligonokból, sokszögekből épülnek fel. Ezek általában háromszögeket jelentenek, mivel azok segítségével lehet a legjobb közelítő felületeket létrehozni, így mindegyik poligon három darab vektort, értékhármast jelent, amelyek a három csúcspont helyzetét jelölik. A renderelési folyamat során ezeket a koordinátákat transzformáljuk és helyezzük el a térben. Mivel ez a művelet meglehetősen számításigényes is lehet, minél több sokszögből áll egy modell, annál nagyobb megterhelést jelent a GPU számára, annál lassabb lesz a program futása. Másrészről viszont, minél kevesebb poligonnal dolgozunk, annál elnagyoltabbak, részletszegényebbek lesznek a modellek. Akkor mégis hogyan érhető el, hogy nagy részletességű, aprólékosan kidolgozott modellekkel népesítsük be világunkat, de úgy, hogy a lehető legkevesebb extra terhelést helyezzük a grafikuskártyára? Ennek a problémának az egyik megoldása a normal mapping. Az eljárás alapja az, hogy az eddig használt normálvektorok helyett, melyekből felületenként csak egy volt megadva, minden ponthoz saját normálist rendeljünk. Mivel a beeső fény hatásait a normálisok segítségével számoltuk ki, ennek következtében a fény szemszögéből a normal map-el ellátott felület már nem lesz homogén, minden pontjánál többé-kevésbé eltérő beesési szöget kapunk eredményül. Normal mapping segítségével minimális teljesítmény csökkenés mellett bámulatos részletességet lehet elérni. Bár alapvetően egy textúra fájlban az egyes pontok színének értékeit tároljuk egy háromdimenziós vektorral, melyet a piros, zöld és kék színek értékei adnak meg, mivel a normálvektorok is háromdimenziós vektorok, a színvektorokat felhasználhatjuk a normálisok értékeinek tárolására. A normálisok minden esetben az adott felületre, illetve annak egy-egy pontjára merőlegesek, azaz minden esetben a Z irányú koordináta a domináns, amelynek a kék szín felel meg, ezért ezek a normal map-ek főleg kékes árnyalatú képek. A kéktől eltérő színek jelölik azokat a helyeket, ahol a normálisok jobban eltérnek a felületre merőleges vektorltól.
57
Modern grafikus effektek OpenGL környezetben
14. ábra: Textúra és a hozzá tartozó normal map
Ezeket a textúrákat ugyan úgy lehet feldolgozni, mint a korábbiakat, viszont fontos megemlíteni, hogy értékkonverzióra is szükség van, mivel a normálvektorok koordinátái egy [-1;1] intervallumon mozognak, míg a színvektoroké [0;1] intervallumon. Ennek megfelelően a textúrából kiolvasott vektorokat meg kell duplázni, majd az eredményt csökkenteni 1-el. Továbbá tudni kell, hogy az OpenGL Y koordinátái ellentétes irányúak, mint ahogyan a zöld szín értékei szerepelnek a képeken, emiatt a vektorok második értékét vagy a normal map generálásakor vagy annak felhasználása során a shaderben meg kell fordítani a helyes eredmények eléréséhez. Noha elméletileg ennyi elég ahhoz, hogy csupán egy normal mapet felhasználva részletgazdag felületeket jelenítsünk meg, hamar észrevehető, hogy az eljárás csak abban az esetben működik megfelelően, ha a felület síkja és a fényforrás, illetve a kamera merőlegesek egymásra. amint ezeket a tényezőket elkezdjük mozgatni, helytelen eredményeket kapunk. Ennek oka az, hogy a normal map által szolgáltatott vektorok mindig az OpenGL kontextusának Z tengelyén pozitív irányba mutatnak, amely mindig a monitorra merőleges irányt jelenti. Emiatt, ha maga az objektum felülete nem is párhuzamos a monitor síkjával, a normálisok továbbra is arrafelé állnak., tehát a konkrét felülettel bezárt szögük változik. Az említett problémát úgy lehet orvosolni, ha a normálvektorokat valahogyan áttranszformáljuk úgy, hogy minden esetben az adott felület szerinti sík Z tengelyén mutassanak a pozitív irányba. Ehhez szükségünk van a felület tangens vektora által meghatározott tangens terére. Ezt a mátrixot TBN, vagyis tangens-bitangensnormális mátrixnak hívjuk és segítségével nem csak a normálvektorokat, de minden vektort transzformálni tudunk az adott felület síkjába. Ezen mátrix meghatározásához először a felületenkénti tangenseket és normálisokat kell kiszámítani. Erre a feladatra létrehoztam az ObjectLoader osztályban egy calculateSurfaceNormal() és calculateTangents() függvényt. 58
Modern grafikus effektek OpenGL környezetben
A normálist számoló metódus a felületet határoló három csúcspont koordinátáit felhasználva számítja ki a normálvektor valódi értékét, mivel, bár a Blenderből exportált .obj fájl tartalmazza ezeket a vektorokat, a fejlesztés során feltűnt, hogy ezek nem minden esetben pontosak, érdemes őket újraszámoltatni. A tangenseket számító függvény a felületeket határoló három csúcspont és a felülethez kötött textúra koordináták segítségével határozza meg azt a vektort, amely a hagyományos, jobbsodrású koordinátarendszer esetén az X tengely pozitív irányába mutatna, valamint azt a bitangens vektort, amely az Y tengelyen hasonlóképpen helyezkedne el. A vertex shaderben ezt a három vektort megszorozva a gl_Normal által szolgáltatott normálmátrixal létrehozhatunk egy 3x3-as mátrixok, melynek transzponáltja már alkalmas a felületek tangens terébe való transzformálásainak elvégzésére. Végső lépésként az így kapott TBN mátrix segítségével mind a textúrákból olvasott normálvektorokat, mind a fényforrások, illetve a kamera hely- és irányvektorait át kell helyezni a tangenstérbe. A végső színvektor meghatározásához a különféle fények számítási módszereit nem kell megváltoztatni, csupán az így kapott új vektorokat kell felhasználni az eredetiek helyett.
15. ábra: Normal mapping-el szimulált egyenetlen felület, diffuse textúra nélkül
59
Modern grafikus effektek OpenGL környezetben
16. ábra: Normal mapping-el szimulált egyenetlen felület diffuse textúrával
60
Modern grafikus effektek OpenGL környezetben
5.12. Parallax mapping (látószög elhajlási térkép) A módszer alapját ugyanazon elv adja, mint a specular és normal map esetén, azaz egy újabb textúra segítségével tárolunk extra információkat a modell felületéről. A parallax mapping elsődleges célja, hogy egyfajta mélységérzetet teremtsen a kétdimenziós felületeken. Ugyan a normal mappignek is nagyjából ez a célja, ez az eljárás sokkal jobban szimulálja a mélységet és a kettőt kombinálva nagyon realisztikusnak ható eredményeket lehet elérni. Az eljárás a displacemnet (áthelyezés) mapping technikák családjába tartozik, amelyek egy textúrában letárolt geometriai információk segítségével számított offset értékkel elmozdítják a vertexeket. Az ehhez szükséges speciális textúra azt mondja meg a programnak, hogy a kép adott értékeihez tartozó pontok milyen magasan helyezkednek el a textúra síkjához képest. Az ilyen képfájlt height mapnek hívjuk és a specular map-hez hasonlóan fekete-fehér árnyalatú, ahol a fehér a legfelső, a fekete a legalsó értékeket jelöli.
17. ábra: Textúra és a hozzá tartozó depth map
Ez az alap elképzelés azt kívánja meg, hogy az adott felület, bár alaphelyzetben egy sima kétdimenziós „lapot” jelent, mégis a lehető legtöbb vertex építse azt fel, hogy a textúra által megkövetelt elmozdulásokat a lehető legnagyobb részletességgel lehessen követni. A csúcspontok számának növekedése azonban, mint már említettem, jelentős teljesítménycsökkenést okozhat, ezért cél, hogy a felület a lehető legkevesebb sokszögből épüljön fel, mégis működjön az eljárás. A megoldás, hogy úgy módosítjuk a textúra koordinátákat, a kamera és a pont közötti irányvektor és a height map felhasználásával, hogy úgy tűnjön, mintha az az eredeti helyéhez képest előrébb vagy hátrébb helyezkedne el. A működése megértésében segít a 16. ábra. 61
Modern grafikus effektek OpenGL környezetben
18. ábra: A kamera által ténylegesen látott „A” pont és a vertexek eltolásával kapott új felületen megjelenő „B” pont
Az ábrán látható piros alakzat jelképezi azt a felületet, ahogyan a részletesen kidolgozott tégláknak meg kellene jelenniük, míg a vastag fekete vonal jelöli, hogy valójában milyen felület van meghatározva. A „V” vektor jelöli a pontból a kamerába mutató irányvektort. Abban az esetben, ha a felület valóban rendelkezne vertexek eltolásával, akkor a néző annak a „B”-vel jelölt pontját látná. Mivel azonban nincs tényleges eltolás megvalósítva, a „V” vektor a felületet az „A” pontban metszi. A parallax mapping ezért azt igyekszik elérni, hogy az „A” pontban található textúra koordinátákat úgy tolja el, hogy a „B”-beli koordinátákat kapjuk eredményül, így azt szimulálva, mintha a néző tényleg a „B” pontot látná. Ahhoz, hogy ezt az eredményt elérjük, meg kell tudjuk határozni a „B”-beli koordinátákat az „A” pontéból kiindulva. Az egyik leggyakrabban használt megoldás erre a problémára, ha a „V” vektort az „A” pontbeli magassággal skálázzuk. Azaz, a „V” hosszát egyenlővé tesszük azzal az értékkel, amely a height map „A”-val megegyező pontjában van letárolva. A 7. ábrán látható az így létrehozott „P” vektor. Végül ennek a „P” vektornak meghatározzuk a vetületét a felület síkjára és az eredeti textúra koordinátát ebbe az új pontba toljuk el, melyet az „X” jelöl. Mint az ábrán látható is, ez egy elég jó közelítést ad, így meglehetősen valóságos eredményeket lehet vele elérni.
62
Modern grafikus effektek OpenGL környezetben
19. ábra
Megjegyzés: Mivel egyszerűbb mélységet szimulálni, mint kitüremkedést, ezért a tényleges eljárás során a már említett depth map-et használjuk, ami miatt az „A” pontot nem az alsó, legmélyebb, hanem a felső, legmagasabb szinthez tartozó síkon jelöljük ki. Az „A” pont emiatt az ábra szerint a „B” ponttól jobbra és felette fog elhelyezkedni, a „P” vektor pedig ellentétes irányú. Persze ez az eljárás egy kifejezetten durva közelítéssel szolgál, így olyan felületek esetén, amelyeken gyakoriak és nagyon meredekek a szintkülönbségek, az így számolt „P” vektor nem mindig kerül elég közel a „B” ponthoz, ami nem realisztikus megjelenítéseket eredményez. Egy másik probléma, amire oda kell figyelni, hogy a modellek elforgatásával nehézkessé válik a helyes koordináták meghatározása, mivel a „P” vektor X és Y koordinátái ilyen esetekben nem esnek egybe a felület síkjának azonos tengelyeivel. Szerencsére erre a problémára is megoldást jelent az előző fejezetben ismertetett TBN mátrix számítása és használata. A fentebb leírt eljárást a fragment shader-ben implementáltam, hiszen itt is, mint a normal mapping esetén, minden egyes pontra külön-külön meg kell határozni az eltolás értékét és irányát, hogy a lehető legjobb eredményt érjük el. Az ezen feladatokat elvégző függvény, a ParallaxMapping(), mely egy textúra koordinátát és egy nézeti irányvektort vár és az eltolt koordinátákat adja vissza, melyeket aztán felhasználok mind a textúra, mind a normal map feldolgozása során. Ennek köszönhetően nem csak a mélység látszata valósítható meg, de az ehhez illő tükröződések is szépen szimulálhatóak. 63
Modern grafikus effektek OpenGL környezetben
Az első megvalósítás által elért eredmények a 18. ábrán láthatóak.
20. ábra
Mint az az ábra is jól mutatja, jelentősen javult a modell részletessége, azonban több probléma is felmerült. Az egyik, hogy az eltolt koordináták által szimulált, mélységgel rendelkező felület széleinél látható, hogy azokon a területeken valamiért nem működik az eljárás. Ennek oka, hogy a széleknél az eltolási értékek kikerülhetnek a [0;1] intervallumból, ami téves eredményeket ad. Szerencsére ennek a problémának meglehetősen egyszerű a megoldása, csupán meg kellett vizsgálnom, hogy az adott, eltolt koordináta X és Y értéke az említett intervallumon belül található-e és amennyiben ez nem így van, egy egyszerű discard-al figyelmen kívül kellett azt hagynom: if(uv.x > 1.0 || uv.y > 1.0 || uv.x < 0.0 || uv.y < 0.0){ discard; } Ennek a minimális módosításnak köszönhetően már, a 19. ábrán is látható eredményt kaptam.
64
Modern grafikus effektek OpenGL környezetben
21. ábra
A másik gondot a már említett, nagy meredekségű területek okozták, a „P” vektor pontatlansága miatt. Ennek kiküszöbölésére az úgynevezett steep (meredek) parallax mapping kiegészítést használtam. Lényege, hogy a „P” meghatározásához nem csak egyetlen mintát használ. Első lépésként felosztjuk a teljes mélységet egyenlő részekre. Minél több részre osztjuk, annál pontosabb és ezáltal valósághűbb eredményeket kapunk, viszont annál több számítást is kell végezni, ezért fontos, hogy megtaláljuk a megfelelő egyensúlyt a minőség és a teljesítmény között. Ezután minden egyes így létrehozott szinten megvizsgáljuk a mélységtextúrában tárolt értéket a „P” vektor mentén egészen addig, amíg olyat nem találunk, amely az aktuális szint értékénél nagyobb. Ennek megértését a 20. ábra segíti.
65
Modern grafikus effektek OpenGL környezetben
22. ábra
Mint az ábrán is látszik, a 0.2-es mélységszint és a „P” vektor metszeténél található „T1” vonal által meghatározott textúra koordináta értéke körülbelül 0.8, tehát ez még nem a jó érték. A következő szinten viszont már 0.3 körüli az érték, aminél nagyobb a szint 0.4 értéke, tehát ezt fogjuk használni. Már ezen a vázlaton is jól látni, hogy mennyivel közelebb került az érték a „B” ponthoz. Szerencsére ennek a kisebb módosításnak a programozása sem volt nagy feladat, hiszen csak megkellett határozni a szintek kívánt számát, majd egy egyszerű while ciklussal megkeresni a megfelelő szint-érték párost. A feljavított parallax mappingel elért eredményeket a 21. ábra szemlélteti.
66
Modern grafikus effektek OpenGL környezetben
23. ábra
Végezetül, mint az az előző ábrán is látszik, a szintenkénti mintavételezés legnagyobb hátulütője, hogy aliasing jelenséget idéz elő és minél meredekebb szögből tekintünk a felületre, annál jobban kivehetőek a rétegek. Erre két megoldás is létezik, a Reliefe Parallax Mapping és a Parallax Occlusion Mapping. A kettő közül az első szolgáltat pontosabb értékekkel, azonban, mivel jóval nagyobb a teljesítmény igénye és mivel olyan jelentős különbség azért nincs közöttük, a második eljárás a kedveltebb, ezért én is azt választottam. Az eljárás alapja, hogy amikor megtaláljuk a steep parallax mapping-el a megfelelő szintet, nem használjuk fel azonnal a hozzá tartozó textúra koordinátát, hanem vesszük annak és azt megelőző szinthez tartozónak a lineáris interpolációját. Ez a megközelítés jelentősen növeli az algoritmus pontosságát, miközben a lineáris interpoláció nem terheli meg jelentősen a rendszert, így a teljesítmény sem romlik olyan nagymértékben. A fentebb ismertetett eljárásokkal kiegészített, végső parallax mapping eljárással megjelenített objektum a 22. ábrán látható.
67
Modern grafikus effektek OpenGL környezetben
24. ábra
68
Modern grafikus effektek OpenGL környezetben
5.13. A végső alkalmazás rövid ismertetése A demo programot futtatva egy előre beállított modell-t kapunk, ami egy úgynevezett „cornell box”, vagyis egy egyik oldalán nyitott doboz, amelyben bizonyos objektumok találhatóak. Gyakran használják fényeffektusok bemutatásához. A manapság már szokásosnak nevezhető „W”, „A”, „S” és „D” gombokkal lehet a kamerát mozgatni, illetve, a jobb-egérgombot lenyomva tartva az egérrel lehet azt forgatni. Ezen felül minden egyéb lehetőség elérhető az AntTweakbar segítségével létrehozott minimális GUI-n, ami az alábbi ábrán látható.
25. ábra: Az AntTweakbar-ral létrehozott GUI és „Help” A menüelemek közül a „Toggle ***” feliratúakkal lehet az egyes fényforrásokat, valamint a specular, normal és parallax mappinget ki-, illetve bekapcsolni. A „*** color” elemek kibonthatóak és az alattuk található 3-3 beviteli mező, valamint a már említett RotoSlider segítségével a fényforrások színeinek piros, zöld és kék értékei állíthatóak. Végül, az előzőhöz hasonlóan a „*** position” sorok is lenyithatóak és a fényforrások X, Y és Z koordinátáit lehet módosítani, mozgatva ezzel őket a háromdimenziós térben. A „főmenü” mellett található „Help” ablak rövid leírásokat tartalmaz az egyes menüpontokról és a hozzájuk tartozó gyorsbillentyűkről. Végül, az alkalmazás ablakának bal alsó sarkában található két ikon segítségével lehet a tweakbar-t és a help-et eltűntetni, illetve megjeleníteni igény szerint. A program bezárható az ablak „X” ikonjára kattintva, de az Escape billentyűhöz is hozzá van kötve ez az esemény, ezért az is használható ugyanerre a célra. 69
Modern grafikus effektek OpenGL környezetben
6. Összegzés A feladatom végére érve úgy érzem, kellőképpen elsajátíthattam a játékfejlesztés, azon belül is a háromdimenziós grafika OpenGL-el való programozásának alapjait. Remek lehetőség volt ez számomra, hogy beleláthassak a folyamatok mélyebb részleteibe is, valamint hogy olyan technológiákat ismerjek meg, amelyek a mai modern játékiparban használt megoldások elődjeként, azok alapjaiként szolgáltak. És bár ezen megoldások egy részét elavultnak nevezhetjük, segítségükkel úgy érzem, részletesebb háttér információkkal gazdagodtam, mintha egy modern, magasabb szintű megoldást választottam volna. Itt említeném meg, hogy a fejlesztés kezdetétől fogva a GLSL 1.30-as verzióját használtam, ami messze elmarad a legmodernebb 4-eshez képest, ami miatt bizonyos, manapság alapvetőnek számító feladat elvégzése jelentősen bonyolódott, ugyanakkor szerintem alaposabban megértettem az olyan folyamatok működését, mint az alap vertex, normál és textúra bufferektől eltérő, saját buffertípusok adatainak átvitele a shaderprogramba. Az említett feladat manapság a GLSL layout-ok használatával sokkal rugalmasabb, hiszen beállítható, hányadik csatolt bufferből kell a változó értéket kiszednie a programnak, így viszont nekem kellett a megfelelő helyre küldeni azokat. A korai verziók használata mellett úgy gondolom, a legjelentősebb problémát egyrészt a segédkönyvtárak telepítés, valamint az OepnGL debugolási folyamatok jelentették. Mivel korábban a legnépszerűbbnek nevezhető Eclipse, NetBeans vagy éppen Visual Studio fejlesztőeszközöket használtam, megkellett ismerkednem a segédkönyvtárak Code::Blocks féle integrálási módjával, amely elsőre kicsit zavaros volt, főleg a dolgozatban már említett okok miatt, mint hogy a könyvtárak telepítését két külön helyen is el lehet végezni, aminek más-más eredménye lesz. Viszont, mint az összes többi, a fejlesztés során felmerült probléma is, annak ellenére, hogy kezdetben sok időt és lendületet elvett, sokkal alaposabban megismertette velem a Code::Blocks-ot, így, ha a későbbikben is ezt kellene használnom, már sokkal gördülékenyebben mennének a dolgok. Ami a debugolási, hibakeresési folyamatokat illeti, rengeteg olyan eljárást és főleg gondolkodási módot megismertem, amik segítségével még úgy is képes vagyok ellenőrizni a kódot, ha azt semmilyen extra grafikus vagy éppen parancssori felület nem teszi lehetővé. Így például a C++ specifikus hibákról bár elég részletes leírást ad a Code::Blocks fordítója, mint egyik debug eszköz, ez sem tökéletes. Annak érdekében, hogy azokat az információkat, amik nekem kellettek a programozás során biztosan megkapjam, létrehoztam egy egyszerű segédosztályt, ami egy külső, dedikált szöveges állományba rögzítette a program futása során bekövetkezett fontosabb eseményeket, adatokat.
70
Modern grafikus effektek OpenGL környezetben
A másik eljárás, amit különösen hasznosnak ítéltem meg, a shaderek debugolásához kapcsolód „színes debugolás”. Mivel a shaderek változóinak értéke semmilyen módon nem kérhetőek le a C++ alkalmazás által, szükségem volt egy alternatív megoldásra, hogy azokat mégis le tudjam ellenőrizni. Noha a gDEBugger ebben nagy segítségemre volt, voltak esetek, illetve változó típusok, amiknél hibásan, esetenként egyáltalán nem adta vissza a shaderprogramok változóinak aktuális értékét. Hosszas keresgélés után akadtam rá az említett megoldásra, melynek lényege, hogy azon változó vagy változók értékeit, amelyeket le szeretnénk ellenőrizni felhasználjuk valamilyen formában a végső színek meghatározásakor. Ezzel a módszerrel például remekül meg lehet jeleníteni a felületekhez tartozó normálvektorok értékeit, hiszen azok is, csakúgy, mint a színvektorok, háromdimenziósak, ezért felhasználhatóak azok helyett. Így könnyen megállapítható, hogy egy vektor, melynek koordinátái (0,1,0) valóban jó irányba mutat-e, hiszen, ha a hozzá tartozó felület nem zöld, akkor biztosan hiba történt. Nehézséget jelentett még az optimalizáció. Noha a programom meglehetősen kicsinek számít ezen a területen, a fejlesztés során még így is belefutottam olyan esetekbe, amikor az alkalmazás azt csinálta, amit elvártam tőle, de kifejezetten lassan, az erőforrásokat túlságosan megterhelve. Erre egy jó példa a tangensvektorok számítása. Mivel ez a művelet sok számolást igényel, a poligonok számának növekedésével meredeken nőtt az alkalmazás inicializációs ideje, ezért, mivel ezekre csak akkor van szükség, ha az adott felület anyagához van hozzárendelve normal és/vagy parallax map, azok esetében, ahol ez hiányzik, nem végeztetem el feleslegesen. Bár az említett példa nem tűnik jelentősnek és valóban nem hozott egetrengető eredményeket, mégis belekóstolhattam az optimalizáció alapjaiba és egy újabb gondolkodásmóddal gazdagodtam, amely remélhetőleg nagy hasznomra lesz a jövőben. Azonban nem csak a rengeteg elsajátított technológia és technika miatt érzem sikeresnek a projektemet, hanem azért is, mert bár a rengeteg grafikus effektből és játékfejlesztési irányelvből meglehetősen keveset tudtam magam implementálni, azok meglátásom szerint megfelelő minőségűek lettek, de legalábbis saját elvárásaimnak megfelelnek. Továbbá, köszönhetően annak, hogy már a kezdetektől fogva ügyeltem a platformfüggetlenségre, valamint arra, hogy egy dinamikusan használható grafikus motor alapjait hozzam létre, nagyon valószínű, hogy bizonyos részeit modernizálva a későbbiekben tovább fogom fejleszteni és remélhetőleg egyszer egy tényleges játékot csinálhatok a segítségével. Ennek megfelelően bőven van mód a bővítésre és a módosításokra is. Elsősorban, amit kijavítanék az alkalmazásban, az az elavult, de legalábbis réginek nevezhető technológiák használata. Ezek ugyanis, mint már említettem, jobb rálátást biztosítottak a folyamatokra, a modern játékok esetén már nem olyan hatékonyak, nehézkesebbé válik a fejlesztés és az optimalizálás is.
71
Modern grafikus effektek OpenGL környezetben
Az implementált effektek közé mindenképpen szeretném betenni az árnyékolást és a tényleges tükröződéseket is, de ezeken felül is találni még rengeteg technikát, mint a gázok, folyadékok szimulációja vagy a speciális shading eljárások, mint a cel shading, amely képregényekhez hasonló stílusban jeleníti meg a modelleket, ezáltal kevésbé realisztikus, de sokkal egyedibb megjelenést adva a játéknak. További hibaként, hiányosságként említeném meg, hogy semmilyen game logic nem lett implementálva, mint például az ütközés érzékelés vagy valamilyen mesterséges intelligencia. Ez azonban azért nem olyan jelentős probléma, mert a dolgozatom elején említett korábbi fejlesztéseimnek hála ezeken a területeken is van már több-kevesebb tapasztalatom, így nem kell a jövőbeni játékfejlesztési feladatoknak ilyen szempontból sem tudás nélkül nekivágnom. Dolgozatom lezárásaként, röviden összefoglalva abszolút pozitívan értékelném az elvégzett munkát, amivel elégedett vagyok –és nagyon örülök, hogy ezt a témát választottam. Legvégül szeretném megköszönni konzulensem, Dr. Mileff Péter fáradságos munkáját. Köszönhetően szerteágazó tapasztalatainak és a ténynek, hogy jóformán éjjel-nappal elérhető volt, rengeteget segített a munkám során, ami nélkül nem hiszem, hogy időre elkészültem volna az alkalmazásommal.
72
Modern grafikus effektek OpenGL környezetben
7.Summary Looking back to the project and the achieved goals I consider the whole thesis a success. Thanks to the many technologies and techniques I used during the development of my application, I have learnt so much about game development and especially OpenGL 3D programming. I have chosen to use older versions of libraries, such as OpenGL of which I used the version 1.30 shader language instead of the most recent 4th version. This however made my job somewhat more difficult, since I dind’t have the comfortable new features, I was able to see the flow of things behind the scenes in more details and therefore I think I have more relevant, more detailed knowledge I will be able to use in the future. During the process I have faced som difficulties, some of which were easier to solve, while others meant a greater challenge. But, on the one hand I had some amazing free-to-use programs that helped me solve many problems, such as the gDEBugger which made debugging my OpenGL application far more easire or the libraries, like AntTweakbar that made it unnecessary to implement my own graphical user interface. On the other hand, thanks to these challenges I now have a different view and sense of mind regarding 3D application development. To give an example I would highlight the so called „color debugging” which is used during the development of shader programs. The idea behind it is to give some kind of feedback about the shaders’ variables, however there is no traditional way of getting this information out of them. This means, there is no option to use the main program to ask for some values and show them on a commandline, only to send data into the shaders. In order to check the values we can use the mentioned technique, which is all about using the right variable’s value as the output color for a vertex or fragment instead of any other computed values. Or I could mention the Code::Blocks IDE which caused many difficulties at the and, since I was not familiar with it at all. The biggest challenge was to correctly install and link external libraries since there are two ways to do this and there is a significant difference between the two in terms of portability. But running into these problems and finding the right solutions gave me one more layer of knowledge I am grateful for. One more thing I feel like a huge experience, was the chance to do some optimization. However my apllication is far less complex than a real game or game engine I still had to do some minor changes during the development, so my demo has better performance. These showed me the base ideas about all these tasks and therefor the right kind of thinking in these situations.
73
Modern grafikus effektek OpenGL környezetben
Beside the many techniques and paradigms I have learnt I feel I have achieved th eset goals, but at least my own ones. It is true, there are many other techniques I could use and I mainly implemented graphic functions, not the so called „game logics”, I think, what I was able to include in my application are all have satisfying results. Of course, my engine is far from complete. Because of this, there are many options to improve and expand it. For example I really want to include shadows and real reflections in the future. Also, there are things, like liquid and gas simulation or actual game logics, like collision detection or AI I haven’t studied int the process, but thanks to my former attempts to create some basic 2D games, I have already known more or less about these topics. Thanks to the fact that I was trying to create a cross-platform application from the start with the goal in mind for it to be dynamic, by applying a few changes, like using OpenGL GLSL 4 instead of the current version, I will be able to use and expand this base application in the future and maybe create a real game with it. To end my dissertation I would simply like to say that in total I am satisfied with my results and very glad I have chosen this topic as it gave me invaluable new knowledge for my future.
74
Modern grafikus effektek OpenGL környezetben
8.
Irodalomjegyzék
[1] C nyelv: http://www.tutorialspoint.com/cprogramming/c_overview.htm [2] C nyelv: https://en.wikipedia.org/wiki/C_(programming_language) [3] C++ nyelv: http://www.cplusplus.com/info/description/ [4] Stanley B. Lippman, Josée Lajoie, Barbara Moo - C++ Primer (5th edition), 2008 [5] Joseph Albahari, Ben Albahari - C# 6.0. in a Nutshell: The Definitive Reference, 2015 [6] OpenGL: https://www.opengl.org/about/ [7] Edward Angel, Dave Shreiner - Interactive Computer Graphics: A top-down Approach with Shader-Based OpenGL, 2011 [8] Stefan Zerbst - Direct3D and 3D Engine Programming, 2006 [9] Codeblocks: http://wiki.codeblocks.org/index.php/Main_Page [10] DevCpp: https://en.wikipedia.org/wiki/Dev-C%2B%2B [11] Visual C: https://en.wikipedia.org/wiki/Visual_C%2B%2B [12] Autodesk Maya: http://www.edulearn.com/article/what_is_autodesk_maya.html [13] Autodesk Maya: https://en.wikipedia.org/wiki/Autodesk_Maya [14] Blender: https://www.blender.org/about/ [15] SDL: https://wiki.libsdl.org/FrontPage [16] GLUT: https://www.opengl.org/resources/libraries/glut/ [17] GLFW: https://en.wikipedia.org/wiki/GLFW [18] CEGUI: http://cegui.org.uk/wiki/ [19] AntTweakbar: http://anttweakbar.sourceforge.net/doc/
A jegyzékben feltüntetett webes hivatkozások utolsó megtekintésének ideje 2016.április 28.
75