České vysoké učení technické v Praze Fakulta elektrotechnická Katedra počítačů
Bakalářská práce
Simulátor pásového bojového vozidla Jan Horký
Vedoucí práce: Ing. Michal Hapala Studijní program: Elektronika a informatika, strukturovaný, bakalářský Obor: Výpočetní technika květen 2011
ii
Poděkování Na tomto místě bych chtěl poděkovat vedoucímu práce za pomoc při řešení bakalářské práce a poskytnutí informačních zdrojů. Dále bych chtěl poděkovat rodině za podporu a kamarádům za pomoc při testování. iii
iv
Prohlášení Prohlašuji, že jsem svou bakalářskou práci vypracoval samostatně a použil jsem pouze podklady uvedené v přiloženém seznamu. Nemám závažný důvod proti užití tohoto školního díla ve smyslu §60 Zákona č. 121/2000 Sb., o právu autorském, o právech souvisejících s právem autorským a o změně některých zákonů (autorský zákon) V Praze dne 25. 5. 2011 v
vi
Abstrakt Cílem bakalářské práce je návrh a implementace simulátoru pásového bojového vozidla ( tanku ) v protředí XNA game studio / .NET C#. Práce umožní uživateli pohybovat se v 3D prostředí s tankem po terénu vytvořeném pomocí výškové mapy a multitexturingu a bojovat s protivníky řízenými umělou inteligencí, Do programu bude zahrnuto také stínování, zobrazení zásahů po střelách na terénu a kolizní systém pro zjištění poškození tanku v závislosti na úhlu a místě dopadu střely.
Abstract The goals of this bachelor`s thesis are design and implementation of military tracked vehicle ( tank in this case ) in XNA game studio / .NET C# environment. This project will allow user to drive a tank in 3D space over a multitextured terrain created from heightmap. It will also allow the user to fight witch opponents controlled by AI. This application will also contain shading, display of grenade shell hits on terrain and collision system between tank and grenade based on location and angle of the hit.
vii
viii
Obsah 1.1 API používaná pro 3D grafiku.................................................................................................13 1.2 Hry zaměřené na simulaci tanku..............................................................................................14 2 Fyzikální model...............................................................................................................................15 2.1 Výběr vhodného fyzikálního enginu........................................................................................15 2.2 Základy konstrukce tanku........................................................................................................16 2.3 Realizace fyzikálního modelu tanku........................................................................................18 3 Grafická část....................................................................................................................................21 3.1 Úvod do vykreslování objektů v XNA....................................................................................21 3.2 Tvorba terénu pomocí výškové mapy......................................................................................21 3.3 Multitexturing..........................................................................................................................23 3.4 Zvýšení detailů textur blíže ke kameře....................................................................................24 3.5 Techniky pro tvorbu stínů........................................................................................................28 3.6 Decals.......................................................................................................................................37 3.7 Částicové efekty.......................................................................................................................39 3.8 Post-processing........................................................................................................................40 3.9 Optimalizace pro zvýšení výkonu............................................................................................41 4 Umělá inteligence............................................................................................................................45 4.1 Algoritmy používané v počítačových hrách............................................................................45 4.2 Popis implementace AI pomocí stavového automatu..............................................................48 4.3 Dodatečné úpravy pro správnou funkci AI..............................................................................49 5 Vliv povrchu terénu a další drobné detaily......................................................................................50 5.1 Vliv povrchu terénu na pohyb tanku........................................................................................50 5.2 Převodovka..............................................................................................................................50 5.3 Pružinová kamera....................................................................................................................50 5.4 Poškození tanku.......................................................................................................................50 6 Zhodnocení vlastní práce.................................................................................................................52 7 Testování..........................................................................................................................................54 8 Použitá literatura a další zdroje.......................................................................................................56 9 Uživatelská příručka........................................................................................................................59 9.1 Spuštění....................................................................................................................................59 9.2 Ovládání ..................................................................................................................................59 9.3 Cíle hry....................................................................................................................................60
ix
x
Seznam obrázků: Obrázek 1.1 - M1 Tank Platoon .................................................................................14 Obrázek 1.2 - M1 Tank Platoon II .............................................................................14 Obrázek 1.3 - Panzer Elite .........................................................................................14 Obrázek 1.4 - T72: Balkans on fire ...........................................................................14 Obrázek 2.1 - Logo BEPUphysics ............................................................................ 15 Obrázek 2.2 - Ukázka z dema BEPUphysics ............................................................ 15 Obrázek 2.3 - Ukázka z dema BEPUphysics (2) ...................................................... 15 Obrázek 2.4 - BEPUphysics spoj - BallSocketJoint ..................................................15 Obrázek 2.5 - BEPUphysics spoj - WeldJoint ...........................................................15 Obrázek 2.6 - Detekce kolizí - broad phase .............................................................. 16 Obrázek 2.7 - Schéma simulovaného tanku Pz II ..................................................... 16 Obrázek 2.8 - Vliv skloněného pancíře ..................................................................... 17 Obrázek 2.9 - Ukázka přídavného pancéřování - spaced armor ................................ 18 Obrázek 2.10 - Drátěný model vozidla ...................................................................... 18 Obrázek 2.11 - Fyzikální model tanku ...................................................................... 19 Obrázek 2.12 - Statická detekce kolizí ...................................................................... 20 Obrázek 2.13 - Detekce kolizí pomocí vrhání paprsku ..............................................20 Obrázek 3.1 - Výšková mapa .....................................................................................22 Obrázek 3.2 - Výpočet normál pro osvětlení .............................................................22 Obrázek 3.3 - Interpolace mezi texturami o vysokém a nízkém rozlišení .................24 Obrázek 3.4 - Textury v nízkém rozlišení ..................................................................25 Obrázek 3.5 - Textury v příliš vysokém rozlišení ......................................................25 Obrázek 3.6 - Interpolace mezi vysokým a nízkým rozlišením textury ....................26 Obrázek 3.7 - Základní princip stínování ..................................................................26 Obrázek 3.8 - Vytvoření shadowmapy ......................................................................26 Obrázek 3.9 - Vytvoření stínů podle shadowmapy ...................................................26 Obrázek 3.10 - Přizpůsobení shadowmapy pohledu kamery ................................... 28 Obrázek 3.11 - Nedostatečné vzorkování shadowmapy ...........................................28 Obrázek 3.12 - Nepřizpůsobení shadowmapy perspektivní projekci kamery ..........28 Obrázek 3.13 - Ukázka stínů ve hře Carmageddon II ..............................................29 Obrázek 3.14 - Shadowmapa bez optimalizací ........................................................30 Obrázek 3.15 - Shadowmapa přizpůsobená pohledu kamery ..................................30 Obrázek 3.16 - Statická shadowmapa ......................................................................30 Obrázek 3.17 - Deferred shadow maping, kaskádové shadowmapy a PCF ............31 Obrázek 3.18 - Decals na terénu ..............................................................................32 Obrázek 3.19 - Textura bez bump-mappingu .......................................................... 33 Obrázek 3.20 - Normálová mapa textury ................................................................ 33 Obrázek 3.21 - Textura po použití bump-mappingu ................................................33 Obrázek 3.22 - Částicové efekty - kouř z výfuku, prach za tankem .........................33 Obrázek 3.23 - Částicové efekty - střelba .................................................................33 Obrázek 3.24 - Částicové efekty - oheň ....................................................................34 Obrázek 3.25 - Částicové efekty - zásah terénu ........................................................34 xi
Obrázek 3.26 - Vertikální rozmazání .........................................................................35 Obrázek 3.27 - Chyby při použití úrovní detailu .......................................................36 Obrázek 3.28 - Střední detaily terénu ........................................................................36 Obrázek 3.29 - Nízké detaily terénu ..........................................................................37 Obrázek 3.30 - Vypuštění detailů u tanku při pohledu z dálky ................................. 37 Obrázek 4.1 - Algoritmus A * ....................................................................................40 Obrázek 4.2 - FSM protivníka ..................................................................................41 Obrázek 4.3 - Kontrola terénu před vozidlem ...........................................................32
xii
1 Úvod Cílem této práce je navrhnout a implementovat hru - simulátor tanku v prostředí XNA game studio v programovacím jazyku C# a s tím související výběr vhodného fyzikálního enginu pro toto prostředí. Pro vytvoření odpovídajícího fyzikálního modelu je také potřeba znát základy konstrukce a ovládání pásových bojových vozidel, část práce se touto problematikou zabývá. U takovéto počítačové hry je důležitý nejen fyzikální model, ale také grafické zpracování a umělá inteligence protivníků. Práce se proto zabývá jak základy umělé inteligence v počítačových hrách, tak také tvorbou terénu z výškové mapy s použitím více textur pro jednotlivé povrchy, tvorbou decals ( textury namapované na objekty při běhu hry - v tomto případě viditelné zásahy po střelách na terénu ) a technikami používanými pro stínování. 1.1 API používaná pro 3D grafiku Během 80. let a na počátku devadesátých let neexistovalo jednotné API pro programování grafiky, pro jednotlivé grafické karty tak bylo třeba psát různá rozhraní. Na počátku 90. let proto firma SGI (Silicon Graphics) vydala standard OpenGL, který řešil přístup k hardwaru. OpenGL je psáno v jazyce C, je multiplatformní a používá se pro 2D i 3D grafiku. Současná verze OpenGL je 4.1 a všechny verze jsou zpětně kompatibilní. OpenGL se uplatňuje jak v profesionálních aplikacích tak i v počítačových hrách, kde je jeho konkurentem rozhraní Direct3D vyvíjené Microsoftem, Direct3D je součástí API DirectX. První verze Direct3D vznikla v polovině 90. let, v současnosti je nejvyšší verze 11. Direct3D je použitelné pouze pod Windows, jednotlivé systémy Windows se navíc liší nejvyšší verzí Direct3D, která je v nich použitelná. V roce 2006 vydal Microsoft první verzi frameworku XNA Game Studio, který je založený na .NET frameworku a poskytuje velkou sadu funkcí důležitých pro tvorbu her. Nevýhodou .NET (a tedy i XNA) je především vyšší náročnost programů na hardware v porovnání s Direct3D a OpenGL. Ta byla v našem případě ještě zvýšena použitím programovacího jazyka C# (vpodstatě mix C++ a Javy), který je pomalejší než C/C++.
13
1.2 Hry zaměřené na simulaci tanku Během desítek let vývoje počítačových her se už mnohokrát řešil problém simulace tanku a vznilky k němu dva rozdílné přístupy - buď se jedná o poměrně realistickou simulaci ve které chování tanků odpovídá realitě, nebo je naopak ovládání co nejvíce zjednodušeno, tento přístup je vidět zejména u konzolových her. Asi první významnou hrou zaměřenou na simulaci tanku byla hra M1 Tank Platoon vydaná firmou Microprose v roce 1989 pro PC, Amigu a Atari ST. V této hře bylo úkolem hráče ovládat tank M1 Abrams a zároveň zadávat příkazy ostatním jednotkám. Hra byla úspěšná a v roce 1998 následovalo pokračování M1 Tank Platoon II.
Obrázek 1.1 - M1 Tank Platoon (1989)
Obrázek 1.2 - M1 Tank Platoon II (1998)
Dalším významným titulem byla hra Panzer Elite, kterou vydali Psygnosis v roce 1999. hra byla zaměřená na 2. sv válku a velmi realisticky zachycovala tankový boj včetně dobývání měst a boje proti pěchotě, která například ve hře Panzer Commander zcela chyběla. Z novějších her zmíním T72: Balkans on fire vyvinutou v Rusku. Tato hra vyniká zejména velmi realistickou simulací chování tanků ovládaných hráčem, konkrétně T34/85 T54/55 a T72. Jako poslední uvedu ještě hru Operace Flashpoint, která umožňuje ovládat velké množství různé bojové techniky včetně pasové, její simulace je zde ovšem oproti výše zmíněným hrám zjednodušena.
Obrázek 1.3 - Panzer Elite
Obrázek 1.4 - T72: Balkans on fire
14
2 Fyzikální model 2.1 Výběr vhodného fyzikálního enginu Jak jsem již zmínil, výběr fyzikálního enginu je důležitým krokem při vývoji hry a významně ovlivní výsledek práce. Vzhledem k vybranému prostředí ( XNA framework verze 3.1, C# ) bylo potřeba vybrat pro hru fyzikální engine který bude v tomto prostředí bez problémů fungovat. Po prostudování bakalářské práce [1] ve které jsou jednotlivé enginy popsány jsem se rozhodl pro BEPUphysics knihovnu [2] verze 0.14.3, která je vytvořena přímo pro XNA a je zdarma pro nekomerční použití. Výhodou této knihovny je také velmi kvalitní dokumentace včetně ukázkových úloh, neustálý vývoj ( nyní již vyšla verze 0.15.2 která obsahuje mnoho vylepšení a zrychlení ). Tato knihovna má také již implementovanou třídu pro fyzikální model auta ( ve verzi 0.14.3 byl pomocí ní vytvořen i nepříliš dokonalý model tanku, který ale nebyl použit v žádném demu ).
Obrázek 2.1
[1]
Obrázky 2.2, 2.3 - ukázky z dema BEPUpsysics
Nevýhodou BEPUphysics je, že simuluje pouze rigidní fyziku ( tělesa nejsou deformovatelná ), což je ale vlastnost většiny fyzikálních enginů. To ale neznamená že nejde simulovat například oddělování jednotlivých částí z modelu, BEPUphysics umožňuje definovat vztahy mezi objekty pomocí mnoha druhů spojů a kloubů a v případě potřeby takový spoj zase zrušit.
Obrázek 2.4 [3] - Ball socket joint kloubový spoj, snaží se držet obě tělesa ve stejné vzdálenosti
Obrázek 2.5 [3] - Weld joint objekty jsou pevně spojeny
15
BEPUphysics také poskytuje kvalitní kolizní systém těles s možností definovat si chování při kolizích pro různé objekty. Kolize objektů sestává ze tří částí: 1. Broad phase: Zjištuje se jestli je v okolí objektu ( jednoduchý bounding box ) nějaký jiný objekt se kterým by mohl kolidovat
Obrázek 2.6 [4] broad phase kontroluje se okolí objektů
2. Narrow phase: Pokud se v 1. fázi zjistí, že by mohlo dojít ke kolizi, následuje tato fáze, ve které se zjistí, jestli spolu objekty opravdu kolidují. 3. Response: Pokud došlo ke kolizi, vypočte se, jakým způsobem mají objekty reagovat. Kolizní systém je také možno v libovolné fázi zastavit. 2.2 Základy konstrukce tanku
Obrázek 2.7 [5] - Rozložení základních částí tanku Panzer II 16
Na schématu výše je popsáno základní rozložení částí německého druhoválečného tanku Panzer 2, který je ve hře použit, nicméně schéma je platné pro většinu tanků od konce 30. let do dnešní doby - někdy jsou poháněna zadní kola, někdy přední. Zvláštní skupinou jsou takzvané kolopásové tanky, které mají poháněna pojezdová kola a mohou se tak pohybovat i bez pásů, tato konstrukce se ale již nepoužívá. Zatáčení tanku se provádí přibržděním jednoho z pásů, případně pohybem jednoho pásu v opačném směru. Pancéřování tanku se na různých místech liší podle toho jak je pravděpodobné že bude na toto místo tank zasažen - vpředu a na bocích bývá nejsilnější, zadní je slabší. Nejslabší pancéřování je spodní a vrchní ( vrchní někdy u věže zcela chybí ). Stejně tak je důležitý sklon pancíře, jehož vhodná volba může velmi zvýšit ochranu vozidla. Střela musí u skloněného pancíře probít větší část pancíře, než jím pronikne. Skloněný pancíř také zvyšuje šanci, že se střela odkloní nebo zcela odrazí. Z obrázku je vidět, že k pokrytí plochy skloněným pancířem je potřeba větší množství materiálu, nicméně skloněný pancíř dovoluje tvarovat tank podle potřeby. Nicméně skloněný pancíř má také nevýhody - u některých druhů munice nedochází při nárazu do šikmého pancíře k odklonu, v některých případech může být dokonce proražen snadněji - to se týká vrstvených pancířů ( např. chobham u britských tanků ).
Obrázek 2.8 - Vliv skloněného pancíře
Dalším způsobem zvýšení pasivní ochrany tanku je Spaced armor - okolo důležitých částí jsou umístěny v dostatečné vzdálenosti tenké pancéřové pláty. Při zásahu tanku tak střela exploduje a probije pouze toto dodatečné pancéřování, samotný tank zůstane nepoškozen. Tato ochrana ale není účinná proti podkaliberní munici s vysokou rychlostí. Používala se především v 2. sv válce, nyní je nahrazena reaktivním pancéřováním. Nicméně proti pomalu letícím střelám z přenosných raketometů se používá obdoba tohoto pancíře tvořená pouze mříží namísto plných plátů.
17
Obrázek 2.9 - typický příklad dodatečného pancéřování - spaced armor na věži německého tanku Panzer IV z 2. sv války
2.3 Realizace fyzikálního modelu tanku Nyní se dostáváme k samotné realizaci tanku pomocí knihovny BEPUphysics. Jak jsem již zmínil, knihovna obsahuje třídu pro vozidla, ze které jsem vycházel.V této třídě je vozidlo reprezentováno tělem ( body ), které je tvořeno libovolným fyzikálním objektem ( entitou )vytvořeným pomocí knihovny, a libovolným počtem kol. Kola nejsou řešena pomocí válců, ale pomocí raycastingu - vysílání paprsků. Každé kolo má své vlastní odpružení a také svůj vlastní motor.
Obrázek 2.10 - drátěný model vozidla vytvořeného pomocí třídy Vehicle v BEPUphysics. Kola řešena pomocí raycastingu.
18
Tank se ovšem narozdíl od auta pohybuje po pásech a poháněná kola jsou navíc umístěna nad zemí. Vytvoření takovéhoto fyzikálního modelu tanku by bylo velmi složité a zřejmě i náročné na výpočty při běhu programu - každý pás by musel být složen z velkého množství článků a bylo by třeba vyřešit svlékání pásů z podvozku. Rozhodl jsem se proto pro jednodušší způsob - nahradit pás velkým množstvím kol, přičemž budou všechna poháněná a samotný pás nebude nijak fyzikálně reprezentován, bude tvořen pouze grafickým modelem s pohyující se texturou. Tento způsob je používán v naprosté většině počítačových her ( nevzpomínám si na jedinnou hru s pásy vytvořenými pomocí jednotlivých článků, ale možná taková existuje ). Nevýhodou tohoto řešení je, že vzhledem k jedinnému grafickému modelu pásu a chybějícímu fyzikálnímu modelu nelze simulovat situace jako přetržení nebo svléknutí pásu. Tělo tanku není možné reprezentovat jednoduchým tvarem jako u ukázkového vozidla, ale musí poměrně přesně reprezentovat tvar tanku. Důvodem je, že i když by na samotné kolizní těleso stačily 1 až 2 kvádry, je u tanku důležitý sklon pancíře pro správné vyhodnocení zásahů. Z tohoto důvodu je kolizní těleso tanku vytvářeno ze zjednodušeného modelu tanku pomocí konvexních těles. Tento způsob je sice výpočetně náročnější, ale zachovává základní tvary tanku včetně sklonu pancíře, což je pro tankový simulátor nutné. Stejným zůsobem ( tedy pomocí konvexních těles vytvořených z grafického modelu ) je reprezentována i věž tanku. Ta je k tělu tanku připojena spojem RevoluteJoint, který omezuje vzájemný pohyb těles tak, že se smí otáčet pouze okolo definované osy. Tento spoj má také motor, který zajištuje otáčení věže.
Obrázek 2.11 - Fyzikální model tanku vytvořený pomocí konvexních těles
19
Posledním problémem, který bylo potřeba vyřešit, bylo zjištění a vyhodnocení kolizí střel s tankem, terénem a případně i dalšími objekty. Střelbu je možné řešit dvěma různými způsoby: 1. Střely jsou fyzikální objekty s definovanou počáteční rychlostí a pohybují se po balistické křivce. 2. Střely se pohybují po přímce nekonečnou rychlostí, tento způsob ignoruje jakoukoli balistiku a pro simulaci střelby tankového kanonu je nevhodný, hodí se spíše do FPS, kdy se střely pohybují na krátké vzdálenosti, zvolil jsem proto první způsob. V BEPuphysics jsou kolize kontrolovány při každé aktualizaci fyzikálního modelu. Protože se střely pohybují vysokou rychlostí, může nastat situace, že se při jednotlivých updatech fyzikálního enginu střela dostane skrz překážku, do které by správně měla narazit. Tento problém je znázorněn na obrázku 2.11.
Obrázek 2.12 - rychle se pohybující objekt prošel překážkou, do které by měl správně narazit
Obrázek 2.13 - kolize s překážkou pomocí raycastingu tento problém odstraňuje
Tento problém jsem vyřešil tak, že samotný fyzikální model střely ignoruje jakékoli kolize a místo toho vysílá před sebe paprsek dostatečné délky aby se zamezilo prolétnutí skrz objekt. Pomocí této metody se dá také snadno zjistit místo a úhel dopadu, tedy informace nutné k vyhodnocení účinku střely. Vzhledem k modelovanému typu tanku jsem do simulátoru nezahrnul vlivy dodatečné pancéřování, vrstvených pancířů a podkaliberní munice, protože v té době ještě neexistovaly. Jejich implementace by byla nicméně až na reaktivní pancéřování poměrně jednuduchá.
20
3 Grafická část 3.1 Úvod do vykreslování objektů v XNA Na začátku kapitoly věnované grafickému zpracování hry krátce vysvětlím způsob vykreslování objektů v prostředí XNA. Veškeré objekty jsou vykreslovány pomocí takzvaných "efektů" - souborů psaných v jazyce podobném C#, pomocí kterých se definují funkce pro vertexshader a pixelshader grafické karty. Ačkoli existuje v XNA třída basic effect, poskytuje ( jak už název napovídá ) jen velmi omezené možnosti. Přesunem části výpočtů z CPU na GPU se navíc dosáhne urychlení, GPU provádí grafické operace mnohem rychleji. Každý efekt může obsahovat několik technik vykreslování, techniky můžou být jak jednoprůchodové tak i víceprůchodové, což může ovšem podstatně snížit framerate. Každý efekt může ( vpodstatě musí ) mít definováno několik globálních proměnných, do kterých se načítají potřebné matice ( world, view, projection, případně i další ), textury nebo jiné hodnoty podle potřeby. Jak je vidět na obrázcích 2.2 a 2.3, v demonstračních úlohách BEPUphysics už je implementováno vykreslování pomocí třídy ModelDrawer, hodí se ovšem pouze na vykreslování fyzikálních objektů, na vykreslování vlastní grafické scény je díky své složitosti a velmi omezeným možnostem naprosto nepoužitelný - ani tak nemá být použit, je určen pouze k vykreslování fyzikálních modelů, spojů a dalších záležitostí úzce spjatých s fyzikálním enginem. 3.2 Tvorba terénu pomocí výškové mapy V Počítačových hrách se obvykle používají dva rozdílné přístupy pro tvorbu herního světa - buď je načítán z grafického modelu tak jako ostatní objekty ve hře a nebo je vytvořen přímo hrou z načtené výškové mapy. Druhý způsob neumožňuje vytvářet víceúrovňové levely a hodí se spíše na tvorbu přírodního terénu. Vzhledem k tomu, že nemám zkušenosti s grafickými programy pro tvorbu modelů a charakteru modelovaného terénu jsem se rozhodl pro tvorbu terénu z výškové mapy.
21
Výšková mapa je 2D pole, u kterého má každá hodnota pole na souřadnicích [x,y] přidělenu hodnotu, která určuje výšku terénu v tomto místě. Výšková mapa je nahrána ve formě textury ve stupních šedi ( černá je nejnižší úroveň, bílá nejvyšší ) a pro každý rozměr je možné nastavit měřítko ( například z textury 512x512 je tedy možné vygenerovat terén o rozměrech 2048x2048 ). Pro vytvoření výškové mapy jsem použil program L3DT - Large 3D Terrain verze 2.8 [6]. S tímto programem jsem se seznámil v předmětu X36ZPG - Základy počítačové grafiky a osvědčil se mi. Z takto vytvořené mapy jsem tedy vytvořil terén složený z trojúhelníků. U výškové mapy jsou velmi důležité plynulé přechody, jinak by se v terénu měnila výška nepřirozeně prudce. Z tohoto důvodu nešlo vytvořit výškovou mapu pomocí programů pro práci s 2D grafikou, ale bylo potřeba použít právě L3DT.
Obrázek 3.1 - výšková mapa ze které je vytvářen terén
U takto vytvořeného terénu je ještě potřeba kvůli výpočtu osvětlení definovat normály buď pro celé trojúhelníky nebo pro jednotlivé vrcholy ( tento způsob jsem použil ). Pokud jsou normály počítány ve vrcholech, zamezí se ostrým přechodům v úrovni osvětlení. Při řešení jsem čerpal z [7], odkud pochází i níže uvedený obrázek.
Obrázek 3.2 [9] - a) normály počítány pro celé trojúhelníky, výsledkem je skoková změna osvětlení b) normály počítány ve vrcholech ( součet normál sousedních trojúhelníků, následná normalizace výsledného vektoru )
22
3.3 Multitexturing Ačkoli lze vykreslovat terén s použitím pouze jedné neustále se opakující textury, mnohem věrnějším zobrazením je použití více textur pro různé materiály s tím že použitá textura je určena například výškou daného místa v terénu. Já jsem zvolil jiný přístup - zastoupení jednotlivých materiálů na každém místě v terénu je nacítáno z textury podobné výškové mapě, hodnoty však neurčují výšku, ale procentuální zastoupení daného povrchu na daném místě. Použil jsem celkem 6 druhů povrchu: tráva, písek, bažina, bláto, skalnatý povrch a prašná cesta. Pro každý vrchol terénu se tedy ukládá celkem 6 hodnot, které reprezentují zastoupení těchto materiálů, normála v daném vrcholu, souřadnice textury a samozřejmě pozice vrcholu. S touto problematikou jsem se podrobně seznámil ze zdroje [8]. Následující část kódu ukazuje strukturu každého vrcholu terénu. public struct VertexMultitextured { public Vector3 Position; public Vector3 Normal; public Vector4 TextureCoordinate; public Vector4 TexWeights1; public Vector2 TexWeights2; public static int SizeInBytes = (3 + 3 + 4 + 4 + 2) * sizeof(float); public static VertexElement[] VertexElements = new VertexElement[] { new VertexElement( 0, 0, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Position, 0 ), new VertexElement( 0, sizeof(float) * 3, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Normal, 0 ), new VertexElement( 0, sizeof(float) * 6, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 0 ), new VertexElement( 0, sizeof(float) * 10, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 1 ), new VertexElement( 0, sizeof(float) * 14, VertexElementFormat.Vector2, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 2 ), }; }
23
3.4 Zvýšení detailů textur blíže ke kameře Při použití multitexturování vypadá terén z dálky poměrně dobře, při přiblížení kamery jsou ovšem textury rozmazané. To je způsobeno tím, že textury mají stále stejné rozlišení ať jsou od kamery jakkoli daleko. Nejjednodušším způsobem by tedy bylo na pevno zvýšit rozlišení textur. Toto řešení s sebou ale přináší opačný problém ačkoli nyní textury vypadají při pohledu zblízka hezky, při pohledu z větší vzdálenosti se projevuje nepříjemné zrnění způsobené tím, že textura má mnohem větší velikost, než na kterou se ji snažíme zobrazit. Řešením tohoto problému je použít vysoké rozlišení na oblast blízko u kamery a nízké na vzdálenou část, s touto technikou jsem se seznámil z [8] a jako nejvhodnější řešení ji následně implementoval. Použitím této techniky se zbavíme všech tří zmíněných problémů ( rozmazání, zrnění a viditelné opakování vzorků ). Celá tato operace bude prováděna pouze na pixelshaderu, zatížení CPU se nijak nezvýší. Vzhledem k tomu, že záleží na vzdálenosti bodu od kamery, je třeba ve vertex shaderu tuto vzdálenost ( hloubku ) zjistit. Pro zjištění hloubky ( v rozsahu 0 až 1) vykreslovaného bodu je třeba podělit souřadnice Z a W pozice bodu zjištěné ve vertex shaderu. Souřadnice Z je vzdálenost od kamery, W je homogenní souřadnice, jejich podíl je právě hloubka pixelu v rozsahu 0 až 1. Následující operace se týkají již pouze pixelshaderu. Je třeba si definovat vzdálenost, ve které začne přechod od vyššího rozlišení k nižšímu, délku tohoto přechodu a funkci která provede interpolaci. Všechny pixely, které jsou blíže ke kameře než je vzdálenost počátku přechodu budou mít texturu o vysokém rozlišení, pixely ve vzdálenosti větší než je součet této vzdálenosti a délky přechodu budou mít texturu o nižším rozlišení. Pixely mezi nimi budou mít texturu vytvořenou interpolací mezi oběma rozlišeními.
Obrázek 3.3 [8] - ukázka interpolace mezi texturami v závislosti na hloubce pixelu
24
Obrázek 3.4 - textury terénu jsou blíže u kamery vlivem malého rozlišení rozmazané
Obrázek 3.5 - opačný problém: textury dále od kamery mají příliš velké rozlišení a dochází tak k zrnění. Navíc je velmi patrné opakování jednotlivých textur v pásech
25
Obrázek 3.6 - řešením je použití vysokého rozlišení blízko kamery a nízkého ve větší vzdálenosti
3.5 Techniky pro tvorbu stínů Za mnoho let vývoje her se již mnohokrát řešil problém tvorby stínů objektů a byly nalezeny různé způsoby lišící se kvalitou stínů, výpočetní náročností i složitostí implementace. Zobrazování stínů s sebou obecně přináší několik problémů, které zde také zmíním. Existují dva základní principy stínování - buď stínování založené na geometrii objektů ( geometry-based ) nebo na vytváření shadowmapy ( image-based).
Obrázek 3.7 [9] - základ stínování
Obrázky 3.8, 3.9 [9] - vlevo: v prvním průchodu je scéna vykreslena z pohledu světla do hloubkové mapy vpravo: při druhém průchodu je scéna vykreslena z pohledu kamery, je proveden depth-test a podle jeho výsledu je pixel buď osvětlen nebo zastíněn 26
Základním přístupem společným pro většinu takzvaných image-based technik (založené na generování shadowmapy) je vykreslení scény ve dvou průchodech. V prvním je vykreslena scéna z pohledu světla do textury označované jako shadowmapa - hloubková mapa scény z pohledu světla. Tato hloubková mapa pak obsahuje vzdálenosti osvětlených bodů od zdroje světla. Jakékoli další pixely ležící za těmito (nebo zcela mimo osvětlovanou část scény ) jsou ve stínu. Při druhém průchodu se scéna vykreslí z pohledu kamery a pro všechny body je proveden depth test - každý bod je transformován do souřadnic zdroje světla a jeho vzdálenost od zdroje světla je porovnána se vzdáleností v shadowmapě. Pokud je jeho vzdálenost větší než vzdálenost v shadowmapě, leží pixel ve stínu. I když dochází ke dvěma průchodům, tvorba shadowmapy není nijak výpočetně náročná a nedochází tedy k velkému úbytku rychlosti. Tento přístup má ovšem i mnoho nevýhod, jednou z nich je například obtížné vykreslování všesměrových světel. Vzhledem k tomu, že světelný zdroj vysílá světlo na všechny strany, je potřeba vytvořit 6 shadowmap ( pro každou stranu krychle jednu ) a tím se celý výpočet velmi zesložiťuje. Druhým možným způsobem pro vykreslování všesměrových světel pouze do 2 shadowmap - pro každou polokouli jednu. Jedním z nejpodstatnějších problémů při stínování je příliš nízké nebo naopak vysoké vzorkování shadowmapy a problikánání zastíněných oblastí. K prvnímu problému ( undersampling ) dochází, pokud je zastíněná oblast téměř rovnoběžná se směrem, ze kterého přichází světlo. Výsledkem pak je, že oblast na kterou v shadowmapě připadá velmi malé množství pixelů, je zobrazena v souřadnicích kamery na mnohem větší plochu a tím klesá kvalita stínu. Pokud je naopak rozlišení shadowmapy příliš vysoké, dochází k zrnění podobnému jako na obrázku 3.5, tento problém není ovšem příliš častý kvůli omezeným rozměrům shadowmapy ( už shadowmapa velikosti 2048 x 2048 způsobuje velmi znatelné zpomalení, vyšší rozlišení je alespoň za současných podmínek prakticky nepoužitelné ). Poslední problém - přeskakování stínů - je způsoben kombinací undersamplingu a toho, že v různých okamžicích je různý poměr velikosti oblasti v shadowmapě a při vykreslení v souřadnicích kamery.
27
Obrázek 3.10 [9] - odstranění nevykreslovaných částí ze shadowmapy V označuje kužel kamery, L osvětlenou část, S scénu a B výslednou oblast, pro kterou bude vytvořena shadowmapa. Obrázek vpravo je pro směrová světla, vlevo probodová světla
Kvalitu stínů lze zlepšit tím, že zajistíme, aby se do shadowmapy zbytečně nevykreslovaly části scény, které jsou mimo pohled kamery a nebudou tedy vidět. Dalším možným zlepšením by bylo zjistit, které části scény jsou nejen v kuželu kamery, ale navíc opravdu viditelné a shadowmapu tak vytvářet pouze pro ně.
Obrázek 3.11 [9] - nedostatečné vzorkování shadowmapy
Obrázek 3.12 [9] - stejná oblast blíže ke kameře zabírá v shadowmapě stejnou plochu, kvůli perspektivní projekci kamery se ale zobrazí větší a dojde tak k aliasingu - nekvalitní stíny
Řešením problému na obrázku 3.10 by bylo vytváření shadowmapy, u které by se v takovýchto místech zvyšovalo vzorkování. Řešením problému na obrázku 3.11 je například použití kaskádových shadowmap - větší množství menších shadowmap rozvrhnutých tak, že na každá zachycuje určitý úsek od kamery. Při vhodném nastavení délek jednotlivých úseků se pak oblasti blíže ke kameře uloží do shadowmapy ve vyšším rozlišení než oblasti vzdálenější. Další velkou výhodou tohoto řešení je, že zatímco použití jedné shadowmapy o velikosti 4096 x 4096 by způsobilo obrovský výkonnostní propad, čtyři menší shadowmapy o velikosti 1024 x 1024 jsou grafickou kartou zpracovány mnohem snadněji a výsledek stínování je navíc kvalitnější. Obvykle se používá 2 až 8 shadowmap, větší množství již nepřináší výkonnostní zlepšení. 28
Deferred shadow mapping, tedy odložené stínování, je technika stínování podobná způsobu popsanému na začátku této podkapitoly ovšem s tím rozdílem, že jednotlivé shadowmapy od různých zdrojů jsou vykreslovány do rendertargetů a výsledek je nakonec zkombinován. Tuto techniku jsem nakonec využil v kombinaci s kaskádovými shadowmapami. Další technika, která ve hrách bývala často hlavně v minulosti používána, je statické stínování vypočítané před spuštěním hry. Tuto techniku jsem také vyzkoušel, ale i přes poměrně dobré výsledky jsem u ní nezůstal. Tato technika spočívá v tom, že se u nepohyblivých objektů vypočte stínování předem a po dobu hry se dále nemění. Velkou výhodou je, že není potřeba žádný další průchod pro vytvoření stínů. Tato technika má ovšem i podstatné nevýhody. Jak z jejího popisu vyplývá, dají se pomocí ní stínovat pouze statické objekty, na pohyblivé je potřeba opět použít jiný stínovací algoritmus, který přidá jeden průchod navíc ( ale vzhledem k tomu že již není potřeba stínovat terén, může se shadowmapa detailněji zaměřit na pozici hráčova vozidla a stíny vytvářet pouze v jeho okolí už s vyšší kvalitou), stínování pohyblivých objektů zcela vynechat a nebo ho velmi zjednodušit. Příkladem takto zjednodušeného stínování mohou být například starší hry Carmageddon II a Driver, u kterých je pod vozidlem obdélníkový stín, který se při otáčení vozidla nijak nemění. Další nevýhodou předvypočteného statického stínování je složitý výpočet stínování na pohyblivých objektech.
Obrázek 3.13 - ukázka dříve častého zjednodušení stínování - Carmageddon II. Vozidla mají obdélníkový stín pod hlavní komponentou modelu, terén má pod sebou jednoduché statické stíny.
K zlepšení kvality stínů jsem dále použil techniku PCF 3x3, tedy rozmazání stínů pomocí průměrování daného pixelu s osmi okolními pixely. Kromě změkčení stínů se tím také zmírnilo přeskakování stínů. Na několika následujících obrázcích jsou vidět rozdíly v kvalitě stínů při používání různých technik.
29
Obrázek 3.14 - použití shadowmapy o rozměrech 2048x2048 bez jakýchkoli optimalizyčních technik
Obrázek 3.15 - stejná technika jako 3.13, ale do shadowmapy vykreslována pouze scéna v kuželu kamery. Kvalita stínů se sice zlepšila, dochází ovšem k jejich problikávání
Obrázek 3.16 - zcela jiný přístup: použití dopředu vypočtené shadowmapy s jednoduchou interpolací zajistilo měkké stíny velmi vhodné pro stínování krajiny. Ačkoli je stínování statické, je možné ho v průběhu hry postupně přepočítat a měnit tak po delších časových intervalech stínování scény - to je výhodné pro vytváření stínů vrhaných pomalu se pohybujícím směrovým zdrojem světla ( slunce ). Tato technika přitom téměř nezvýší výpočetní složitost. Vzhledem k obtížnému stínování pohybujících se objektů jsem se pro ni ovšem nakonec nerozhodl. Shadowmapa má velikost pouze 512 x 512 pixelů. 30
Obrázek 3.17 - konečný výsledek, stínování je provedeno pomocí deferred shadow mappingu kombinovaného se 4 kaskádovými shadowmapami velikosti 1024 x 1024. Jednotlivé barvy znázorňují úrovně kaskády. Stíny jsou dále změkčeny pomocí PCF 3x3 a shadowmapy jsou tvořeny pouze pro část scény v kuželu kamery. Výsledkem jsou poměrně kvalitní stíny, jen zřídka se vyskytuje drobné problikávání. Nevýhodou je vyšší počet průchodů ( vytvoření hloubkové mapy z pohledu kamery, vytvoření shadowmapy a nakonec vykreslení celé scény )
31
3.6 Decals Tato podkapitola se bude zabývat vykreslováním otisků ( decals ) na jiné grafické objekty. Tyto otisky jsou dobře známé především z žánru FPS jako zásahy po kulkách na zdech. Jedná se vpodstatě o textury, které promítají na již vykreslené objekty. Při tvorbě decals je třeba vypořádat se s problémy jako je omezený počet naráz zobrazených otisků a jednotlivých textur použitých pro ně a dále pak nutnost deformovat otisky tak aby kopírovaly původní povrch. Otisky jsem použil k vykreslování zásahů na terénu, implementaci jsem provedl tak, že se do pole ukládají pozice posledních šesti zásahů terénu a toto pole je pak předáno efektu s technikou použitou pro vykreslení terénu. Následně se při vykreslování terénu v pixelshaderu kontroluje rozdíl pozice vykreslovaného bodu ve světových souřadnicích a pozic uložených v poli. Pokud je tento rozdíl menší než definovaný poloměr zásahu, je na toto místo promítnuta textura zásahu s odpovídajícími souřadnicemi. Vzhledem k použití víceprůhcodového stínování jsem se snažil vyhnout použití dalšího průchodu pro vytváření otisků. Z tohoto důvodu je vykreslováno pouze posledních 6 zásahů a je pro ně použita pouze jedna textura. Tato omezení mi umožnila vykreslovat otisky na terén okamžitě bez nutnosti dalšího průchodu ( u velkého množství otisků by vznikl při porovnávání pozic problém s maximálním počtem aritmetických instrukcí pixelshaderu pro jeden průchod). Jednotlivé otisky se v závislosti na jejich stáří zobrazují s různou intenzitou.
Obrázek 3.18 - krátery po střelách na terénu vytvořené pomocí otisků
Vzhledem k tomu, že takto vykreslené otisky vypadají i přes použití textury ploché. Tento nedostatek by se dal odstranit pomocí bump-mappingu, techniky používané pro zplastičtění textur na 3D modelech pomocí úpravy normál pomocí textury - mapy normál. Bump-mapping se také používá ke zvýšení detailnosti modelů, výsledný model pak vypadá detailněji. Obrázky 3.19 - 3.21 pocházejí ze zdroje [11] . Bump-mapping jsem nicméně neimplementoval.
32
Obrázek 3.19 - původní textura
Obrázek 3.20 - normálová mapa Obrázek 3.21 - výsledek po použití bump-mappingu
3.7 Částicové efekty Částicové efekty se používají především na vykreslování kouře, ohně, výbuchů a podobných efektů. Ačkoli je jejich tvorba jednoduchá, lze pomocí nich vykreslovat výše zmíněné efekty velmi věrně. Základním principem při tvorbě částicových efektů je vykreslování velkého množství 2D objektů (částic tvořených obvykle zčásti průhlednou texturou), které stále směřují čelem ke kameře. Každá částice má po svém vytvoření definovanou rychlosta dobu života - po jejím uplynutí částice zmizí.Často se také do chování částice zahrnuje náhodně generovaná hodnota, díky tomu je pak výsledný efekt přirozenější ( všechny částice se nepohybují stejně ). Částicové efekty jsem použil na vykreslování kouře při poškození motoru, střelbě, explozích, kouře výfuku a pro zobrazení ohně a prachu za tankem při jízdě.
Obrázek 3.22 - kouř z výfuku a prach za tankem pomocí částicových efektů. Prach se mění podle terénu jakým tank projíždí.
Obrázek 3.23 - výstřel z kanonu
33
Obrázek 3.24 - požár motoru, tank zničen
Obrázek 3.25 - zásah terénu
3.8 Post-processing Za některých situací je potřeba upravit výsledný obraz ještě nějakými dodatečnými efekty, jako je například rozostření, přidání kontrastu, jasu, inverze barev nebo jakékoli jiné operace které byste s texturou zachycující scénu provedli ve 2D editoru obrázků. Právě k těmto úpravám se dá použít post-processing - poté co je scéna vykreslena, aplikují se na ni tyto dodatečné úpravy. Vzhledem k tomu, že je potřeba vykreslit pouze 2 trojúhelníky ( tak je pokryta celá obrazovka ), postprocessing příliš nezvyšuje výpočetní složitost. Samozřejmě ale záleží také na složitosti výpočtů na pixelshaderu při provádění úprav a celkovém počtu post-process efektů. Na vertexshaderu se obvykle neprovádí žádné nebo jen velmi jednoduché výpočty. V tankovém simulátoru jsem post-processing použil pro horizontální rozmazání obrazu při prudkém nárazu tanku do překážky nebo při pádu z výšky. Zda a jak moc má být obraz rozmazaný se zjišťuje pomocí změny rychlosti fyzikálního modelu. Pokud je změna příliš velká, dojde k rozmazání. Inspiroval jsem se přitom poměrně starou automobilovou hrou Interstate 76, ve které se při prudkém nárazu vozidla rozmazal obraz. Tato hra mimochodem vynikala kvalitním fyzikálním modelem automobilů a velmi propracovaným modelem poškození, který zahrnoval jak venkovní části vozidla, tak i poškození podvozku, zbraní ( na vozidlo se připevňovaly nejrůznější zbraně), motoru a vpodstatě všeho co automobil má.
34
Obrázek 3.26 - ukázka post processingu: Dočasné horizontální rozmazání obrazu při prudkém nárazu
3.9 Optimalizace pro zvýšení výkonu V předcházejících podkapitolách jsem se soustředil především na zlepšení grafické stránky hry, tato bude věnována neméně důležitému problému - celkové výpočetní složitosti vykreslované scény. Vzhledem k poměrně rozsáhlému terénu a přitom pouze malé zobrazené části bylo velmi neefektivní vykreslovat terén celý bez ohledu na to, jestli je daná část vidět nebo ne. Z tohoto důvodu jsem se rozhodl pro implementaci terénu pomocí struktury QuadTree, kdy je terén rozdělen na větší množství uzlů, přičemž každý uzel který není koncový má 4 potomky. Každý uzel má také svůj vlastní bounding box. Pokud tento bounding box nekoliduje s kuželem kamery ( nic uvnitř tohoto bounding boxu nebude vykresleno ) nevykreslí se ani daný uzel ani žádný z jeho potomků. Pokud ke kolizi dojde, testují se všichni potomci na kolizi opět výše zmíněným způsobem. Implementace QuadTree výrazně snížila výpočetní složitost. Ačkoli se pomocí QuadTree hra výrazně zrychlila, docházelo ke zpomalení hry pokud kamera zabírala velké množství uzlů, typicky při pohledu téměř vodorovně. Přestože byly tyto uzly velmi daleko a drobné detaily tak nebyly potřeba, terén se v těchto místech zobrazoval se stejnou složitostí jako blízko u kamery. Řešením tohoto problému bylo zavedení úrovní detailu v závislosti na vzdálenosti uzlu od kamery. Tento problém jsem vyřešil zavedením celkem tří úrovní detailu pro terén. Jednotlivé úrovně jsem vytvořil tak, že jsem při vykreslování vypouštěl část vykreslovaných vrcholů. 35
Nejblíže u kamery se tedy vše zobrazovalo ve vysoké kvalitě, u druhé úrovně se terén vytvářel pouze z každého čtvrtého vrcholu, u nejnižší úrovně z každého osmého vrcholu. Toto řešení opět pomohlo zvýšit rychlost hry. Jak jsem zjistil, druhá úroveň je použitelná i pro část blízko u kamery, nejnižší úroveň už ale vytváří velké nepřesnosti ve vzhledu terénu a je použitelná pouze pro větší vzdálenosti. Před spuštěním hry je možné zvolit jednu z pěti úrovní detailů, které se liší rozložením vzdáleností pro jednotlivé úrovně, případně úplným vypuštěním některých úrovní ( celý terén v detailech nejvyšší, druhé, nebo třetí úrovně ). Při použití úrovní detailu se ovšem objevil nový problém - drobné mezery na hranicích jednotlivých úrovní způsobené tím, že kvůli vypouštění určitého počtu vrcholů na sebe jednotlivé části terénu přesně nenavazují. Vzhledem k tomu, že jsem tuto techniku implementoval až na konci vývoje hry, tímto problémem jsem se nezabýval, protože jeho řešení by znamenalo velké zásahy do konstrukce terénu.
Obrázek 3.27 - použití úrovní detailů na terénu, na obrázku jsou patrné nepřesnosti na rozhraní jednotlivých úrovní
Obrázek 3.28 - druhá úroveň detailů pro celý terén, nepřesnosti jsou téměř neznatelné i přes vypuštění 3 / 4 vrcholů. 36
Obrázek 3.29 - nejnižší úroveň detailů použita pro celý terén, nepřesnosti jsou vlivem vypouštění vrchoů znatelné ( zde například prochází terén částí tanku ). Tyto nepřesnosti ovšem nejsou tak rozsáhlé, aby znemožňovaly hraní.
Další optimalizace se týká vykreslování samotných modelů. Ačkoli samotný model tanku nevypadá příliš detailně, po převodu do formátu .fbx měl přes 60 000 vrcholů. Vykreslování všech tří tanků pak představovalo pro počítač velkou zátěž a vzhledem k nedostatečným znalostem programů pro editaci 3D modelů jsem nemohl model zjednodušit přímo. Tento problém jsem proto vyřešil opět pomocí úrovní detailů. Zde mi velmi pomohlo, že jakýkoli ModelMesh ( část modelu ) má v XNA definovánu kouli, která ho ohraničuje - BoundingSphere. Stačilo tedy jen méně významné ( menší ) ModelMeshe vypouštět v zavislosti na vzdálenosti tanku od kamery a poloměru jejich BoundingSphere. Takto jsem opět vytvořil tři úrovně detailu tanku lišící se vzdáleností na kterou jsou použity a maximálním poloměrem vypouštěných ModelMeshů. Vzhledem k tomu, že každý pás je tvořen jedním meshem, jsou pásy vypuštěny po pevně stanovené vzdálenosti. Dále jsem každému tanku přiřadil BoundingBox. Pokud se protíná s kuželem kamery, je tank vykreslen. Tím je opět zajištěno, že se nevykreslí žádný tank mimo kužel kamery. Tak jako u stínování, dalším krokem by bylo vykreslování pouze těch tanků, které jsou doopravdy vidět. Při větším počtu jednotek by opět bylo výhodné použít QuadTree nebo OctTree.
Obrázek 3.30 - při velké vzdálenosti tanku od kamery se vykreslují pouze nejdůležitější části, tím se opět dosáhlo urychlení hry 37
Ve snaze umožnit spuštění hry i na méně výkonných počítačích jsem do úvodních nastavení také přidal volbu rozlišení: 512x384, 640x480,800x600 (toto rozlišení je výchozí a hra původně používala právě jen toto) a 1024x768. Je zde také možnost nastavit velikost shadowmapy ( pro každou kaskádu ) od 256x256 přes 512x512 po 1024x1024. Při nižších velikostech shadowmapy ovšem kvalita stínů rychle klesá a přitom nedochází k nějak výraznému urychlení.
38
4 Umělá inteligence 4.1 Algoritmy používané v počítačových hrách Použití nějaké formy umělé inteligence je společné prakticky pro všechny počítačové hry, její řešení se ale v jednotlivých herních žánrech podstatně liší, požadavky na umělou inteligenci v tahové strategii budou například úplně jiné než u FPS nebo závodního simulátoru. Pro realizaci AI se často používají konečné automaty (FSM - finite state machines ). Principem tohoto řešení je, že jednotlivé stavy automatu reprezentují akce prováděné jednotkou, přechody mezi stavy pak určují podmínky, za kterých k těmto činnostem dochází, na začátku hry se počítačem řízená jednotka nachází v počátečním stavu. Umělá inteligence řízená pomocí konečného automatu není výpočetně náročná, je snadno implementovatelná a díky jasně definovaným přechodům mezi stavy lze snadno odstraňovat případné chyby - lze předpokládat jak se má jednotka za daných podmínek chovat. Toto pevně dané chování je ale také nevýhodou takovéto AI. Řešením je použití fuzzy logiky, kdy jsou přechody mezi stavy prováděny s určitou pravděpodobností. Pokud například jednotce dochází munice, zvyšuje se pravděpodobnost s jakou si bude hledat munici na doplnění. Zcela jiný druh AI bývá použit v závodních hrách, kde se vyžaduje aby soupeř nenarážel do stěn a pohyboval se plynule. Místo skutečné AI je pak použit systém, kdy se protivník pohybuje po křivce definované pro danou trať. S umělou inteligencí souvisí také použití vhodného algoritmu pro vyhledávání cest. V počítačových hrách se tento problém řeší nejčastěji buď pomocí Dijkstrova algoritmu nebo algoritmu A* (A star ). Oba způsoby jsou založené na podobném principu - z herní scény je vytvořen graf s ohodnocenými hranami a uzly, uzly reprezentují jednotlivé pozice ve scéně na ketré smí jednotka vstoupit, hrany pak cesty mezi nimi. Výstupem obou algoritmů je nejkratší cesta ze startovního bodu do cílového. A* se používá, pokud známe bod do kterého se chceme dostat. Jeho použití je vhodné pro přesun jednotky na danou pozici. A* se snaží prohledávat graf jen ve směru k cílovému bodu a díky tomu je rychlejší. Algoritmus Dijkstra oproti tomu prohledává rovnoměrně do všech směrů a používá se, pokud neznáme pozici do které je třeba se dostat. Tento algoritmus se uplatňuje například u jednotek, které musí vyhledat nejbližší místo s nějakými vlastnostmi - například těžební jednotky ve strategiích při vyhledávání zdrojů.
39
Obrázek 4.1 [15] - vyhledání nejkratší cesty pomocí algoritmu A*. Zelený bod oznařuje startovní pozici, červený cílovou a modrý překážku. Modré a žluté uzly jsou vestavu closed, zelené open, nejkratší cesta je vytečkována. Ukazatele v uzlech ukazují na rodiče daného uzlu. Ohodnocení uzlů: číslo v pravém rohu je vzdálenost od cílového uzlu ( H ), v levém dolním rohu je vzdálenost od počátečního uzlu ( G ), v levém hroním rohu je jejich součet ( F ).
Na následujících řádcích je popsán princip algoritmu A*. Open list je seznam otevřených uzlů, closed list seznam uzavřených uzlů. 1. přidání startovního uzlu do open listu 2. opakování následujících kroků: 2.1. pro všech 8 okolních uzlů: 2.1.1. pokud se na uzel nedá vstoupit nebo je v closed listu, tak je ignorován 2.1.2. pokud není v open listu, je tam přidán a jako rodič je mu nastaven uzel, jehož okolí prohledáváme. Pak jsou spočteny vzdálenosti F, G, H. 2.1.3. pokud je v open listu, zjistíme jestli je nově nalezená cesta k němu kratší než původní cesta ( porovnáme hodnoty G, tedy vzdálenost od počátečního uzlu ). Pokud je nová cesta kratší, nastavíme rodiče uzlu na uzel, který právě prohledáváme a přepočítáme hodnoty F a G. 2.2. algoritmus ukončíme, pokud byl cílový uzel přidán do closed listu nebo je open list prázdný a nenašli jsme cílový uzel ( cesta neexistuje ). 3. uložíme nalezenou cestu
40
4.2 Popis implementace AI pomocí stavového automatu Umělou inteligenci protivníků jsem se rozhodl implementovat pomocí konečného automatu a s použitím algoritmu A* pro hledání cest. Terén je pokryt uzly, které jsou ohodnoceny v závislosti na obtížnosti terénu v jejich okolí. Tento výpočet zahrnuje druh povrchu terénu a výškové rozdíly terénu v okolí uzlu. Na takto ohodnoceném grafu provádí počítačem řízené jednotky hledání cest pomocí algoritmu A*. Uzly s ohodnocením přesahujícím určitou hranici jsou brány jako nedostupné a do výsledné cesty nejsou nikdy zahrnuty. Konečný automat obsahuje několik stavů představujících různé chování tanku. Jsou zde celkem 4 stavy: náhodné prohledávání mapy, sledování waypointů, útok a ústup. Na počátku hry protivní začíná podle náhodné hodnoty buď ve stavu náhodného prohledávání mapy nebo ve stavu sledování waypointů. Tyto stavy může protivník střídat po určitém počtu kroků. Pokud protivník objeví hráče ( buď hřáče uvidí, nebo se k němu hráč přiblíží na malou vzdálenost ) a přitom má munici a není silně poškozen, přejde do stavu útoku. V tomto stavu hráče do určité vzdálenosti pronásleduje a snaží se ho zasáhnout kanonem. Do stavu útoku může protivník s určitou pravděpodobností také přejít, pokud je zasažen hráčem. Pokud je naopak protivník silně poškozen nebo mu dojde munice, přejde do stavu pro ústup. V tomto stavu protivník na hráče neútočí a snaží se dostat do bezpečí daleko od hráčova vozidla.
Obrázek 4.2 - stavový automat protivníka, jednotlivé stavy: A - vytvoření jednotky B - prohledávání mapy C - pohyb po waypointech D - útok E - ústup 41
4.3 Dodatečné úpravy pro správnou funkci AI V této podkapitole se zaměřím na problémy spojené s hledáním cest a pohybem počítačem řízené jednotky po mapě, konkrétně na vyhýbání se pohyblivým objektům a uváznutí v obtížném terénu. Vzhledem k tomu, že při hledání cesty není možné zahrnout do výpočtu pohyblivé objekty a tak se jim vyhnout, je nutné průběžně kontrolovat oblast před jednotkou a při zjištění překážky cestu přepočítat s tím že uzel obsazený pohyblivým objektem a uzly v jeho nejbližším okolí jsou označeny jako nedostupné. Přítomnost objektu blízko před jednotkou lze díky použití BEPUphysics snadno kontrolovat vysíláním dvou krátkých paprsků před jednotku ( každý paprsek na jedné straně ). Dalším problémem byly malé obtížně přístupné části terénu, které se sice díky své malé velikosti neprojevily při ohodnocení uzlů, ale přitom na nich snadno docházelo k uváznutí nebo převrhnutí počítačem ovládané jednotky. Převrhnutí jsem zamezil tím, že si jednotka kontroluje v určité vzdálenosti před sebou výšku terénu nalevo a napravo od své - v místech po kterých by se pohybovala kola. Pokud výškový rozdíl obou míst překročí určitou hodnotu ( to znamená že je zde povrch šikmý a hrozí převrhnutí ), jednotka zastaví a provede přepočítání cesty tak aby do těchto míst nenajela. Spolu s touto kontrolou jsem také zavedl porovnávání současné výšky terénu pod jednotkou a výšky terénu před ní. Tím jsem zamezil jejímu zapadávání do příkopů a pádům z výšky.
Obrázek 4.3 - výsku terénu je třeba porovnávat jak těsně u jednotky tak i ve větší vzdálenosti pokud je vzdálená výška téměř stejná jako současná, pokračuje se v pohybu dál. Přístup znázorněný v horní části obrázku by způsobil, že by jednotka byla příliš opatrná.
42
5 Vliv povrchu terénu a další drobné detaily V této kapitole zmíním několik méně důležitých technik týkajících se jak fyzikálního modelu tak i grafické stránky hry, které nešlo zařadit do předchozích kapitol. 5.1 Vliv povrchu terénu na pohyb tanku Protože jsou pro terén použity různé textury znázorňující jednotlivé povrchy, zahrnul jsem do simulátoru také změny chování vozidla ( tanku ) na tyto povrchy. Využil jsem přitom toho, že fyzikální knihovna BEPUphysics umožňuje u třídy Vehicle měnit parametry jednotlivých kol během hry. Druh povrchu je detekován pod každým kolem zvlášť podle dat v texturách, ze kterých se složení povrchu načítá. V závislosti na zjištěných poměrech povrchů na daném místě je pak u brzd kol nastavena vlastnost RollingFrictionCoefficient. Výsledkem toho je, že se tank po různých terénech pohybuje různě rychle. Parametry se samozřejmě nastavují pouze u kol která jsou právě ve styku s terénem. Povrchy terénu jsou zohledněny i při vyhledávání cest u protivníků. 5.2 Převodovka Od počátku jsem se setkával s problémem uváznutí tanku například při otáčení na nerovném terénu. K každého motoru kola se nastavují vlastnosti MaximumForwardForce a MaximumBackwardForce, které určují, jakou silou dokáže motor kolem otáčet. Problémem bylo, že buď docházelo k uváznutí (nízká hodnota) nebo se naopak tank dokázal pochybovat příliš rychle (vysoká hodnota). Tento problém jsem vyřešil tak, že se v závislosti na rychlosti tanku přepíná mezi 4 hodnotami - vyšší hodnoty pro nízkou rychlost, nízké pro vyšší rychlost. Tím se dosáhlo toho, že tank neuvázne a zároveň se pohybuje tak jak by měl. 5.3 Pružinová kamera V počítačových hrách se často místo kamery pevně připojené za vozidlem v dané vzdálenosti používá pružinová kamera (spring camera). Ta se od kamery s pevným offsetem od vozidla liší tím, že se hcová jako by byla k vozidlu připojena pružinou - při zrychlování zůstává dále od vozidla, při zpomalování se naopak přiblíží (chová se jako kdyby měla setrvačnost). 5.4 Poškození tanku Ačkoli není model tanku deformovatelný, je zde poškození znázorněno odlétáváním střepin s kouřovou stopou při zásahu tanku a odpadáváním schránek na materiál. Při velkém poškození motoru se tank zastaví, při velkém poškození věže se věž zablokuje. 43
44
6 Zhodnocení vlastní práce Hlavním úkolem práce byla implementace jízdy v pásovém vozidle v prostředí XNA Game Studio / .NET C# s možností boje proti protivníkovi s jednoduchou umělou inteligencí. Další body zadání se týkaly modelu poškození, vlivu povrchů na pohyb vozidla, vykreslování terénu pomocí výškové mapy, použití multitexturingu a vykreslování stínů a decals. Postupně přibylo ještě použití pružinové kamery, znázornění poškození a použití částicových efektů. Myslím, že se všechny zadané úkoly nakonec podařilo splnit. Tato práce pro mě byla přínosná především tím, že jsem se seznámil s prostředím XNA a programovacím jazykem C# a s použitím fyzikálních knihoven a propojením fyzikálního a grafického modelu hry. Hlavním přínosem ovšem bylo seznámení se s programováním vertex a pixel shaderu na grafických kartách a s technikami používanými pro vykreslování stínů. Před tímto projektem jsem vytvořil pouze jednoduché aplikace v OpenGL a s většinou postupů jsem se tak v této práci setkal poprvé. Nejobtížnější částí úlohy bylo zřejmě vykreslování stínů a doladění umělé inteligence protivníka. Ačkoli základ AI jsem vytvořil poměrně snadno, doladit její chování v členitém terénu a předcházení kolizí s ostatními jednotkami nebylo jednoduché. Nyní uvedu několik vylepšení, na která ovšem už buď nezbyl čas, nebo by jejich implementace byla tak náročná, že by vyžadovala velké zásahy do programu. Asi nejdůležitějším by bylo snížení hardwarových nároků hry - ta je nyní náročná především na výkon grafické karty, navíc kvůli stínovacím technikám vyžaduje pixel shader verze 3.0 (ten je nicméně podporován většinou grafických karet od roku 2005 dál). Ke snížení HW nároků by bylo třeba zavést zdokonalené použití úrovní detailu terénu a také použití view cullingu, ne pouze frustum cullingu. Užitečné by bylo také snížení počtu vrcholů modelu tanku, současná hodnota přesahuje 60000 vrcholů a to se při zobrazení více jednotek najednou na HW nárocích negativně odráží. Naopak zlepšit grafickou stránku hry by bylo možné pomocí vyhlazování hran, bump-mappingu u decals a anisotropického filtrování. Mezi další možná vylepšení by patřila síťová hry více hráčů proti sobě a deformace tanku i terénu po zásahu. Hře by také jistě prospělo propracovanější vyhledávání cest. U takto nízkého počtu jednotek sice hru znatelně nezpomaluje, jiná situace by pravděpodobně ale nastala při větším počtu jednotek. Vyhledávání cest by tak bylo vhodné neprovádět najednou pro všechny jednotky ale postupně například každý frame pro jednu jednotku.
45
46
7 Testování Hru jsem vyvíjel na počítači s procesorem AMD Athlon 64 X2 6000+, grafickou kartou GeForce 8600 GTS (256MB) , RAM 2GB a s operačním systémem Windows XP SP 2. Téměř až do konce vývoje jsem se na různých počítačích setkával s tím, že se hra ihned po nastavení a spuštění zablokovala. Jak jsem později zjistil, tento problém byl způsoben špatným nastavením rendertargetů (cíle kam se vykreslují jednotlivé průchody) při výpočtu stínování. Po odstranění této chyby už hra fungovala správně i na jiných PC. Níže jsou uvedeny výsledky testování na různých konfiguracích. konfigurace
výsledek
AMD Athlon 64 X2 6000+, GeForce Hra zcela plynulá i při nejvyšších 8600GTS, 2GB RAM, Win XP SP2 - PC detailech, rozlišení 1024x768 a každé na kterém jsem hru vyvíjel shadowmapě o velikosti 1024x1024 Intel Q6600 2.4GHz, GeForce 8600, Win XP Intel Core i3 M 330 2.13GHz, ATi mobility HD 5145, Windows 7
Hra byla plynulá při rozlišení 800x600 a vyšších detailech, nicméně se nezobrazoval terén.
47
48
8 Použitá literatura a další zdroje [1]
Kryštof Valenta, Závodní simulátor - bakalářská práce, vedoucí práce Ing. Michal Hapala, 2010
[2]
BEPUphysics, http://bepu.squarespace.com/
[3]
BEPUphysics, BEPUphysicsJointsAndConstraintsDocumentation.pdf, 2010
[4]
BEPUPhysics, BEPUphysicsCollisionRulesDocumentation.pdf, 2010
[5]
Ivo Pejčoch, Obrněná technika 1 Německo 1919 - 1945 1.díl, 2000
[6]
Aaron Torpy, L3DT - Large 3D Terrain 2.8, http://www.bundysoft.com/L3DT/
[7]
Riemer Grootjans, XNA tutorials: 3D series 1 - Terrain, www.riemers.net
[8]
Riemer Grootjans, XNA tutorials: 3D series 4 - Advanced terrain, www.riemers.net
[9]
Daniel Scherzer, Michael Wimmer, Werner Purgathofery, A survey of real-time hard shadow mapping methods, Vienna University of Technology, 2010
[10] Riemer Grootjans, XNA tutorials: 3D series 3 - HLSL, www.riemers.net [11] Sřren Dreijer, Bump Mapping Using CG, www.blacksmithstudios.dk/projects/downloads/bumpmapping_using_cg.php [12] The Danger Zone, Deferred shadow maps, http://mynameismjp.wordpress.com/samples-tutorials-tools/deferred-shadowmaps-sample/ [13] The Danger Zone, Deferred cascaded shadow maps, http://mynameismjp.wordpress.com/2009/02/17/deferred-cascaded-shadowmaps/ [14] Riemer Grootjans, XNA 3.0 Game programming recipes, 2009 [15] Patrick Lester, A* Pathfinding for beginners, http://www.policyalmanac.org/games/aStarTutorial.htm [16] František Hruška, Metody umělé inteligence - bakalářská práce, vedoucí práce Ing. Petr Felkel PhD, 2009 [17] Catalin´s XNA blog, Custom content processor and normal mapping, http://www.catalinzima.com/tutorials/deferred-rendering-in-xna/customcontent-processor-and-normal-mapping/ [18] Microsoft, APP Hub, http://create.msdn.com/en-US/education/catalog/
49
50
9 Uživatelská příručka 9.1 Spuštění Hru spstíte buď přímo spuštěním souboru noinstall/achtungpanzer.exe nebo nainstalujete pomocí install/setup.exe, případně si můžete hru sami přeložit, soubor projektu pro visual studio C# je ve složce src/achtungpanzer.sln. Ve visual studiu nastavte konfiguraci Release x86 a stiskněte F6. Po spuštění hry nastavte podle výkonu PC úroveň detailů a dejte OK. Objeví se základní menu hry s tlačítkem "nová hra", na toto tlačítko klikněte. 9.2 Ovládání Akce
Klávesa
přepnutí kamery z volného pohledu na kameru za tankem
C
přepínání kamery za tankem / zaměřovač C jízda dopředu
W
jízda dozadu
S
otočení vlevo
A
otočení vpravo
D
věž vlevo
šipka vlevo
věž vpravo
šipka vpravo
kanon nahoru
šipka nahoru
kanon dolu
šipka dolu
střelba z kanonu
LEFT CTRL
střelba z kulometu
LEFT SHIFT
návrat do opravny
Insert
volná kamera
V
volná kamera vpřed
numpad 8
volná kamera vzad
numpad 2
volná kamera vlevo
numpad 4
volná kamera vpravo
numpad 6
volná kamera - rozhlížení
pohyb myši
51
9.3 Cíle hry Cílem hry je zlikvidovat co nejvíce nepřátelských tanků. hru začínáte v opravně s nastavenou volnou kamerou. Do normální kamery se přepnete stiskem C. Do opravny se můžete během hry kdykoli vrátit, tank bude okamžitě opraven a bude doplněno střelivo. Do opravny se lze přenést také stiskem klávesy Insert, tím se ovšem vynuluje počet zničených tanků. Počet zničených tanků se vynuluje také vždy když nepřítel zničí Váš tank. Pokud Vás nepřítel zničí, objevíte se po uplynutí časového limitu nebo po stisku klávesy Insert v opravně. Pokud zničíte nepřátelský tank, tento tank se objeví po uplynutí čásového limitu opět na své startovní pozici.
52