A WORLDFORGE EMBER KLIENS GRAFIKAI ALRENDSZERÉNEK TOVÁBBFEJLESZTÉSEE



Hasonló dokumentumok
Transzformációk. Grafikus játékok fejlesztése Szécsi László t05-transform

Információ megjelenítés Számítógépes ábrázolás. Dr. Iványi Péter

Láthatósági kérdések

Információ megjelenítés Számítógépes ábrázolás. Dr. Iványi Péter

1. Bevezetés 1. Köszönetnyilvánítás A számítógépes játékfejlesztésről 3

Plakátok, részecskerendszerek. Szécsi László

HLSL programozás. Grafikus játékok fejlesztése Szécsi László t06-hlsl

Transzformációk. Szécsi László

GráfRajz fejlesztői dokumentáció

Feladatok. Tervek alapján látvány terv készítése. Irodai munka Test modellezés. Létező objektum számítógépes modelljének elkészítése

Számítógépes grafika

Árnyalás, env mapping. Szécsi László 3D Grafikus Rendszerek 3. labor

Számítógépes Graka - 4. Gyak

OpenCL alapú eszközök verifikációja és validációja a gyakorlatban

x = cos αx sin αy y = sin αx + cos αy 2. Mi a X/Y/Z tengely körüli forgatás transzformációs mátrixa 3D-ben?

GPU Lab. 14. fejezet. OpenCL textúra használat. Grafikus Processzorok Tudományos Célú Programozása. Berényi Dániel Nagy-Egri Máté Ferenc

A játékfejlesztés több területből áll. A kódolás csupán egy része a munkáknak.

Számítógépes Grafika mintafeladatok

Az ErdaGIS térinformatikai keretrendszer

Földmérési és Távérzékelési Intézet

SZE, Doktori Iskola. Számítógépes grafikai algoritmusok. Összeállította: Dr. Gáspár Csaba. Felületmegjelenítés

Grafikus csővezeték 1 / 44

Orvosi készülékekben használható modern fejlesztési technológiák lehetőségeinek vizsgálata

(Solid modeling, Geometric modeling) Testmodell: egy létező vagy elképzelt objektum digitális reprezentációja.

Direct3D pipeline. Grafikus játékok fejlesztése Szécsi László t03-pipeline

SZOFTVERES SZEMLÉLTETÉS A MESTERSÉGES INTELLIGENCIA OKTATÁSÁBAN _ Jeszenszky Péter Debreceni Egyetem, Informatikai Kar jeszenszky.peter@inf.unideb.

Számítógépes Hálózatok. 7. gyakorlat

Elengedhetetlen a játékokban, mozi produkciós eszközökben Nélküle kvantum hatás lép fel. Az objektumok áthaladnak a többi objektumon

Textúrák. Szécsi László

Készítette:

Maga a tématerület így nagyon nagy. A fények pontos fizikai szimulációja kimondottan számításigényes

Játékfejlesztés a Windows Phone 7 erejével

Szegedi Tudományegyetem Informatikai Tanszékcsoport SZAKDOLGOZAT. Fertői Ferenc

Használati Útmutató. KeyShot alapok

Termék modell. Definíció:

A bemutatott példa a Phong modell egy egyszerűsített változatát alkalmazza a Blinn-Phong-féle megközelítést

WordPress segédlet. Bevezető. Letöltés. Telepítés

Flash és PHP kommunikáció. Web Konferencia 2007 Ferencz Tamás Jasmin Media Group Kft

A NetBeans IDE Ubuntu Linux operációs rendszeren

Ficsor Lajos Általános Informatikai Tanszék Miskolci Egyetem

Az Ampère-Maxwell-féle gerjesztési törvény

14.2. OpenGL 3D: Mozgás a modellben

Geometriai és hullámoptika. Utolsó módosítás: május 10..

Mérnöki létesítmények geodéziája Mérnöki létesítmények valósághű modellezése, modellezési technikák, leíró nyelvek

Book Template Title. Author Last Name, Author First Name

Görbe- és felületmodellezés. Szplájnok Felületmodellezés

Véletlen szám generálás Labirintus felépítése 1x1-es felbontástól a teljes méretig

Navigációs GPS adatok kezelése QGIS programmal (1.4 verzió) Összeállította dr. Siki Zoltán

Renderelés megjelenésmódok, fények, anyagjellemzők

A Java EE 5 plattform

Hajder Levente 2017/2018. II. félév

Tartalom. Tartalom. Anyagok Fényforrás modellek. Hajder Levente Fényvisszaverési modellek. Színmodellek. 2017/2018. II.

HAWK-3. Az OMSZ saját fejlesztésű időjárási megjelenítő rendszere

Optika és Relativitáselmélet II. BsC fizikus hallgatóknak

Felhasználói kézikönyv - Android kliens

OPTIKA. Geometriai optika. Snellius Descartes-törvény szeptember 19. FIZIKA TÁVOKTATÁS

Operációs rendszerek. Az X Window rendszer

SDL_Universe SDL, C++, 3D szoftver renderelő

Verifikáció és validáció Általános bevezető

PLC Versenyfeladat. XIV. Országos Irányítástechnikai Programozó Verseny Budapest, március Összeállította az EvoPro Kft.

Microsoft SQL Server telepítése

Rubin SPIRIT TEST. Rubin firmware-ek és hardverek tesztelése esettanulmány V1.0. Készítette: Hajnali Krisztián Jóváhagyta: Varga József

A fény visszaverődése

Space Invaders Dokumenta cio

Választó lekérdezés létrehozása

NETinv. Új generációs informatikai és kommunikációs megoldások

1. tétel. A kommunikáció információelméleti modellje. Analóg és digitális mennyiségek. Az információ fogalma, egységei. Informatika érettségi (diák)

HVK Adminisztrátori használati útmutató

G Data MasterAdmin 9 0 _ 09 _ _ # r_ e p a P ch e T 1

Geometria megadása DXF fájl importálásából

[SZÁMÍTÓGÉP-HÁLÓZATOK]

OpenGL és a mátrixok

Lakóház tervezés ADT 3.3-al. Segédlet

Címtár Felhő Projektfeladat specifikáció

Sú gó az ASIR/PA IR Públikús felú lethez

QGIS gyakorló. --tulajdonságok--stílus fül--széthúzás a terjedelemre).

Sugárkövetési algoritmusok (2. rész)

HLSL programozás. Szécsi László

OpenOffice.org mint fejlesztési platform

Minden jog fenntartva, beleértve bárminemű sokszorosítás, másolás és közlés jogát is.

BARANGOLÁS AZ E-KÖNYVEK BIRODALMÁBAN Milyen legyen az elektonikus könyv?

Zimbra levelező rendszer

Digitális Technika. Dr. Oniga István Debreceni Egyetem, Informatikai Kar

DIGITÁLIS KÉPANALÍZIS KÉSZÍTETTE: KISS ALEXANDRA ELÉRHETŐSÉG:

Technikai információk fejlesztőknek

Modellek dokumentálása

Geometriai modellezés. Szécsi László

Struktúra nélküli adatszerkezetek

Az MTA Cloud a tudományos alkalmazások támogatására. Kacsuk Péter MTA SZTAKI

Máté: Számítógépes grafika alapjai

Algoritmusok bonyolultsága

OOP. Alapelvek Elek Tibor

Felhasználói leírás a DimNAV Server segédprogramhoz ( )

Operációs rendszerek. 9. gyakorlat. Reguláris kifejezések - alapok, BASH UNIVERSITAS SCIENTIARUM SZEGEDIENSIS UNIVERSITY OF SZEGED

Infobionika ROBOTIKA. X. Előadás. Robot manipulátorok II. Direkt és inverz kinematika. Készült a HEFOP P /1.0 projekt keretében

Országos Területrendezési Terv térképi mel ékleteinek WMS szolgáltatással történő elérése, Quantum GIS program alkalmazásával Útmutató 2010.

Készítette: Enisz Krisztián, Lugossy Balázs, Speiser Ferenc, Ughy Gergely

Informatika érettségi vizsga

Szilipet programok telepítése Hálózatos (kliens/szerver) telepítés Windows 7 operációs rendszer alatt

Miért érdemes váltani, mikor ezeket más szoftverek is tudják?

Átírás:

Budapesti Műszaki és Gazdaságtudományi Egyetem Villamosmérnöki és Informatikai Kar Szücs Péter A WORLDFORGE EMBER KLIENS GRAFIKAI ALRENDSZERÉNEK TOVÁBBFEJLESZTÉSEE KONZULENS Dr. Umenhoffer Tamás BUDAPEST, 2014

Tartalomjegyzék Összefoglaló... 5 Abstract... 6 1 Bevezetés... 7 1.1 Kiválasztott környezet... 8 1.1.1 WorldForge... 8 1.1.2 Az Ember kliens... 9 2 Water Shader... 17 2.1 Fénytani alapismeretek... 17 2.2 Fresnel egyenlet... 18 2.3 Hullámok... 19 2.4 Implementáció... 20 2.5 Végeredmény... 22 3 Light Indexed Deferred Rendering (LIDR)... 23 3.1 A LIDR Működése... 25 3.1.1 Depth prepass... 27 3.1.2 Light index mapping pass... 28 3.1.3 Forward rendering... 32 3.2 Végeredmény... 34 4 Hardware Skinning... 35 4.1 Karakter animáció... 35 4.2 Implementáció... 37 4.3 Végeredmény... 39 5 Mesh Redukció... 40 5.1 Görbület alapú súlyozás... 42 5.2 Négyzetes hiba alapú súlyozás... 42 5.3 Normálok bevonása súlyozásba... 44 5.4 Technikák a súlyozás továbbjavítására... 46 5.5 Mesh szintű redukció... 48 5.6 LOD tömörítés... 49 5.7 Háttérben való számítás... 49 5.8 Profiler... 50 5.9 Mesh külsejénél súlyozása... 50

5.9.1 Specifikáció... 51 5.9.2 Algoritmus összefoglaló... 51 5.9.3 Konvex burok meghatározása... 52 5.9.4 Lapok bejárása... 55 5.10 Autokonfigurálás... 57 5.10.1 Redukció mértékének meghatározása... 57 5.10.2 LOD megjelenítési távolság meghatározása... 58 5.11 Mesh LOD generátor használata... 59 5.12 Implementáció... 60 5.13 Végeredmény... 60 6 Mesh LOD eszközök... 62 6.1 OgreMeshUpgrader... 62 6.2 MeshLodEditor Ogre3D-ben... 63 7 Konklúzió... 64 Irodalomjegyzék... 66 Függelék... 70

HALLGATÓI NYILATKOZAT Alulírott Szücs Péter, szigorló hallgató kijelentem, hogy ezt a diplomatervet meg nem engedett segítség nélkül, saját magam készítettem, csak a megadott forrásokat (szakirodalom, eszközök stb.) használtam fel. Minden olyan részt, melyet szó szerint, vagy azonos értelemben, de átfogalmazva más forrásból átvettem, egyértelműen, a forrás megadásával megjelöltem. Hozzájárulok, hogy a jelen munkám alapadatait (szerző(k), cím, angol és magyar nyelvű tartalmi kivonat, készítés éve, konzulens(ek) neve) a BME VIK nyilvánosan hozzáférhető elektronikus formában, a munka teljes szövegét pedig az egyetem belső hálózatán keresztül (vagy hitelesített felhasználók számára) közzétegye. Kijelentem, hogy a benyújtott munka és annak elektronikus verziója megegyezik. Dékáni engedéllyel titkosított diplomatervek esetén a dolgozat szövege csak 3 év eltelte után válik hozzáférhetővé. Kelt: Budapest, 2014. 05. 23..... Szücs Péter

Összefoglaló Az elmúlt évtizedben a játékfejlesztésben jelentősen előtérbe kerültek az ún. MMORPG (Massively Multiplayer Online Role-Playing Game sok-szereplős online szerepjáték) játékok. Ezek a játékok szemben az egyszemélyes lokális gépeken futtatható játékokkal kizárólag interneten játszhatóak, és nagy játékközösséget szolgálnak ki. Ennek megfelelően speciális igényeket kell kielégíteni, úgy, mint nagy komplexitású színterek alkalmazása és sok animált karakter megjelenítése. Az MMORPG játékok évről-évre komplexebbek lesznek és ennek következtében a kezdeti sok szereplős piacon már csak pár nagyobb vállalat terméke maradt, amely termékek éves szinten dollár milliókat termelnek. A többszereplős piac folyamatos csökkenésének fő oka, a felhasználók igényeinek folyamatos bővülése, amely szempontot csak a magas minőségű, összetett rendszerek tudnak kielégíteni. Ez azt a következményt vonja magával, hogy a fejlesztési költségek az egekbe szöktek a minőségi MMORPG alkotásoknál. Az MMORPG játékfejlesztés területén is vannak azonban a nagyvállalatoktól független szereplők. A WorldForge [1] non-profit organizáció egy olyan nyílt forráskódú MMORPG játékmotor platformon dolgozik már 1998 óta, amely a jövőben egy platformot biztosíthat a nagy fejlesztési költségek leszorításában, teret engedve így a kisebb fejlesztő csapatoknak is. Sajnos az elmúlt 15 évben a keretrendszer grafikai alrendszerének egyes komponensei már elavultak a piacon található termékhez képest. Ezért a diplomamunkám keretein belül azt a célt tűztem ki, hogy a WorldForge Ember grafikai alrendszerének egyes komponenseit továbbfejlesszem, abban a reményben, hogy a szoftver így közelebb kerülhet a piacvezetők hasonló termékeihez. Dolgozatomban olyan problémákra kerestem a megoldásokat, melyek nagyobb jelentőséggel bírnak az MMO játékok területén. Ilyen problémák a nagyszámú fényforrások kezelése, karakter animáció teljesítményének javítása, magas komplexitású geometria kezelése.

Abstract Within the last decade, MMORPG (Massively Multiplayer Online Role-Playing Game) games have started to dominate the gaming industry over single player games. These games have gotten more and more complex with every year of development, which has limited the market to only a few big MMORPG games over time. Other games haven't been able to keep up with the continuously growing and challenging demands of users, despite attempts to diversify themselves from other games. These games have development costs so high that companies do not think they are worth investing in. In this challenging online market, there are still some projects independent from big companies, like WorldForge [1], which has been developing since 1998 on a free and open-source engine, making MMORPG development goals cheaper, simpler, and faster to achieve. In the past 15 years, however, the graphical front-end of the client hasn't been able to catch up with competitors. Therefore, in order to help WorldForge with their goals, I would like to bring the graphical front-end to the level of current generation games. In this thesis, I have researched and developed areas that have a massive impact on the MMO gaming experience. These include lighting dynamic scenes with large amount of light sources, improving the performance of animated characters, and handling large and complex geometries. 6

1 Bevezetés Mint minden játékfejlesztés iránt érdeklődő fejlesztő, én is fejlesztettem kisebb játékmotorokat és próbálkoztam MMORPG fejlesztésével is. Ezek a fejlesztések rendkívül hasznos tapasztalatokat adtak, az elkészült rendszerek azonban nem tudják felvenni a versenyt egy mai professzionális játékkal, melyek sok programozó több éves munkájából születnek. A kezdeti tapasztalatok után jutottam arra döntésre, hogy érdemes csatlakozni egy hosszabb ideje működő projekt fejlesztői közösségéhez. A WorldForge [1] project azzal tűnik ki a többi MMO motortól, hogy az egyik legrégebb óta fejlesztett nyílt forráskódú MMORPG a világon. Fő fejlesztési platformja a Linux, és már legtöbb Linux disztribúció csomagkezelőjéből elérhető a játékosoknak. Emellett futtatható Windows és OS X operációs rendszereken is. A projektben résztvevő fejlesztők és grafikusok segítőkészek, valamint hét éve Google Summer of Code programban is részt vesznek, amin keresztül minden évben újabb és újabb fejlesztők csatlakoznak a projekthez. Architektúrálisan a kliens és szerver közötti kommunikációs protokol szabványosítva van, így több különböző kliens (Equator [3], Sear [4] vagy Ember [5]) és szerver (Indri [6] vagy Cyphesis [7]) implementáció közül lehet választani. Manapság az Ember kliens és a Cyphesis szerver implementációk a jelentősebbek, a legnagyobb fejlesztések rájuk irányulnak. Mivel a WorldForge egy régóta aktívan fejlesztett, tiszta architektúrájú projekt, erre a rendszerre esett a választásom, és csatlakoztam a fejlesztők csapatához. A játékfejlesztés területén belül engem mindig jobban érdekeltek a játékszoftverek grafikai megvalósításai. Korábbi fejlesztéseimben is a grafikai komponenseket dolgoztam ki jobban, így a WorldForge projektben is a grafikai komponensek fejlesztésén dolgozom. Fontosnak tartottam, hogy ezen a téren a projekt fejlődjön, mivel a megvalósításai elavultnak számítanak egy mai professzionális játékhoz képest, és a grafikai látvány egy nagyon fontos szempont szokott lenni a játékok értékelésénél. Jelen dolgozat a WorldForge Ember klienséhez készített grafikai fejlesztéseimet tárgyalja. A Bevezetés fejezetben ismertetem a WorldForge MMORPG platformot és az Ember klienst, majd következő fejezetekben a fejlesztéseimről beszélek. A diplomamunkámat úgy terveztem meg, hogy minden fejezetet a többi fejezet ismerete nélkül is el lehessen sajátítani, ezzel segítve a diploma megérthetőségét, mivel alapos lineáris matematikai és 3D grafikai ismeretek szükségesek bizonyos részek megértéséhez. 7

1.1 Kiválasztott környezet A fejezet rövid áttekintést nyújt a WorldForge elmúlt évtizedekbeli munkájáról és az általuk készített Ember kliensről. 1.1.1 WorldForge A WorldForge organizáció 1998-ban jött létre, amelynek célja egy FOSS (Free and Open Source Software), egységes és univerzális MMORPG rendszer fejlesztése. 1998-2002-ig egy 2D RPG-t (Roll Playing Game) fejlesztettek, amelyet be is fejeztek, majd a gyűjtött tapasztalatokból nekiláttak egy 3D MMORPG motor fejlesztésének, amely több MMORPG játék alapja lesz. A nyílt forráskódú fejlesztéseknek rengeteg előnye van: -bárki továbbfejlesztheti a komponenseket; -tetszőleges külsős személyek is javításokat végezhetnek el a kód adatbázison; -megfelelő licenc alapján a cégek is érdekelté válhatnak a fejlesztésekben; -a vállalatok költségeket spórolhatnak meg a felhasználás során. Az organizáció nagyon összetartó, aktív levelezőlistát tartanak fent, ahol megbeszélik tapasztalataikat, fejlesztési terveiket. Ezeken a fórumokon rengeteg tapasztalatot szerezhetnek a fejlesztők, köztük én is. A fejlesztői csapaton kívül a projekt rendelkezik grafikus részleggel is, ahol modellezési, textúrázási munkákat végeznek. Ez azért is nagyon fontos, mert megfelelő tartalom nélkül nem lehet minőségi játékszoftvert fejleszteni. A fő modellezési eszköz a Blender [8], amely szintén ingyenes, nyílt forráskódú rendszer, de készítenek tartalmat más, a játékfejlesztésben elterjedt szoftverekkel is (például Maya és 3D Studio Max). Ezen kívül az OrchestraForge [9] részleg készít hang effekteket és zenéket is. Ezeket az audio és grafikai alkotásokat a WorldForge által fejlesztett Mason [10] és Deeds [11] világokhoz készítik, de vannak olyanok is, amelyek azért készülnek, hogy jövőbeni WorldForge alapú játékok felhasználhassák. Ezen kívül a teljesen nyílt licencszelésnek köszönhetően több, nem WorldForge alapú játékban és más játékmotorban is fel lettek használva, mint például Rise of Heroes, SumWars, jmonkey, EGO Game Editor, Rastullahs Lockenwickler. Valamint a közösséggel közösen fejlesztünk két világot is. Egyik a Mason, amely egy sandbox jellegű játék, ahol alapanyagokból eszközöket lehet létrehozni, majd az elkészített épületekből komplexebb konstrukciókat (mint például épületeket) és folyamatosan ezeket tovább lehet építeni. A másik pedig a Deeds, amely egy egyszerűbb hagyományos MMORPG-t céloz meg, amely alapot biztosít egy új világ létrehozásához. A két világ csak a szerver oldali szabályhalmazban (ruleset) tér el, amellyel a világ funkcionális működését lehet leírni. 8

1.1.2 Az Ember kliens Az Ember egy 3D kliens keretrendszer, amely egy stabil, moduláris, bővíthető és testreszabható alapot képez egy játék specifikus kliens létrehozásához. A WorldForge saját két játék specifikus világát támogatja: a Mason és a Deeds világokat módosítás nélkül. Rengeteg előnye van, ha erre építik a világot a játékvilág fejlesztők. Egyik előnye, hogy Windows, Linux, OpenBSD és OS X operációs rendszereken is működik, valamint nemsokára Android platformon is elérhető lesz. Teljes körű 3D világot biztosít Ogre3D [12] segítségével, amely objektum orientált és könnyen testreszabható rajzolási láncot (pipeline-t) és egyszerűen kezelhető material [13] leíró rendszert nyújt. Könnyen használható és szerkeszthető GUI rendszert is biztosít CEGUI [14] segítségével, amelynek a kezelése akár teljesen Lua [15] szkript nyelvből is megoldható. Emellett komplex vizuális modelleket, csontvázakat, animációkat, fényforrásokat, részecske rendszereket is le lehet írni XML-ben az entititások reprezentációja érdekében. Egy fejlett kliens-szerver kommunikáció támogatja. Valamint a kényelmes világ fejlesztési élmény érdekében integrált eszközöket tartalmaz, mint például a Terrain Editor, Assets Editor, Model Editor, Entity Creator, Entity Inspector, stb. A következő alfejezetekben ismertetem a belső és külső függőségeit (1. ábra), Ember komponenseit és az OgreView komponens részeit, hogy a témában jártasabb olvasó képes legyen elképzelni, hogy körülbelül miről is van szó és mit fejlesztettem tovább. Ez 46 elem listaszerű ismertetését magába foglalja, amely az olvasó számára egy támpontot ad, ha mélyebben bele szeretne nézni az Ember kliensbe. Mivel egy komplex MMORPG-ről van szó, egy külön könyvet lehetne írni ezekről az alfejezetekről, így nem szükséges ennek ismerete a későbbi fejezetekhez. 1. ábra: Ember fő függőségei 9

1.1.2.1 Az Ember kliens külső függőségei Külső függőségnek nevezem, azokat a könyvtárakat (library), amelyeket az Ember használ, de nem a WorldForge organizáció fejleszt. A következő listában röviden ismertetem Ember külső függőségeit. Amennyiben az olvasót részletesebben érdekelné egy adott függőség, kérem, tekintse meg az elhelyezett hivatkozást: Ogre3D [12]: Biztosítja a grafikai megjelenítést. CEGUI [14]: Biztosítja a grafikus felhasználói felületi elemeket (GUI). Caelum [16]: Grafikai komponens, mely biztosítja a környezet megvilágítását és az időtől függő égboltot. Például ha szerveren este van, akkor sötét van minden kliensnél és ugyanazt a csillag elhelyezkedést látják az égbolton. PagedGeometry [17]: Batch-elt (egy geometriába egyesített) fa és növényzet rajzolást biztosít. Távoli fákat pedig billboard-ként (2D képként) jelenít meg. OpenAL [18]: Biztosítja az hang kimenteteket effekteknek és zenének. Boost [19]: Biztosítja a socket-et, szálkezelést, és smart pointer-t. libsigc++ [20]: Biztosítja a signals&slots pattern (Observer) alapú kommunikációt az Ember kliensen belül. SDL [21]: Biztosítja a bemeneti eszközök kezelését: egeret és billentyűzetet. Lua [15]: Kliensben közvetlen szkriptelést tesz lehetővé. A teljes GUI megjelenítés Lua scriptben van írva. tolua++ [22]: C++ header-hez hasonló (*.pkg) fájlokból automatikusan generál Lua binding-et, így Lua-ból is lehet C++ osztályok példányait létrehozni és hívni. autotools [23]: Ez biztosítja a platform független fordítást az egyes operációs rendszereken (Windows, Linux, OS X). 10

1.1.2.2 Az Ember Kliens Belső Függőségei Belső függőségnek nevezem, azokat a könyvtárakat, amelyeket az Ember használ és a WorldForge organizáció fejleszt. Ezek a következők: Atlas-cpp [24]: A WorldForge egy Atlas nevű szabványos XML formátumot definiál a kommunikációhoz kliens-szerver között, amelyet az Atlas-cpp könyvtár objektum-orientáltan biztosít. Minden egyes beérkező XML parancshoz társít egy Atlas::Object-ből leszármaztatott osztályt, amely leírja a parancs funkcionalitását, így elintézi nekünk az XML feldolgozását. Létezik hozzá Java, Python és Lua keretrendszer is. SkStream [25]: A TCP/IP protokollon keresztüli folyamot (stream) biztosítja. Eris: Az Atlas-cpp könyvtárra épülő Eris egy magas szintű objektum-orientált világ nézetet biztosít (Például melyik objektum hol van, milyen jellemzőkkel rendelkezik és mit csinál). Mercator [27]: A Mercator biztosítja az automatikus terrain és növényzet generálást. Minden egyes blokknak meg van adva a mercator paraméter listája, amit a szervertől (például a négy sarkának a magassága, egy véletlen szám generátor száma, milyen sűrű legyen a növényzet, stb.), ami alapján generál. Ezáltal nagyon kevés paramétert kell átadni a szervernek a kliensekhez, hogy a növényzeteket, mint például füvet és domborzatot ugyanúgy megjelenjen összes kliensnél. WFMath [28]: Egy általános grafikai műveletekre (lineáris algebra) specializálódott matematikai könyvtár. Minimális ütközésdetektálást is tartalmaz. Varconf [29]: A konfigurációs fájlok input és output műveletek menedzselését végzi. libwfut [30]: Távoli media fájlok letöltését és szinkronizálását biztosítja. 11

1.1.2.3 Az Ember Kliens Komponensei Az Ember kliens több alkomponensből áll, melyek külön statikus könyvtárakként jelennek meg. Ember komponenseinek nevezem az Ember kódbázisában található statikus könyvtárakat. A legnagyobb és legkomplexebb része az OgreView, amely a komplett színtér megjelenítését biztosítja. Az Ember tartalmaz egy módosított színtér gráf kezelő rendszert (SceneManagert), amely dinamikusan lapok betöltésével akár végtelen nagyságú világot is tud reprezentálni. A teljesség kedvéért röviden felsorolnám az OgreView-n kívüli komponenseket is: Framework: Általános célú osztályokat tartalmaz Ember komponenseknek. Config service: A konfigurációs fájlokat kezeli. Valamint, ha beállításokban a játékos módosít valamit, akkor gondoskodik a beállítások szinkronizációjáról. Input service: Perifériák kezelését biztosítja (egér és billentyűzet). (Ez lehet OIS vagy SDL2). Logging service: Globális naplózást tesz lehetővé, valamint az Ogre és a CEGUI Log fájljait egyesíti az Ember log fájljába. Meta server service: A szerverek listájának lekérdezését és kezelését biztosítja. Scripting service: Lua inicializálását biztosítja. Server service: Szerverhez való csatlakozást és kapcsolat bontását biztosítja. Server settings service: Szerver függő beállításokat kezeli. Sound service: Audio fájlok lejátszását biztosítja. wfut service: Szerverhez szükséges media fájlok letöltését kezeli. main: Program indítását és a program fő ciklusát kezeli. entity mapping component: Eris (világ reprezentációs komponens) rétegre épülő Ember kötéseket tartalmazza. cegui component: GUI kezelését tartalmazza. OgreView component: grafikai megjelenítést biztosítja. 12

1.1.2.4 Az OgreView komponens Az OgreView biztosítja a világ megjelenítését. Az Eris világ reprezentáció globális koordináta rendszert használ és minden entitás egy kvaternióval forgatott dobozként reprezentál egy adott paraméter listával, amelyet a szervertől kap. Ehhez képest grafikai világban természetesen sokkal komplexebb világ reprezentációra van szükség. Az egyik oka, hogy egy Eris entitás több grafikai elemből is állhat. Például az Eris világban egy fáklya entitás egy égő tulajdonsággal, úgy reprezentálható, hogy vessük a fáklya geometriáját, hozzá csatolunk egy fényforrást és egy részecske sugárzót (emitter). Másik példa erre egy karakter entitás Eris-ben, amely fáklyához hasonlóan egy kvaternióval forgatott doboz tulajdonság listával, míg grafikai világban ruhát hord (csatolt geometria) és a kezében kard van (csatolt geometria), míg csontváz animációval mozog. Így az OgreView alapja egy hierarchikus színtér rendszer az összetett jelent megjelenítéséhez sok más játékhoz hasonlóan, amely segítségével lokális koordináta rendszert lehet létrehozni. A 2. ábra illusztrál egy 2D színteret, amely két részből áll: Csomópontok, Mozgatható objektumok (MovableObject). Csomópontok egy lokális koordináta rendszert reprezentálnak a szülő csomóponthoz képest, MovableObject pedig egy rajzolandó objektumot az adott koordináta rendszerben. Ez lehet entitás, kamera, fényforrás, stb. 2. ábra: Színtér gráf felépítése OgreView fontosabb részei: authoring: a világ szerkesztését biztosítja a kliensből világszerkesztőknek (adminisztrátor felhasználónak). Tartalmaz osztályokat entity létrehozáshoz, material szerkesztéshez és Cyphesis szerver oldali Python forráskód módosításához importálásához és exportálásához. Ezen kívül tartalmaz Ogre és Eris AABB megjelenítő segéd osztályokat, modell teret jelző tengely vektor nyilak megjelenítőjét, Terrain festő és Terrain magasság szerkesztőket. 13

Valamint sok kisebb dolgot, amelyek csak adminisztrátor felhasználók által érhetőek el, de játékosoknak nem. camera: Camerát kezeli. Tartalmaz First Person-, Third person- és Free kamera nézetet. Valamint tartalmaz egy felvevő osztályt, amivel videót lehet felvenni a játékról külső szoftver használata nélkül. environment: Égbolt, időjárás, víz és növényzet generátort tartalmaz. A növényzetet több rétegen (foliage) el lehet helyezni. Mindegyik réteg máshogy lesz feldolgozva. Valamelyik statikus rétegben a mesh-ek egyesítve lesznek egy geometriává (batch), míg az entitás rétegen az interaktív objektumok vannak. A fák és egyéb növényzet külön réteget képeznek, ugyanis a fák távolabbról látszanak, mint a növények (például fű). environment/caelum: Napszak függő égbolt (felhők, csillagok, nap és hold) rajzolását végzi. Ennek a könyvtárnak megadjuk, hogy mi a jelenlegi idő és annak megfelelően fogja beállítani az égbolt textúráját, valamint a felhők pozícióját. Beállítja a környezeti megvilágítást is, így a jelenet jobban beleolvad az adott napszakba. environment/meshtree: Fa generátor, amely adott paraméterek és random generátor számok átadásával létrehoz egy fát. environment/pagedgeometry: Batch-elt kisebb és sűrűbb növényzet (fű, bokrok) rajzolását végzi. Rengeteget lassítaná a rajzolást, ha minden fűszálat külön-külön transzformálnánk, ezért ez a könyvtár biztosítja, hogy egy adott blokkban levő geometria halmaz egy geometriaként (Batch-ként) legyen kirajzolva. lidr: Light indexed deferred rendering-et kezeli. Ez a diplomamunkám része, bővebben a 2. fejezetben olvashat az olvasó. lod: Mesh LOD generálását és megjelenítését kezeli. Úgyszintén a diplomamunkám része, bővebben az 5. fejezetben olvashat az olvasó. mapping: EntityMapping (1.1.2.3. fejezetben ismertetett), az Eris világreprezentációt kapcsolja össze az Ember klienssel. Ebben a komponensben pedig a grafikai modellel kapcsolja össze az EntityMapping komponenst. 14

model: Egy grafikai modellt reprezentál. Animációk, csontvázak, geometriák és ezek jelenlegi állapotát magába foglalja egy modell. Ebben a komponensben van implementálva a modell szerkesztő (3. ábra), ahol csak a grafikai reprezentáció beállításait lehet változtatni, míg az Entity Editor-ban az Eris entity beállításai érhetőek el. Minden modell kliens oldalt egy XML fájlba szerializálható, így biztosítva van a modellek perzisztenciája. 3. ábra: Modellszerkesztő terrain: A domborzat rajzolásáért felelős. Legtöbb nyílt világgal rendelkező játék használ egy ilyen domborzat rajzoló technikát. A terrain egy 2D magassági textúra alapján ki tud rajzolni hatékonyan egy domborzatot. Például ha a textúrába rakunk egy fehér foltot, akkor a világban egy hegy keletkezik. Ez képezi a földfelszínt, amelyre az entitásokat helyezzük. Textúrákat lehet festeni a földfelszínre (splatting), hogy meg lehessen különböztetni a füves, homokos és beton felszínek textúráját és színeit. 15

terrain/foliage: A felszíni rétegek menedzselését végzi. Meghatározza, hogy melyik földfelszín területen (terrain page), melyik rétegén, hol és mik jelenjenek meg. terrain/ogreterrain: Ogre Terrain komponenst kezeli. Mivel mi egyedi földfelszín festő és területekre bontási technikát is használunk, ezért szükség volt leszármaztatni pár osztályt az Ogre Terrain komponenséből, hogy megfeleljen igényeinknek. Ezek a változtatások itt érhetőek el. terrain/techniques: Három fajta földfestő (splatting) technikát támogat az Ember kliens: Base/Simple, Shader, ShaderNormalMapped. A Base technika fixed function pipeline-t használ, manapság ez a technika már nem számít hatékonynak, ezért alapértelmezetten nincs használva. A Shader technika fragment shader-ben festi a réteget. Minden blokknak (page) egy saját material-t generál, ahol shader preprocesszorként megadja, hány darab textúrára van szükség, majd ezekhez egy blend textúrát is társít 4 textúránként (4 színcsatorna miatt), hogy adott textúra milyen mértékben befolyásolja adott pixel-t. A ShaderNormalMapped technika nagyon hasonlít a árnyaló technikára, azzal a különbséggel, hogy itt egy 3D-sebb megjelenítést tud elérni a textúrák normál map-jének használatával. Az utóbbi csak magas grafikai fokozaton lesz alkalmazva. 16

2 Water Shader Az Ember kliensben a vízfelszín megjelenítése egy áttetsző textúrázott felülettel történt. Ez azonban messze elmarad a mai játékokban megszokott megjelenítéstől, ezért ennek kijavítását fontosnak tartottam. A fejezet elején ismertetem a fénytani alapismereteket, ugyanis ez nemcsak a víz megjelenítés megértéséhez szükséges, hanem a light indexed deferred rendering (3. fejezet) megértését is elősegíti. Ezután pedig ismertetem a számítógépes grafika specifikus optimalizációkat, amelyeket alkalmaztam a víz valósághű varázsolásához, majd a dolgozathoz készített implementációról és a végeredményről is beszámolok. 2.1 Fénytani alapismeretek A fény [33] [35] [47] az emberi szem számára érzékelhető elektromágneses sugárzás, amely a szemben fényérzetet kelt, és ez által látható. Az ember csak úgy képes a látásra, ha fény érkezik a szemébe. A látáshoz tehát szükséges valamilyen fényforrás. A fényforrásból kiinduló fénysugarak homogén közegben gömbhullámként, minden irányba egyenes vonalban, egyenletesen, elektromágneses hullámok formájában terjednek. A fénysugarak végtelenül keskeny sugárnyalábok. A fényáram a fényforrás minden irányban szétsugárzott fényteljesítményének összegsége (Jele: Φ). Vákuumban a fény sebessége megközelíti a 300 000 km/s sebességet. A fény az összes lehetséges út közül azt választja, melynek megtételéhez a legrövidebb időtartam szükséges. A fény terjedésének három alaptörvénye: Homogén közegben a fénysugarak egyenes vonalban terjednek. A fénysugarak útja megfordítható. A fénysugarak függetlenek egymástól, közöttük nincs kölcsönhatás még akkor sem, ha egymás útját keresztezik. A fény nem kerüli meg a testeket, viszont némelyiken átmegy. Például az ablaküveg, a víz, a levegő átlátszó. Ha egy fényáteresztő felületre (pl. vízfelületre) fény esik, a felület a ráeső fény egy részét visszaveri (reflexió), egy részét elnyeli (abszorbció), egy részét pedig átbocsátja (transzmisszió). Fényelnyelés (abszorbció) esetén a fényáram egy része az anyagban energiaátalakulást szenved. 17

A tükröződés (reflexió), a sugárzás visszatérítése valamely felületről anélkül, hogy monokromatikus összetevőinek frekvenciája megváltoznék. A fénytörésnek (refrakciónak), azt a jelenséget hívjuk, ha két optikailag különböző közeg határfelületére esik a fénysugár, az átbocsájtott fény egy része irányát megváltoztatva behatol a közegbe (megtörik). Két felület közötti fényáteresztésnek (transzmissziónak) nevezzük azt a tényezőt, amely a fény monokromatikus összetevőinek frekvenciáját befolyásolják. Ezt a tényezőt nevezik áteresztési tényezőnek (1. képlet), Jele: τ. τ= (1) A 4. ábra mutatja egy fénysugár fényvisszaverődés és a fénytörés jelenségét. Egy sima felületen a visszavert fény iránya = képlettel határozható meg, ahol a felület normálját vesszük alapul. Az átbocsájtott fénynél az és a szög egyenesen arányos, bármilyen beérkező fénysugárnál. Az és szög szinuszainak aránya megegyezik a felületben való fényterjedési sebességek arányával, valamint ezt az arányt nevezzük a két felület közötti törésmutatónak (2. képlet), jele: n. = ő í = ő, í (2) fénysugár αβ fényvisszaverődés Levegő Víz γ fénytörés 4. ábra: Fény beesés reprezentálása 2.2 Fresnel egyenlet A Fresnel-egyenletek [35] Augustin-Jean Fresnel által a Maxwell-egyenletekből levezetett fénytani egyenletek. Azt fejezik ki, hogy fényvisszaverődésnél, a visszavert és a beérkező fény energiahányada mekkora. Ennek meghatározására Maxwell egyenletekből először levezetett két egyenletet, egyik a merőleges fénybeesésnél (3. képlet) és egy a párhuzamos fénybeesésnél (4. képlet). Ezekből a képletekből vezette le a nem polarizált fényvisszaverődés energiahányadát (5. képlet). 18

(3) (4) (5) Az eddig bemutatott képletekből látszik, hogy ennek meghatározása pixelenkénti fénysugár esetén rengeteg számítást igényel. Mivel valós időben szeretnénk render-elni a vízfelületet, így ennek használata nem nagyon lelassítaná a rajzolást. De Francesco Maria Grimaldi a 17. században feltalált erre egy approximációt, amelyet Fresnel diffrakciónak nevezünk (diffrakciónak nevezzük azt, ha nem vesszük figyelembe a fény hullámhosszának hatását a számításaink alatt). Ennek egy GPU-ra optimalizált változata a következő: = 1 (6) Ahol fresnel határozza meg a visszavert fény hányadát, eyedir a beérkező fény irányát (kamera normálját), a normal pedig a víz normálja, a maradék három változó pedig a vizuális effekt testre szabását szolgálja (nincs valós fizikai kapcsolata), amelyekkel be lehet állítani, hogy milyen szögnél, milyen szögtől legyen 1 és milyen szögnél legyen 0 a tükröződés mértéke, valamint a közte levő átmenetet határozzák meg. Én a következő értékeket használtam a water shader megjelenítéséhez: bias=0.2; scale=1; power=5. 2.3 Hullámok A 2.1. fejezetben ismertettem, hogy a fénysugár visszaverődésének szöge függ a felület normáljától. Így ha a felület nem sima, akkor minden pixel fénysugara különkülön másik irányba halad tovább. A GPU standard rajzolási modellje csak pontszerű kamerákat engedélyez, amellyel ezt nem lehet megvalósítani. A GPU-khoz optimalizált hullám effektust úgy érjük el, hogy a visszavert és átbocsájtott fényt egy animált textúra (5. ábra) alapján eltoljuk. Ez természetesen messze van a valóságos hullámoktól, de valóságos kinézetet kelt. 19

2.4 Implementáció 5. ábra: Hullámokat szimulálásához használt zaj textúra Kiindulási alapnak az Ogre Fresnel water shader-t választottam. Refraction-nak (fénytörés) és reflection-nek (tükröződés) külön render target-et használ, azaz háromszor ki kell rajzolni a jelenetet: 1. Először a levegő és víz közötti törésmutató és kamera távolság alapján újrapozícionált kamerával render-eljük ki a jelenetet. Ezt a víz alatti objektumok kirajzolására használjuk majd fel. Custom near clipping technikával pedig levághatóak a víz feletti objektumok. 2. Másodszor úgy rajzoljuk ki a jelenetet, hogy a víz felszín lapjára tükrözzük a kamerát. Ezt a víz feletti objektumokhoz fogjuk használni. Custom near clipping technikával pedig levághatóak a víz alatti objektumok. 3. Végül a hagyományos kamera szögből rajzoljuk ki a jelenetet, míg a két textúrát a textúrát fesnel diffrakcióval összemossuk (blending), valamint a hullám effektus generálásához a textúrák koordinátáit offset-eljük egy animált zaj textúra segítségével. Mivel ennek a számítása nagyon költséges, ezért egy egyszerű szoftveres occlussion query-t (láthatósági teszt) is írtam hozzá, amelly a kamera frustum-mal és a terrain page minimum AABB-jével hasonlítja össze. 20

A fényabszorbciót egy konstanssal oldottam meg (tintcolor), amely segítségével a víz alatti objektumok enyhén kékes árnyalatúvá válnak. Ehhez a színhez hozzávettem a környezeti megvilágítást is, hogy valósághű víz jelenjen meg nappal és este egyaránt. void main_fp( float4 pos : POSITION, float3 noisecoord : TEXCOORD0, float4 projectioncoord : TEXCOORD1, float3 eyedir : TEXCOORD2, float3 normal : TEXCOORD3, out float4 col: COLOR, uniform float4 tintcolor, uniform float noisescale, uniform float fresnelbias, uniform float fresnelscale, uniform float fresnelpower, uniform sampler2d noisemap : register(s0), uniform sampler2d reflectmap : register(s1), uniform sampler2d refractmap : register(s2) ) { // Zaj, hullámok float3 noisenormal=(tex2d(noisemap,(noisecoord.xy/5)).rgb-0.5).rbg*noisescale; float2 final = projectioncoord.xy / projectioncoord.w + noisenormal.xz; // Reflection / refraction float4 reflectioncolor = tex2d(reflectmap, final); float4 refractioncolor = tex2d(refractmap, final) + tintcolor; // Fresnel egyenlet float fresnel=fresnelbias+fresnelscale*pow(1+dot(eyedir,normal),fresnelpower); col = lerp(refractioncolor, reflectioncolor, fresnel); } 6. ábra: Water fragment shader 21

2.5 Végeredmény Az alábbi képeken (7. ábra, 8. ábra) szemléltetem bemutatni az olvasónak az Ember kliens grafikai javulását, amely a játék élményét véleményem szerint drasztikusan feljavította. 7. ábra: Víz megjelenése fejlesztés előtt (jobb) és fejlesztés után (bal) 8. ábra: Játékos félig víz alatt az új water shader-rel. 22

3 Light Indexed Deferred Rendering (LIDR) A 3D grafikai megjelenítésben fordulópontot jelentett az úgynevezett árnyalók programozhatóságának megjelenése, amelyekkel az addigi szigorúan kötött képalkotási folyamat (9. ábra) bizonyos lépéseit saját modulokra cserélhettük. Megfelelő árnyalóprogramok segítségével változatosabb és komplexebb árnyalási technikákat alkalmazhatunk, amelyek bonyolultságát a grafikus processzorok (GPU) rohamos fejlődésével folyamatosan növelhetjük. Vertex shader Vertex transzformálás. Raszterizálás A vektor alapú geometriát képpontokká (fragment-ekké) alaktás. Fragment shader Fragment színezés. Frame buffer Textúra, amely megjeleníthető egy kijelző eszközön, vagy felhasználható egy másik rajzolási ciklus (pass) bemeneteként. 9. ábra: Standard rajzolási lánc (Geometry shader és tesszeláció lépések nélkül) A hagyományos ún. forward rendering megjelenítés során minden objektumra kikeressük a hozzá tartozó legközelebbi, legrelevánsabb fényforrásokat (tipikusan maximum 8-at) és mindegyikre elvégezzük az árnyalási számításokat, amelyeket összegzünk. Ezeket a számításokat egyetlen árnyalóban is végezhetjük, de a változó fényforrásszám miatt nehéz hatékony árnyalóimplementációt készíteni. Ha a fényforrások számában nagy flexibilitásra törekszünk, választhatjuk azt a megoldást is, hogy a geometriát többször jelenítjük meg, minden megjelenítéskor csak egy fényforrást számítva, és az egymás utáni rajzolásokat a grafikus hardver által támogatott blending segítségével összegezzük. 23

A fent vázolt megoldások teljesítménye két ok miatt korlátozott. Ha nagy komplexitású a színterünk sok olyan pixel lesz, melyre kiszámoljuk a megfelelő árnyalt színértéket, azonban később felülírjuk, mert egy kamerához közelebbi pontot találtunk (ez a GPU-k z-buffer algoritmusából adódik). A második megoldás esetében ezen felül minden fényforrásra külön render-elést (rajzolást) kell indítani, csúcspontok újbóli transzformációjával és raszterizációjával. A deferred rendering [31] technika úgy próbálja meg orvosolni a fenti problémákat, hogy szétválasztja a láthatóság számítását az árnyalástól. Egy előzetes render-elési lépésben (prepass) kirajzolja a teljes jelenetet és eltárol egy ún. geometria buffert (g-buffer), mely minden olyan információt rögzít, mely az árnyalás számításához kellhet. Ilyen adatok az árnyalt pont pozíciója, felületi normálisa, színe stb. Ezt követően minden fényforrásra képpontról képpontra számítja az adott fényforrás hozzájárulását a tárolt adatoknak megfelelően. Az egyes fényforrásokra számított értékeket összegezzük. Nézzük meg hogyan alakul az algoritmus számításigénye. Ha van L=100 fényforrásunk és N=500 objektumunk, akkor a forward rendering L*N=50000 rajzolási műveletet igényel a jelenet kirajzolásához, míg a deferred rendering L+N=600 műveletet. A rajzolási műveletek száma komoly korlátozó tényező lehet. Egy felmérés szerint egy nagyteljesítményű GPU sem képes egy tipikus játék során másodpercenként 10000 rajzolásnál többre. Mivel az MMO játékok komplex színtereiben az objektumok száma is igen magas lehet, a felhasználható fényforrások száma igen alacsony lesz forward rendering technikával, deferred rendering technikával azonban jelentősen növelhető. A deferred rendering-nek az előnyei ellenében vannak hátrányai is. Nem tud átlátszó objektumokat kezelni, nem kompatibilis forward rendering-el, nagy a G-buffer, tükröződés vektort fényforrásonként újra ki kell számítani, valamint korlátozza az anyagjellemzők számát. Létezik azonban egy viszonylag friss technika az úgynevezett light indexed deferred rendering [32], ami a fent vázolt két fő irány között helyezkedik el. Meg tudja tartani a deferred rendering technika L+N műveletigényét. Ugyanakkor az objektumok árnyalása során forward rendering-et alkalmaz, így elkerüli a deferred rendering számos hibáját. Például meg tudja őrizni az anyagok változatosságát, és helyesen kezeli az átlátszó objektumokat. 24

Az alábbi ábrán (10. ábra) látható egy jelenet LIDR megvilágítással, amely rajzolási folyamatát ebben a fejezetben be szeretnék mutatni az olvasónak. 3.1 A LIDR Működése 10. ábra: LIDR megvilágítás Ember kliensben A LIDR megtartja a forward rendering-nél használandó árnyalókat, ezen árnyalókban használható fényforrások számát azonban korlátozza. Ez nem jelent túl nagy megkötést, a legrelevánsabb fényforrások használata általában elegendő. A releváns fényforrások leválogatását azonban nem bízza a CPU-ra, hanem megfelelő segédstruktúrák használatával a GPU-n számolja. Ahhoz, hogy az árnyalók minden fényforrás adatát láthassák, a fényforrástulajdonságokat egy textúrába mentjük. Ezt nevezzük fény információ térképnek (11. ábra). A nagy méretű táblázat jellegű adatok textúrában tárolása gyakran használt technika a GPU árnyalóknál. 11. ábra: Light info map 25

A releváns fényforrások leválogatása (light culling) pixelenként történik, a művelet végeredménye egy olyan struktúra (szintén textúra formájában), mely minden pixelre egy listát tárol az adott pixelben látható árnyalt pontra ható legfontosabb fényforrások azonosítójáról. Ezt fény index térképnek (12. ábra) hívjuk. 12. ábra: Light index map A fényforrások azonosításához szükség van az árnyalt pontok pozíciójára. Ezt egy, a deferred rendering technikánál használt geometria bufferrel állíthatjuk elő, itt azonban lényegesen kevesebb információt kell csak eltárolnunk, elegendő az árnyalt pont mélysége. Ennek a buffernek az előállítását nevezzük depth prepass-nak. Ennek a lépésnek megvan még az az előnye, hogy feltölti a z-buffert a látható pontok mélységével, így amennyiben a z-buffert nem töröljük- a későbbi forward rendering lépésekkor már a nem látható pontok automatikusan eldobásra kerülnek. A pixelfelülírás ilyen módon történő elkerülése is jelentősen növelheti a teljesítményt és gyakran alkalmazzák tisztán forward render-elésen alapuló rendszerekben is. A következő alfejezetekben részletesen tárgyaljuk az LIDR technika egyes allépéseit. 26

3.1.1 Depth prepass A mélységbuffer (13. ábra) tárolásakor több lehetőségünk is van, ugyanis a tárolandó mélységet több koordinátarendszerben is értelmezhetjük. A Gl_FragCoord.z, amely a GPU által alapértelmezetten használt nem lineáris depth érték. Ezt a GPU mindenképp létrehozza, ugyanis ezt használja fel mélységteszteknél, de végül a view space mélységnél maradtam. Ennek az oka a gömb hátoldalának mélység tesztjéhez át kell adni a fényforrás középpontját is, amelyhez lineáris tér szükséges. 13. ábra: Depth buffer (utólag kontraszt növelt kép) 27

3.1.2 Light index mapping pass Ebben a lépésben a cél, hogy per pixel light culling-ot végezzünk, azaz kilistázzuk az adott pixelre legerősebben ható fényforrásokat (12. ábra). 3.1.2.1 Pixelre ható fényforrások meghatározása Az egyes fényforrások által befolyásolt pixeleket úgy határozzuk meg, hogy a fényforrásokat gömbökként reprezentáljuk, ahol a gömb sugara a fény hatósugara és a gömb középpontja a fényforrás pozíciója. Ezeket a gömböket kirajzolva az általuk befolyásolt pixeleket fogják lefedni. A megjelenítést úgy végezzük, hogy az előző depth prepass során használt depth buffert megtartjuk, és nem módosítjuk (gömbök mélységét nem írjuk be a mélység bufferbe). Így minden olyan pixelbe, ahol az árnyalt pont a kamerához közelebb esik, mint a fényforrás gömbje, a gömb nem lesz kirajzolva a mélységteszt miatt. Ezzel azonban még nem szűrtük ki azokat az árnyalt pontokat, melyek távolabb vannak a kamerától, mint a gömbök hátsó mélysége (depthback). Ahhoz, hogy ezt hatékonyan számítsuk, paraméterként átadjuk az árnyalónak a gömb középpontját, mely segítségével a hátsó mélység már számítható. Mivel mi kamera térbeli mélységekkel dolgozunk, de valójában perspektív vetítést használunk, ez némi pontatlanságot visz be a hátsó mélységek számításakor a képernyő szélei felé haladva. Az így előkerült vizuális hibákat egyszerűen orvosolhatjuk, ha a gömb sugarát kicsit megnöveljük. A gömbök kirajzolásakor kirajzolandó színként a fényforrás egyedi azonosítóját használjuk. Ezt illusztráltam a 14. ábra: Felülről nézet egy fényforrás hatógömb hatáskörének meghatározására ábrán. A képlet, amelyet a hátsó mélység (depthback) approximálására fejlesztettem ki a következő: h h h h (7) 28

14. ábra: Felülről nézet egy fényforrás hatógömb hatáskörének meghatározására uniform vec4 outpos; // fényforrás hatógömb középpontja varying float depthfront; varying float depthback; void main() { float spherecenter = outpos.z; depthfront = (gl_modelviewmatrix * gl_vertex).z; float depthsphere = (spherecenter - depthfront); depthback = spherecenter + depthsphere; gl_position = ftransform(); } 15. ábra: lidrlightmapbuffer_vs.glsl (vertex shader) // csomagolt fényforrás indexe uniform vec4 outcolor; // SRCrgba, csomagolt fényforrás index színe uniform vec4 screensize; uniform sampler2d DepthBuffer; varying float depthfront; varying float depthback; void main() { float depthbuf = texture2d(depthbuffer,gl_fragcoord.st * screensize.zw).r; if (depthbuf > depthfront depthbuf < depthback) discard; // Ez a fényforrás nem hat erre a fragment-re. gl_fragcolor = outcolor; } 16. ábra: lidrlightmapbuffer_fs.glsl (fragment shader) 29

3.1.2.2 Fényforráslista hatékony tárolása Ideális esetben a végeredmény egy pixelenkénti láncolt lista lenne, melyben a fényforrások egyedi azonosítói (indexei) vannak eltárolva. A GPU-n azonban nem lehet hatékonyan láncolt listákat tárolni és kezelni. Ezért korlátozzuk a fényforrások számát: minden fényforrásra csak a négy legrelevánsabb fényforrás indexét tároljuk el. Ez az információ elfér egy textúra négy csatornájába. A fényforrásindex tárolásakor viszonylag bonyolult árnyalókódot kell írnunk, mely egy tárolandó index esetén ellenőrzi, hogy hány indexet tároltunk már korábban és az új indexet a következő szabad csatornába helyezi el. Az elosztott teljesítményt itt jelentősen csökkentheti az elágazások használata. Ezen jelentősen tudunk javítani a fényforrásindexek megfelelő csomagolásával és bináris aritmetika alkalmazásával. Az új fényforrásindex megfelelő beszúrását biteltolásokkal fogjuk elvégezni. Ehhez átalakítjuk az indexek tárolási módját, és nem csatornánkként egy indexet tárolunk, hanem az indexeket mind a négy csatornára elosztva tároljuk. Ha összesen 256 fényforrást támogatunk, akkor az indexek egy bájtnyi helyet igényelnek. Ezt az egy bájtot eloszthatjuk a négy csatornába, úgy hogy minden csatornába két bájt jut. A fényforrás indexét tehát bináris számként felírva, ezt a számot két bites csoportokra osztva, és minden kettes bitcsoportra az alacsony helyiértékeket zérus értékekkel 8 bitre kiegészítve kapjuk meg az egyes színcsatorna értékeit. Példaképpen nézzük meg a 165. indexű fényforrás színének meghatározását: 165 = 10100101 = 0.25,0.25,0.5,0.5 (8) 17. ábra: Példa a 165. fényforrás színének számítása (bináris számokkal) 30

Egy újabb index beszúrásakor e két felső helyiértékű biteteket kell jobbra tolnunk és helyükre az új index bitjeit illeszteni minden csatornában. Ez kettő bitenkénti eltolást és egy összeadást igényel. Azonban a bitenkénti eltolást osztásként is felírhatjuk, így a régi indexeket tartalmazó csatornaértékeket 4-el osztjuk (0.25-el szorozzuk) és hozzáadjuk az új csatornaértékeket. A súlyozott összeg azonban hardveresen támogatott a 3D API-kban a blending segítségével: az új fragmens szorzófaktorát 1.0-re, a régit pedig 0.25-re állítva a kívánt eredményt kapjuk. Példaképpen tegyük fel, hogy üres a jelenet és tegyük a listába a 165. indexű fényforrást. Ebben az esetben 0 0.25 1. Rakjunk be még egy indexet. Például 177 10110001 0.25,0.0,0.75,0.25. Ekkor scene blending pedig a következő színt adja vissza: 0.25,0.25,0.75,0.75 0.25 0.25,0.0,0.75,0.25 1 (9) 18. ábra: Bit shifting hardver blending által Tehát a kimenet egymás után láncolja a színeket, míg a legmélyebben fekvők kipotyognak a lebegőpontos pontatlanság miatt. Teszteléseim alapján float16-ban 5 index, míg float32-ben 9 index fér el pontosan. Ogre3D nem támogatja a konstans alapú scene blending-et, így saját fejlesztésű patch szükséges a technika alkalmazásához. Ez különösen nagy probléma, ugyanis a Linux disztribúciókban elérhető Ogre csomag inkompatibilis ezzel a technikával és egy külön Ogre verziót kell adniuk az Ember package mellé. Remélhetőleg Ogre 1.10 már tartalmazni fogja változtatásomat. 31

A CPU-n generálunk egy 256xN-es light info textúrát (19. ábra), ahol N változtatásával lehet növelni a fényforrások információmennyiségét, mint például fény pozíció, hatógömb sugara, diffuse szín, specular szín, fényforrás típusa. A textúra X koordinátája pedig jelöli a lightid-t (minden fényforrásnak van egy oszlopa). Alapértelmezetten elég átadni a fényforrás diffuse színét, fény intenzitását (gömb sugarát) és a fényforrás pozícióját, amely 256x2 méretű textúrában elfér. A fenti képen 1. sorban a fényforrás pozícióját és 1/sugarát adjuk át, második sorában pedig a fényforrás színét. Az adott pixelre ható fényforrás listának elemszámát úgy határozzuk meg, hogy egy 0 indexű fényforrást kapunk a lista végén, ezért fényforrások indexeinek számozását 1-el kell kezdeni. 3.1.3 Forward rendering 19. ábra: Light info textúra A végső fázisban hagyományos forward rendering-el kirajzoljuk a jelenetet, de a fényforrások listáját a paraméterként átadott Light Index Map alapján csomagoljuk ki és a kicsomagolt index alapján a Light Info Map-ből tudjuk kiolvasni a fényforrás adatait. Kicsomagolásnál (20. ábra) először beszorozzuk 4-el, hogy a kiolvasandó index bitjei egész számoknál legyen, majd a floor() függvénnyel lenullázzuk a törteket. Ezután skaláris szorzatát vesszük (1, 4, 16, 64) konstanssal, hogy integráljuk a biteket egy integer számmá. 20. ábra: A Fényforrás index listájának kicsomagolása 32

void unpack(inout vec3 lighting, in vec3 texturecolor) { // get Screen position on screen. screensize.zw = 1/vec2(800,600) vec2 fragpos = vec2(gl_fragcoord.xy * screensize.zw); fragpos.y = 1.0 - fragpos.y; // Invert Y axis! // Look up the bit plane texture vec4 packedlight = texture2d(lightmapbuffer, fragpos); // Unpack constant vec4 unpackconst = vec4(1.0, 4.0, 16.0, 64.0) / 256.0; // Unpack each lighting channel for (int i = 0; i < 5; i++) { packedlight = packedlight * 4.0; vec4 floorvalues = floor(packedlight); packedlight -= floorvalues; float lightindex = dot(floorvalues, unpackconst); if (lightindex < 0.75 / 256.0) return; // No more lights // Center the pixel lightindex += 0.5 / 256.0; // Lookup the Light position (with inverse radius in alpha) vec4 lightviewpos = texture2d(lightinfobuffer, vec2(lightindex, 1.0/8.0)); // Lookup the light color vec3 lightcolor = texture2d(lightinfobuffer,vec2(lightindex,3.0/8.0)).rgb; lighting += processlight(lightviewpos, lightcolor, texturecolor); } } 21. ábra: Forward rendering pass-ban a fényforrásokat kicsomagoló fragment shader részlet 3.1.3.1 Szélsőséges esetek kezelése Mivel a fényforrásokat gömbökként rajzoljuk ki, ez csak akkor működik, ha a kamera nem tartózkodik a gömb belsejében. Így külön kezelni kell, ha a fényforrás és kamera közötti távolság kisebb, mint a gömb sugara. Ekkor lecseréljük a gömböt egy lapra, amely a kamera előtt van (vagy akár egy compositor shaderre) és csak depthbackre teszteljük, mivel a depthfront a kamera mögött van. Az eredeti implementáció nem tesztelte a depthback-et és ezt én dolgoztam ki, hogy így sokkal több fényforrás elfér a képen egymás mögött. Viszont van egy hátránya. Félig átlátszó objektumoknál nem lesz megvilágítva az átlátszó objektum, mivel csak a mögötte levő szolid objektum kerül megvilágításra. Így ha a jelenet sok átlátszó objektumot tartalmaz, akkor érdemes kikapcsolni a depthback tesztelést. 33

Egy optimálisabb megoldás az lenne, ha depth prepass-t egy MultiRenderTarget-ként kezeljük, ahol a másik kimenet egy mask, ami meghatározza, hogy adott pixelt fedi-e átlátszó pixel. Ezzel a mask-al a Light Map Pass-ban már el tudjuk dönteni, hogy használható-e a depthback,. Az implementáció csak pont alapú fényforrásokat támogat, de kibővíthető spot fényforrásokra is, ha szükséges információt beleírjuk a Light Info Map-be. Irány fényforrások (directional) esetében pedig egy darab fényforrást engedélyeztem, ez lehet a nap vagy a hold fénye, amely LIDR rendszertől függetlenül fel lesz dolgozva. 3.2 Végeredmény A WorldForge Ember kliens egy teljes MMORPG, így rengeteg nem grafikai számítást végez, ezért szintetikusabb környezetben fejlesztettem és mértem a teljesítményt egy saját fejlesztésű példaalkalmazáson (22. ábra). A fejlesztőkörnyezetben az ablak felső részén lehetett látni a Light Info Buffert, míg bal oldalt a Light Index Map Buffert. Az ablak maradék részén pedig a LIDR-el világított jelenet látható. A jelenetben 100 fényforrás látható és 137 FPS-el fut egy alacsony kategóriájú hardveren. Hozzá kell tenni, hogy a kék fényforrást jelölő gömbök nélkül 250 FPS-el rajzolódik a jelenet. 22. ábra: Saját fejlesztésű LIDR technológiai demó jelenet 34

4 Hardware Skinning A fejezetben ismertetem a karakterek csontváz animációjának működését, majd az implementációról és a végeredményről számolok be. 4.1 Karakter animáció Egy karakter rengeteg csúcspontból és azokat összekötő háromszögekből (face) áll. Karakter animálás alatt azt értjük, hogy a nagy számú csúcsponthalmazt meg kell mozgatni az animáció által az adott időpillanatban elvárt pózba. Az egyik ilyen technika a morph vertex animáció, amely eltárolja az animáció adott időpillanataiban levő vertex pozíciókat (snapshot-ot készít), majd kirajzolásánál az időpont idősáv szerinti két szomszédos snapshot-jának interpolációjával határozza meg a vertex pozícióját. Ennek az a hátránya, hogy drasztikus mennyiségű memóriát foglal el egy ilyen animáció. Egy másik technika a pose vertex animáció, amelynél több vertex állapotot (pózokat) lehet egymással interpolálni. Ezzel a technikával szokták az arc animációját készíteni, mivel az arcban levő arcizmok állapotát külön-külön pózonként lehet ábrázolni. A harmadik technika a csontváz animáció (skeletal animation), amely manapság a legelterjedtebb animálási technika. Egy karakter csontváz animálása azzal kezdődik, hogy létrehozzuk a csonthierarchiát. A csontváz minden csontja egy külön koordináta rendszert képez a szülő koordináta rendszeréhez képest, így ha egy csontot megmozgatunk, akkor a hierarchiában alatta levő csontok is mozognak vele együtt. Például ha valaki megmozgatja a vállát, akkor az egész kar és kézfej mozog vele együtt, így a váll is egy lokális koordináta rendszert képez a karnak. A csontváz animáció készítés folyamata a következő lépésekből áll: 1) Létrehozunk egy csontvázat (pozícionált csonthierarchiát). 2) Létrehozunk animációkat a csontok mozgatásával. 3) Vertex-eket hozzátársítjuk a csontokhoz (rigging), hogy melyik csont, milyen mértékben befolyásolja adott vertex-t. 35

4) Végül az animáció megjelenítéséhez egy úgynevezett skinning technikát alkalmazunk, amely azt határozza meg, hogy a csont mozgatásánál a vertex milyen viszonyban transzformálódik. A rigging információkat vertex bufferben olyan formában tároljuk, hogy minden vertexhez rendelünk egy bone index listát és a bone-okhoz tartozó súlyt. A bone index alapján le lehet kérdezni, az adott bone modell space-ből való transzformációs mátrixát. A súlyokkal pedig azt lehet meghatározni, hogy mennyire hat ki a az adott bone a vertex-re. A skinning-et lehet CPU-n (ún. software skinning) és GPU-n (ún. hardware skinning) is számolni. A software skinning előnye, hogy egy jelenet többszöri kirajzolása (különböző pass-ok) esetén is csak egyszer végzi el a skinning-et, majd ezt küldi el GPU-nak, de a hátránya, hogy nagyon lassú (lásd mérési eredményeket 4.3. fejezetben). A hardware skinning előnye, hogy gyors és LOD-nál nem megjelenő vertex-eket nem dolgozza fel (hatékonyabb). A hátránya viszont az, hogy a grafikai engine nem tudja elvégezni ezt nekünk transzparensen és integrálni kell összes animált karakter shader-ébe, valamint a csontok transzformációjáról a shader-nek át kell adni egy nagy fix méretű tömböt. A 23. ábra illusztrálja a csontváz animációt. 23. ábra: Skeletal animation [34] Többféle skinning technika van, mindegyik más előnyökkel és hátrányokkal. Az úgynevezett linear skinning a legegyszerűbb és leggyorsabb skinning technika, viszont nem biztosít jó eredményeket, ha a csontot a saját tengelye körül forgatjuk el. A linear skinning a = képletet használja, ahol a vertex modell-space pozíciója, a vertex-hez társított i. csont súlya, míg a model space-ből bone space-be transzformáló mátrix. Az eredmény, pedig az animált vertex pozíció model spaceben. Az mátrix-ot minden Frame-ben újra kell számítani, ugyanis a skinning spacet, amely a szülő bone-hoz képest relatív, model space relatívvá kell alakítani. 36

4.2 Implementáció A skinning kiválasztásánál a teljesítményt fontos szempontnak tartottam, ezért linear skinning technikát választottam. Mint minden hardware skinning technika, át kell adni egy fix méretű tömböt a shader-nek (4.1. fejezetben ismertetett), amely tartalmazza a bone transzformációs mátrixot. Ennek az a hátránya, ha egy mesh túllépi a fix bone számot, a kilógó bone-ok transzformációi memória szemét lesz. A 24. ábra mutatja, hogy a modell testének azok a vertex-ei, amelyek memória szemétbe lógó csontokat tartalmaznak, a végtelenségig elnyúló háromszögeket képeztek. Ennek kiküszöbölésére nyújt az Ogre egy Run-time shader system-et, amellyel a shader-eket dinamikusan lehet létrehozni, de az Ogre material rendszerének az előnye hogy lecsatolja a C++ forráskódtól a rajzolást, míg RT Shader system-nél C++ kóddal összefonódik a rendszer, ezért WorldForge többi fejlesztőjével való tárgyalás után arra jutottunk, hogy tervezési okok miatt inkább a grafikusoknak kell ügyelnie erre. A probléma könnyítésére úgy implementáltam a skinning-et, hogy a grafikusnak van lehetősége választani a hardware és software skinning között, így a grafikust motiválja az optimalizált bone hierarchia használatára, de engedi a szoftveres megoldást komplexebb modellekhez. 24. ábra: Beállított limitnél több csont használata 37

A vertex shader-ben (25. ábra) nemcsak a vertex pozíciókat kell animálni, hanem a vertex normál vektorjai is megváltoznak. Ehhez átalakítottam a transzformációs mátrixot 3x3 nyagságú forgatási mátrixra. A shader-nek négy bemenete van. A vertex pozíció modell térben, a blendindices, amely 4 csont index-et tartalmazhat, a blendweights, amely a csont index-hez társított súly, valamint a worldmatrix3x4array, amely minden csonthoz tartalmazza a bone space-modell space transzformációs mátrixát. A for ciklus hosszának állításával (1-4 között) lehet szabályozni, hogy hány csontot szeretnénk figyelembe venni. attribute vec4 blendindices; attribute vec4 blendweights; uniform vec4 worldmatrix3x4array[120]; void main() { vec4 blendpos = vec4(0.0); vec3 blendnorm = vec3(0.0); for (int bone = 0; bone < 3; ++bone) { int idx = int(blendindices[bone]) * 3; mat4 worldmatrix; worldmatrix[0] = worldmatrix3x4array[idx]; worldmatrix[1] = worldmatrix3x4array[idx + 1]; worldmatrix[2] = worldmatrix3x4array[idx + 2]; worldmatrix[3] = vec4(0.0, 0.0, 0.0, 1.0); float weight = blendweights[bone]; blendpos += (vertex * worldmatrix) * weight; mat3 worldrotmatrix = mat3(worldmatrix[0].xyz, worldmatrix[1].xyz, worldmatrix[2].xyz); blendnorm += (normal * worldrotmatrix) * weight; } blendnorm = normalize(blendnorm);... } 25. ábra: Hardware skinning shader részlet A ciklus hossza erősen kihat a hardware skinning sebességére, így a cél, hogy minél kevesebb súlyt használva jó animációkat kapjunk. A jelenlegi karakterek nincsenek optimalizálva HW skinning-re és 2 súlynál szétcsúsznak (26. ábra), így alapértelmezetten 3 súlyt veszünk figyelembe. A grafikusok dolgoznak egy új rig-en, amely már két csont használatával is jó minőséget generál. 38

4.3 Végeredmény 26. ábra: HW skinning két (bal) és három (jobb) csont súllyal A teszt gép hardvere: CPU: Intel core 2 quad Q9550 GPU: Radeon HD 5770 A teszt folyamán 8 darab animált male.mesh-t jelenítettem meg a WorldForge Ember kliens futása közben, hogy teljesen reális eredményeket tudjak mérni. Az eredmény felülmúlta az elvárásaimat, mert drasztikus gyorsulás észleltem. A teljes kliens 205 FPS-ről 259 FPS-re növekedett, amely 26%-os növekedést jelent. Ez rengeteg, ha figyelembe vesszük, hogy egy Frame nem csak a grafikát, hanem minden játéklogikai számítást is tartalmaz. Ennek ellenére egy low-end notebook GPU-val (Radeon HD 4350m) körülbelül ugyanolyan FPS-t mértem, mint SW skinning esetében. 39

5 Mesh Redukció A távolban Ember mindig high-poly (nagy részletességű) modelleket használt. De távolban a modellek csak pár pixelre látszanak a játékos számára, így fölösleges sok poligon kirajzolása és emiatt fölöslegesen lassul a render-elés. Ennek a kiküszöbölésére találták fel az úgynevezett Mesh Level-of-Detail (LOD) management rendszert, amely arról gondoskodik, hogy ha egy adott modell bizonyos távolságnál távolabb kerül, akkor az eredeti geometriát lecseréli egy alacsonyabb poligon számú, de megközelítőleg ekvivalens modellre. Ezeket a redukált geometriákat nevezi LOD szinteknek. A Mesh LOD generátor pedig automatikusan generálni tudja a low-poly modelleket, így a grafikusoknak sem kell foglalkoznia azzal, hogy egy adott modellt több példányban, de eltérő poligon számban kirajzolja. Ezzel a cégeknek idő- és költségmegtakarítást biztosít és a modell dinamikusan továbbfejleszthető marad, mivel a grafikusnak csak egy modellen kell végrehajtania a módosításokat. Ezen felül a grafikusok által létrehozott manuális LOD szinteknél a vertex buffert is duplikálni kell, ezáltal kevésbé hatékony lesz, mivel a generátor speciális implementációjának köszönhetően csak index buffert érintő LOD szinteket képes generálni. Amikor először kipróbáltam az Ogre beépített LOD generátorát, akkor nagyon csalódott voltam, ugyanis nagyon rossz minőségben és nagyon lassan végezte a redukciót. Valamint az interfésze is logikátlanul limitált volt (nem lehetett exponenciális redukciós értékeket megadni) és automatikusan nem is tudta behangolni a mesh-t. Ekkor döntöttem úgy, hogy ez számomra elfogadhatatlan és ezért írok egy sajátot az Ember-hez. Ez később az elképzeléseimnél is jobban sikerült és az Ogre3D 1.9 és Ogre3D 1.10 már az én implementációimat használja. Azóta már többen írtak nekem, hogy használják játékukban és hálásak érte, sőt még java programozási nyelvre is implementálták jmonkey engine fejlesztői [36]. A fejezet áttekintést nyújt az általam fejlesztett mesh LOD redukció rendszer alapműködéseiről és a támogatott technológiájáról. Végül ismertetem az implementációm szerkezetét, amely Ogre 1.10-ben integrálva van, és az eredményeket összevetem az Ogre 1.7 LOD generátor rendszerével, amely az algoritmusom alapjául vett LOD generátor. 40

A következő néhány definícióval ismertetni szeretném a szükséges alapfogalmakat. Definíció LodData: Adatszerkezet, amely a mesh-t, pontok és irányított élek hálózataként reprezentálja. Minden él két darab egy irányú élként reprezentál, melyeknek külön-külön van collapsecost nevű élsúlya, amely azt fejezi ki, hogy az él irányába való redukálásnál mekkora grafikai hiba keletkezik. Összes él súlya rendezve lesz és heap-ként, mindig a legkisebb súlyú él mentén redukálunk. Definíció Redukció: A redukció folyamán a mesh háló vertex-einek számát csökkentjük eggyel. Ezt követően kiválasszuk a legkisebb súlyú irányított élt, majd az él mentén a kezdőpontot átmozgatjuk a végpontba, így a kezdőpont eltűnik. A 27. ábra mutatja egy redukció A pontból B pontba. Redukció után eggyel kevesebb vertex-ünk lesz és az A-B közös háromszögei is eltűntek. 27. ábra: Mesh háló redukció előtt (bal) és redukció után (jobb) Definíció speciális vertex: Olyan vertex, amely pozíciója többször szerepel a vertex bufferben. Ezek a hard edge-ek (ahol a self shadowing nem simított), két submesh-ben szereplő vertex, és két UV koordinátával rendelkező vertex. Definíció speciális él: Olyan él, amely legalább egy speciális vertex-et tartalmaz. Definíció A collapse cost egy pozitív valós szám, amivel minden él rendelkezik. A komplex algoritmus (pl. Görbület alapú súlyozás, négyzetes hiba alapú súlyozás, stb.), amely meghatározza ennek a számnak az értékét, egyben a redukált mesh minőségéért és redukció időtartamáért is felelős. 41

5.1 Görbület alapú súlyozás A görbület alapú súlyozás az egyik legfontosabb része a LOD generátoromnak. Egy átadott élnek kell meghatároznia az él súlyát, amely meghatározza, hogy melyik él szerint kell végeznünk a redukciót. A generátor a számítási idő 60%-át ennek számolásával tölti. Például a WorldForge media adatbázisában levő, 41399 vertex-ből álló cg_donjon.mesh redukálásánál 383329-szer hívja meg ezt a komplex függvényt. Az Ogre 1.7 implementációja és az én implementációm egyaránt a Stan Melax 1998-as képletére épül (10. képlet)., = max {min {.. }} (10) A képlet nem teljes körű, azt feltételezi, hogy folytonos a felület (azaz minden él pontosan 2 háromszögben szerepel), nincsen textúra törés (seam ripping) és nincsenek submesh-ek. Ogre3D fejlesztői ezeket a hiányosságokat kijavították, de sok hasznosat el is rontottak, amelyeket pedig kijavítottam. Évek folyamán Ogre 1.7-es verzióig rengeteg teljesítmény patch-et kapott az algoritmus, mint pl. gyorsító cache buffer, de minőségben és architektúrában nem változott. Az Ogre 1.8-as verzióban egy terület alapú redukcióra váltott, ami gyorsabb volt (1.7-hez képest), de sokkal rosszabb minőségű modelleket generált. Az 1.9-es verziótól az én teljesen újjáírt megoldásomat használja. 5.2 Négyzetes hiba alapú súlyozás A négyzetes hiba alapú súlyozás a legelterjedtebb technika a LOD redukciós körökben. Ennek véleményem szerint az az oka, hogy Microsoft a DirectX 9 beépített LOD generátora ezt használja, valamint a súlyozás egyszerűsége miatt akár dupla olyan gyorsan is tudunk LOD szinteket generálni. Sokszor feljött ez a téma, hogy miért nem használjuk ezt a súlyozást, ha mindenki isteníti? Így végül arra jutottam, hogy csinálok hozzá egy implementációt, hogy össze lehessen hasonlítani a Görbület alapú súlyozással (5.1. fejezet). A számításokhoz létre kell hozni a vertex-ekhez tartozó úgynevezett quadric mátrixokat. Ehhez a következő lépések szükségesek: 1. Minden háromszögnek határozzuk meg síkjának egyenletét: + + + =0, h + + =1 42

2. Hozzunk létre a háromszög síkjából egy 4x4-es (triangle quadric) nevű mátrixot a következőképpen: = Megjegyzés: Ez a mátrix szimmetrikus és az is marad az algoritmus folyamán, ezért legtöbb implementáció 10 elemű 1D tömbként reprezentálja a quadric mátrixot, de itt egyszerűség kedvéért mátrixokkal dolgozunk. 3. Számoljuk ki minden vertex Q mátrixát, amely a vertex által érintett háromszögek mátrixainak összege. = [ é ] Az előző 3 lépésben minden vertex-hez rendeltünk egy quadric mátrix-ot. Már csak azt kell meghatározni, hogy hogyan határozzuk meg egy adott v1 v2 él súlyát. Ezt a következő lépésekkel határozhatjuk meg: 1. = + 2. Legyen a 2 vertex pozíciója: =[ 2 2 2 1] 3. = Végül a kísérlet célja az volt, hogy össze tudjam hasonlítani a görbület alapú súlyozással. Minőségben (28. ábra) erős visszaesést észleltem, így a quadric error megvalósítás optimalizálását nem tartottam fontosnak, ezért teljesítmény teszteket sem készítettem, viszont optimalizációk nélkül is ugyanolyan generálási időt mutatott, mint a komplexebb görbület alapú redukció. 43

28. ábra: Négyzetes hiba (bal) és görbület (jobb) alapú redukció összehasonlítása 5.3 Normálok bevonása súlyozásba A redukció során a normálok okozzák a legnagyobb grafikai hibát. Viszont ezt nem lehet tökéletesen kiküszöbölni. Ennek az az oka, hogy a normálok inkább a háromszög felületére vonatkoznak. A 29. ábra normálokat színekkel reprezentálja illusztrációként. Itt is látszik, hogy még ha ugyanolyan színű él alapján redukál, akkor is hiba keletkezik. Amely arra bizonyíték, hogy ilyen jellegű hiba elkerülhetetlen, de mértéke csökkenthető. Erre fejlesztettem ki egy algoritmust. 29. ábra: Normál súlyozás algoritmusom szerint a két mesh háló megegyezik 44

Rengeteg kísérletet elvégeztem, különböző elméletek szerint, míg a 7. heurisztika (nem ismertetem előző kísérleteket) hozta a várt eredményt, amely minden szituációban javít a normálokon, valamint megvédi a görbe felületeket összeomlástól: Adott =, á, h, á í é h. G egy csillaggal izomorf gráf (egy adott pont szomszédos éleivel). -re, Dist é Dot, amely a következő képpen számolható: Legyen a redukció utáni ei él: 1. = Redukció előtti él hossza 2. = Redukció utáni él hossza 3. Dist =max 4. = 1, 2 a = 1, 2,, 5. Legyen a vi vertex normálja. Ekkor az él súly a következő: (a) Dot =max /8, cost = max Dist Dot Mivel a vertex normálok redukció folyamán nem változnak, így ez a kiindulási állapotot is figyelembe veszi a kimenetnél és így gömbölyített felületek sokkal jobban megmaradnak. A redukció minősége így a végeredményben javulni fog (30. ábra). 30. ábra: Mesh redukció normálok bevonásával (bal) és bevonás nélkül (jobb) 45

5.4 Technikák a súlyozás továbbjavítására A következő listában összefoglaltam a redukált mesh kimenet minőségével kapcsolatos egyéb fejlesztéseimet. 1. Collapse cost clash: Eredetileg a collapse cost 0 és 1 közötti értéket vehetett fel, ahol összes speciális eset értéke 1-re lett állítva. Ezzel az volt a probléma, hogy amikor a legkisebb súlyú élt kellett meghatározni, az 1.0 értékű súlyok véletlenszerű sorrendben lettek kiválasztva. Ezt úgy bővítettem ki, hogy a speciális eseteknek ugyanúgy kiszámítom a költségét, de a sima esetek többszörösére szorzom. Így azok is rendezettek maradtak grafikai hiba szerint. 2. Él hossz bevonása: Egy nagyon fontos szempont a Stan Melax algoritmusában az él hossz (5.1. fejezet, 9. képlet u-v része) bevonása, ugyanis rövid él kisebb hibát okoz. De ezt az Ogre fejlesztői kivették, azért, mert a speciális esetek konstansát nem tudták meghatározni, anélkül, hogy kivennék az élhosszt a számításból. Ezt én úgy oldottam meg, hogy konstansok helyett beszoroztam az eredményt. 3. Alternatív él választás: Például ha seam ripping-nél befele redukálunk, akkor régi implementációban lyuk keletkezik, míg az én implementációm inkább csak a textúrát rontja el. Így megmarad a test folytonossága és további redukció is jobb minőségű marad. 46

A WorlForge mesh adatbázisához fejlesztettem egy visual benchmarking system-et, amellyel egyértelműen látszik összes mesh-nél az óriási minőségi különbség (31. ábra, 32. ábra és 33. ábra). 31. ábra: 90% redukció saját algoritmussal (bal) és Ogre3D algoritmusával (jobb) 32. ábra: 95% redukció saját algoritmussal (bal) és Ogre3D algoritmusával (jobb) 33. ábra: 90% redukció saját algoritmussal (bal) és Ogre3D algoritmusával (jobb) 47

5.5 Mesh szintű redukció Ha például 95%-al redukálunk egy mesh-t, akkor régen submesh-enként 95%-al csökkent, ami nem optimális, ugyanis egyik submesh lehet akár low poly a másik high poly (poligon), ekkor high poly-ból több redukálása javítja a végeredményt. Én implementációmban egy submesh akár 100%-osan is redukálódhat, míg más fontosabb submesh-ek megmaradnak. Mikrolyuk elkerülés: submesh szintű redukciónál két submesh közötti seam redukálása függetlenül történik egymástól. Ilyenkor mikrolyukak keletkeznek már legkisebb redukálás esetén is. Az alábbi ábrán látszik, hogy a felső submesh-en megmaradt a vertex, míg az alsón redukálva lett és lyuk keletkezett. 34. ábra: Eredeti mesh 35. ábra: Submesh szintű redukció wireframe nézetben 36. ábra: Submesh szintű redukció 48

5.6 LOD tömörítés A LOD szintek csak index buffer alapúak, tehát egy LOD szint generálásakor nem hozunk létre új vertex pozíciót, hanem csak a meglévő vertex-eket tudjuk újrahasználni. Ez azt jelenti, hogy a nem érintett háromszögek megegyeznek. Így létrehoztam egy frame shifting alapú tömörítést, amely úgy rendezi a háromszögeket két egymást követő LOD szintnél, hogy elején vannak azok a háromszögek, amelyek csak LOD1-ben vannak, végén pedig azok, amelyek csak LOD2-ben vannak. Így megfelezhetjük a szükséges hardware bufferek számát. 5.7 Háttérben való számítás 37. ábra: Két index buffer tömörítése egy index bufferbe A LOD generálás nagyobb mesh-eknél akár több másodpercig is eltarthat. Ezt természetesen játék futása közben nem lehetne kivitelezni. Így szükség volt arra, hogy háttérben is lehessen futtatni a generálást. Viszont GPU-t csak main thread-en szabad lezárni. Ezért létrehoztam egy bufferelt, háttérszálas backend-et is. A mesh index és vertex buffereit lemásolja, majd háttérszálon feldolgozza, majd ha végzett, main szálon Ogre frame listener interfész által beinjektálja automatikusan az eredményeket. 49

5.8 Profiler Az általam bevezetett algoritmus sem tud minden esettel megbirkózni. Például ha vertex seam ripping 3 ágúan metszi egymást. Ebben az esetben lehetőség van használni a profiler-t, hogy orvosoljuk a problémát. Ogre MeshLodEditor ehhez segítő eszközt is nyújt. Megkeressük, hogy hányadik vertex redukciójánál történik, majd lekérdezzük ennek az élnek paramétereit és hozzáadjuk a profilhoz, módosított súllyal. A profiler-t arra is lehet használni, hogy kényszerítsünk egy él redukálását. 5.9 Mesh külsejénél súlyozása 38. ábra: Profiling előtti (felső) és utáni (alsó) kép A WorldForge lead grafikusa (Dean Buvier) vette észre legelőször, hogy a LOD redukciós algoritmusom rosszabbul teljesít épületeken. Ennek kutatások után kiderült az oka, hogy az algoritmus nem volt tisztában azzal a ténnyel, hogy melyik vertex/háromszög látszik kívülről és melyik belülről. Például a ház fala két oldalról is látszik: Egyik belülről, másik kívülről. Így ha X mennyiségű vertex-et redukálunk, akkor normális esetben csak X/2 vertex redukálódik kívülről és X/2 belülről. Ez nem volt optimális, mert ha először a belső vertex-eket, majd a külsőt redukáljuk, akkor sokkal jobb minőség érhető el. Szakirodalomban nem találtam erre a problémára specifikus algoritmust, ezért egy saját algoritmus fejlesztésébe kezdtem. 50

5.9.1 Specifikáció A specifikációban ismertetem, hogy a problémára, amelyre a megoldást kerestem, mik voltak a bemenetei, kimenetei és optimalizációs szempontjai. Bemenet: Egy mesh LodData reprezentációban. Feltételezzük, hogy ez a mesh tartalmaz belső vertex-eket, különben nem lenne értelme az algoritmusnak. Kimenet: Egy vertex lista, amely megmondja, hogy melyik vertex látszik kívülről és melyik nem. Szempontok: Az Ember kliens mesh betöltésekor, valós időben és egyetlen háttérszálon generálja a LOD szinteket, így a teljesítmény fontos szempont, míg minőségben kisebb hibák megengedettek. 5.9.2 Algoritmus összefoglaló Az algoritmus két lépésből áll, amelyeket következő alfejezetekben részletezek: 1. Konvex hull meghatározása 2. Konvex hull pontjaiból kiindulva kívülről látszó face-ek bejárása és megjelölése. Az algoritmus 2D-ben egyaránt működik, ezért a 39. ábra használatával mutatom be a problémát 2D-ben. Az ábrán a jelölések a következők: Kék pontok: a megjelölendő külső pontok (Algoritmus célja). Piros vonalak: a konvex hull oldalai (face). Piros nyíl: a konvex hull adott lapjának normálja. Fekete vonalak: a mesh oldalai (mesh face). Fekete nyíl: a mesh adott lapjának normálja. 39. ábra: Mesh külsejének detektálása 51

5.9.3 Konvex burok meghatározása Egy pontrendszer konvex burka az a minimális konvex halmaz, amely tartalmazza a pontrendszert. Másképpen, adott vertex pozíció halmazra egy olyan burkot keresünk, amelynek térfogata maximális. Az alábbi képen (40. ábra) kékkel jelölt terület a konvex burok része. 40. ábra: Kék konvex burok a modell ponthalmazából A következő algoritmusokat vizsgáltam meg a konvex burok algoritmus fejlesztése előtt, amelyre az implementációm egyedi megoldása alapszik: Gift wrapping [41]: O(nh) Graham s scan [42]: O(n log n) Chan algoritmusa [43]: O(n log h) Incremental [44]: O(n log n) Quick hull [45]: O(n log n) Ahol n=bement pontjainak száma és h=konvex burok pontjainak száma. Mindegyik algoritmus megvalósítható 2D-ben és 3D-ben egyaránt, de első három inkább 2D-ben, míg utolsó kettő inkább 3D-ben hatékony. 5.9.3.1 Gift wrapping A gift wrapping [41] algoritmus a következőképpen működik (41. ábra): 1. Két változóra lesz szükségünk P pont és Dir irányvektor. 2. Kezdetben legyen P a minimális X koordinátával rendelkező pont és legyen Dir a Y tengely. 52

3. Keressük meg az irányvektorhoz képest legkisebb szöget bezáró pontot és legyen ez a pont Pnext. 4. Legyen Dir a Pnext-P vektor és legyen P=Pnext. 5. Folytassuk az algoritmust, míg vissza nem jutunk a kezdőpontba. 41. ábra: Gift wrapping algoritmus két dimenziós térben 5.9.3.2 Graham s scan Graham scan [42] a következő lépésekből áll: 1. Jelöljük ki a minimális X koordinátával rendelkező pontot 2. Számítsuk ki összes többi ponttal bezárt szöget. 3. Rendezzük a szögeket. O(n log n) 4. Járjuk végig a rendezett pontokat úgy, hogy ügyelünk a konkáv fordulatokra. 5. Ha konkáv fordulat van, akkor ki kell venni előző pontot a burokból. 6. Folytassuk az algoritmust, míg vissza nem jutunk a kezdőpontba. 5.9.3.3 Chan algoritmusa A Chan algoritmusa [43] rendelkezik a legjobb matematikai komplexitással O(n log h), de ahhoz hogy ezt elérje h partíción futtatja le a Graham s scan algoritmust, majd Gift wrapping algoritmussal kombinálja egybe a h darab burkot. Ehhez először tudni kell a kimenet pontjainak számát, amelyet csak approximálni lehet, valamint annyira sok extra számítást igényel, hogy nem lesz gyorsabb, mint a QuickHull algoritmus három dimenzióban. 53

5.9.3.4 Inkrementális burok Az inkrementális burok [44] könnyen vizualizálható 3D-ben, ezért innentől fogva 3D térben levő burokról lesz szó. Határozzuk meg a miny pontot, majd ettől legtávolabb levő pontot, majd előző két pont közötti egyenestől legtávolabbi pontot, majd e háromszögtől legtávolabbi pontot. Ez azért fontos, mert így már az elején kapott tetraéderrel nagy terület lefedhető, és kevesebb iterációt igényel az algoritmus. 1. Hozzuk létre e négy pont által meghatározott tetraédert. 2. Válasszunk egy véletlen pontot, amelyet még nem dolgoztunk fel. 3. Horizont megkeresés: Keressük meg azokat a konvex burok lapokat, amelyekből látszik a pont. 4. Ha nem találtunk ilyet, akkor a burkon belül van és eldobható a pont. 5. Ha találtunk, akkor szedjük ki a lapokat és a keletkező lyuk széleit kapcsoljuk össze az új ponttal. 6. Folytassuk az algoritmust, míg összes pontot fel nem dolgoztuk. 5.9.3.5 Quick Hull A Quick hull [45] ugyanazon az elven működik, mint az inkrementális algoritmus pár kivétellel: Mindig a legtávolabbi vertex-et kell választani. Minden konvex burok laphoz társítunk egy listát a látszó pontokról, majd amikor a pontból látható lapokat határozzuk meg, akkor csak a lap szomszédjait kell tesztelni. 5.9.3.6 Általam kidolgozott módszer Míg a quick hull particionálja a pontokat (quicksort-hoz hasonlóan), ez nem teljesen igaz a horizont meghatározásnál 3D-ban, mivel a szomszédokat is meg kell vizsgálni. Valamint az a tény, hogy vertex-enként rendezni kell, a méréseim szerint körülbelül ugyanolyan gyors, mint az inkrementális algoritmus. Így én a Quick hull és inkrementális algoritmus kombinációját alkalmaztam: Inkrementális algoritmus, azzal a feltétellel, hogy mindig a legtávolabbi pontot válasszuk ki. 54

Különös problémát jelentett az egy síkban levő pontok. 3D modellekben tipikusan előfordul, hogy 4 pont egy síkban van, ekkor az algoritmusom lebegőpontos számítási hibákba ütközött. Már a lebegő pontos epszilon hiba meghatározása is problémás volt. Végül erre az epszilonra működik: = h 4 Így ennek az epszilonnak ismervén le tudtam tesztelni, hogy egy síkban van-e vagy egy vonalon van-e vagy ugyanott van-e egy pont. Ezeket külön-külön kezelni kellett, ugyanis ekkor az algoritmus hibázik. 5.9.4 Lapok bejárása Ebben az alpontban az én intuitív külső burok meghatározó algoritmusomat mutatom be. Kiindulási alapként vesszük az előző alfejezetben tárgyalt (5.9.3.6. fejezet) konvex burok algoritmusát, meghatározzuk a mesh lapjainak normáljait és a konvex burok lapjainak normálját. Hozzunk létre egy stack-ot, amibe később a mesh vertexeit lehet berakni. Valamint minden vertex megjelölhető külső vertex-ként, alapértelmezetten a konvex burok vertex-ei meg vannak jelölve. Az algoritmus röviden a következő lépésekkel jelöli meg a konvex burok pontjaiból kiindulva a kívülről látszó vertex-eket: Minden konvex hull (CH) lapra: Tegyük be a lap pontjait a stack-ba. Legyen az NCH a lap normálja. Amíg a stack nem üres, egyesével dolgozzuk fel a stack vertexeit: o Jelöljük meg a vertex-et külső vertex-ként. o Keressük meg azokat a szomszédos lapokat, amelyek normálja 90 -nál kisebb szöget zárnak be az NCH-val, és ha ezek pontjai, ebben a CH lap ciklusban még nem kerültek bele a stack-ba, akkor rakjuk be. 55

Ez az algoritmus a következő eredményeket nyújtotta épületeknél (42. ábra): 42. ábra: Eredmény, mesh külsejének súlyozása nélkül(bal) és súlyozással(jobb) 56

5.10 Autokonfigurálás Implementáltam egy hiba arányos redukciót is, amely automatikusan, egyetlen beállítás nélkül generálja LOD szinteket, így ha bármilyen mesh-t betöltünk, akkor gyorsabb lesz az alkalmazásunk (43. ábra). Pontosan négy LOD szintet fog generálni, mert tapasztalatom szerint négy LOD szint generálása már elegendőnek bizonyult. A LOD szinteknek két fő jellemzőt kell megadnunk: 1. Mennyit redukáljon 2. Milyen távolságban jelenjen meg adott redukció. 5.10.1 Redukció mértékének meghatározása Három féleképpen lehet megadni a redukálandó vetex-ek számát, amelyek a következőek: Vertex count: Megadjuk, hogy hány vertex-el redukálja a mesh-t. Proportional: Megadjuk, hogy hány százalékkal redukálja a mesh-t. Collapse cost: Megadjuk, hogy mekkora hibáig redukálja a mesh-t. Automatikus rendszernél, amely bármilyen mesh-el működik, természetesen a collapse cost alapú redukciót kellett választanom. Mivel a collapse cost függ az él hosszától, ezért bele kellett vonnom a számításba a mesh bounding sphere radius-t, ami amúgy is használva lesz a pixel count távolságoknál (5.10.2. fejezetben ismertetem). Amikor ezt a rendszert létrehoztam, csak az első és utolsó LOD szint mértékét határoztam meg és a közte lévő értékekre pedig egy képlettel () számítottam ki és a következő redukciós konstansokra jutottam: 3125, 411, 97, 32. A 3125-ös konstansnál közel nulla hiba keletkezett, míg 32-nél még tűrhető minőségű mesh-eket generált. A következő képlet i=2-nél a legközelebbi LOD, míg i=6-nál legtávolabbi LOD költségét határozza meg: A redukciós konstans például a legtávolabbi LOD szintnél a következő képlettel határozható meg: 57

5.10.2 LOD megjelenítési távolság meghatározása A távolságot megadhatjuk pixel-ben és távolsági egységekben. Mivel távolság játékfüggő, ezért természetesen pixel alapú távolságokat határoztam meg. Pixel számnál valójában csak a mesh bounding sphere-jének a képen levő pixel számát tekinti, amely általában több mint a modell pixeleinek száma. Méréseim alapján legtöbb játéknak az optimális LOD távolságot a 3388608/ határozza meg, ahol i=[2..6] egész számok. Ha ezt az értéket átszámolnánk, akkor arra jutnánk, hogy ekkora pixel sugarú bounding sphere-nél vált LOD szintet: 260px (520x520px terület) 115px, 65 px, 42 px. Alábbi példán látható hogyan lehet átszámolni a pixel count-ot pixel sugárrá/területté. 43. ábra: Autokonfigurált LOD szintek jobbról-balra: eredeti mesh (9068 poly), LOD1 (8743 poly), LOD2 (5507 poly), LOD3 (3346 poly), LOD4 (2346 poly) 58

5.11 Mesh LOD generátor használata A MeshLodGenerator osztály képezi a rendszer front-end-jét. A felhasználó, csak MeshLodGenerator és LodConfig-ról kell tudnia. Minden más el van rejtve, így nem kell ismerni az implementációt a megértéshez, de tapasztaltabb fejlesztőknek lehetőséget nyújt saját komponensek használatára is. Így kezdőknek és tapasztalt fejlesztőknek is ajánlott a frontend-en keresztüli LOD generálás. A LodConfig struktúra tartalmazza összes beállítást, amelyel a felhasználó leírja a generálandó LOD szintet. Használat előtt ajánlanám a LodConfig osztály átnézését, hogy tudjunk összes beállítási lehetőségről. Viszont itt egy gyorstalpaló példa a használatához: #include <OgreMeshLodGenerator.h>... new MeshLodGenerator(); // Inicializálás... LodConfig config(mesh); // Paraméterben átadott mesh-t szeretnénk redukálni // 5 ogre worldspace egységnél 50%-osan redukált mesh-t szeretnénk config.creategeneratedlodlevel(5, 0.5); // Alapértelmezetten a függvény távolságot és proportionalt használ. config.creategeneratedlodlevel(10, 0.75); // Manual level, amit blender, maya vagy 3ds max segítségével hoztunk létre. config.createmanuallodlevel(15, "mymesh_lod3.mesh"); // Háttérszálon (non-blocked) vagy azonnal (blocked) generálást szeretnénk? config.advanced.usebackgroundqueue = true; MeshLodGenerator::getSingleton().generateLodLevels(config); // generálás // Vagy 1 soros autoconfigurált LOD szinteket is használhatunk: MeshLodGenerator::getSingleton().generateAutoconfiguredLodLevels(mesh);... delete MeshLodGenerator::getSingletonPtr(); // deinicializálás 44. ábra: Példa mesh LOD generátor használatára 59

5.12 Implementáció Az elején egy osztályból állt a teljes LOD Generátor, de ahogy újabb és újabb technológiákat raktam bele, már túl nagy lett és szükség volt egy újraszervezése (refactoring). A Következőképp osztottam fel 6 komponensre a MeshLodGenerator rendszert (Az 1. függelék tartalmazza a komponensek közötti kommunikáció szekvencia diagramját): LodData: Az algoritmushoz szükséges mesh háló reprezentációt tartalmazza, mint pl élek, háromszögek, vertex-ek, stb. LodCollapseCost: Él súlyát számító algoritmusok interfésze (görbületi hiba, négyzetes hiba, profiling, outside weight, normals). LodInputProvider: Egy adott forrásból LodData adatszerkezetet hoz létre (mesh vagy bufferelt(háttérszálhoz) ). LodOutputProvider: LodData adatszerkezet jelenlegi állapotáról képes egy kimeneti állományt generálni (mesh, bufferelt, mesh+tömörített vagy bufferelt+tömörített) LodCollapser: A legkisebb költségű élek alapján redukálja a vertex-eket, míg a kért mennyiséget el nem éri. Ő fog háromszögeket törölni/létrehozni. MeshLodGenerator: Frontend. Specifikálhatjuk a redukció paramétereit egy LodConfig struct kitöltésével, majd ha ezt átadjuk MeshLodGenerator-nak, akkor létrehozza a szükséges komponenseket a kért mértékig. 5.13 Végeredmény Kifejlesztettem egy mesh benchmark adatbázist, amelyben minden mesh-nél gyorsabb teljesítményt mutat az algoritmusom, átlagosan 8x sebességnövelést (eredmények a 2. függelékben szerepelnek). A sebességnövelés mértéke eléggé ingadozik, ugyanis jobb minőségnél kevesebb a szakadás keletkezik, kevesebb szakadásnál több a háromszög kerül egy vertex-re, így többet kell számolni. Mivel én algoritmusom sokkal jobb minőségű és kevesebb szakadást tartalmaz, így több számítást is kell végeznie, de ennek ellenére is bizonyos mesh-eknél 10x sebesség növekedés mérhető. 60

A mesh LOD minősége az ismertetett (5.1. fejezet - 5.4. fejezet) technikáknak köszönhetően minden modellnél szabad szemmel láthatóan feljavult. Erre pár példa a 45. ábra, 46. ábra és 47. ábra. 45. ábra: 90% redukció saját (bal) és Ogre3D (jobb) algoritmusával 46. ábra: 95% redukció saját (bal) és Ogre3D (jobb) algoritmusával 47. ábra: 90% redukció saját (bal) és Ogre3D(jobb) algoritmusával 61