PROCEDUELL TERRÄNG Proceduella metoder för bilder (TNM084) Jimmy Liikala (jimli570@student.liu.se) Institutionen för teknik och naturvetenskap Sammanfattning Rapporten beskriver hur en proceduell terräng har genererats och texturerats proceduellt (inklusive skybox) med noise funktioner. Implementationen har skett i windows-miljö med C++ och GLSL, det tas inte specifikt upp hur det implementerades i just dem språken.
1.0 Inledning Terräng är en viktig del inom datorgrafiken, framförallt när det gäller spel. Man kan antingen moddelera terrängen i förhand i något programvara t.ex 3D studio MAX eller så kan man generera terrängen proceduellt. Att generera terrängen proceduellt ger fördelen att man kan generera stora områden lika bra som små områden, utan någon större ansträngning. Man kan också enkelt göra förändringar i miljön genom att ändra på några få parametrar. Detta projekt är utfört som en del av examinationen i kursen Procedurella bilder (TNM084) på Linköpings universitet, år 2011. Terrängen genereras på CPUn (C++) och färgläggning/texturering och ljussättning sker på GPUn (Graphic Processing Unit) med GLSL. Syftet med projektet är att använda tillämpa någon form av noise, i detta fall Pearlin noise[1] som används vid både gererering av terräng och textureringen och att lära sig att använda GPUn för beräkningar. En skybox implementeras och animeras också med GLSL shaders. 2.0 Metod Projektet är utfört i windows-miljö med ett nvida grafikkort, programmrat i C++ och GLSL (fragment och vertex shader). Ordningen här spelgar till stor del i vilken ordning delar utfördes. 2.1 Datastrukturen En egen datastruktur specifikt för ändamålet sattes upp, där varje vertex-punkt och triangel sparades i ett eget objekt (figur 1). Varje triangel och vertex sparades i ordning ner till separata vektor strukturer, vilket genom travesering möjligör rendering av all trianglar. Vertex objekten innehåller koordinaterna till punkter och normaler. Triangeln innehåller dess normal och index till rätt vertex objekt i vertex vektorn som bygger upp triangeln. Figure 1. Illustration av Vertex och Triangel objektet. 2.2 Generering av triangel meshen 2.2.1 Generering av vertex-punkter Vertex-punkter och deras position generarades till regelbundet rutnät med två for-loopar (figur 2).
Figur 2. Rutnät innehållandes totalt 400*400 verticer, totalt 160000. 2.2.2 Generering av trianglar En triangel byggs upp av tre vertex-punkter, eftersom rutnätet med vertices i detta fall var regelbundet var det möjligt att relativit enkelt att räkna ut vilka vertex-punkter som skulle kopplas till vilka trianglar. Efter att genereringen av trianglar så var en triangle mesh formad, se figur 3. Figur 3. Rutnät innehållandes 79202 trianglar och 160000 vertex punkter.
2.2.3 Beräkning av triangel-normaler Normaler är något man för det mesta brukar vilja ha, då det brukar användas till ljussättning av scenen. En travesering genom hela vektorn innehållandes alla trianglar utfördes, där det för varje triangel beräknades två vektorer v1 och v1 (figur 4). Figure 4. Illustration av vektorerna v1 och v2. Normalen fås genom att ta genom att beräkna kryssprodukten mellan v1 och v2 (och sen normalisering), väljer man fel vektorer kan man dock få en inverterad normal. 2.2.4 Beräkning av vertex-normaler För att få bättre en bättre ljussättning kan normaler beräknas per vertex. Vertex normal beräknades genom att addera dem till vertex-punkten intillligande trianglars normaler och sedan dividerar det på antal normaler som har adderats. I detta fall har varje vertex sex stycken intilliggande trianglar (figur 5), förutom dem vid meshens gränser som har färre (koll görs för att ta reda på om man är vid en gräns).
Figure 5. En vertex-punkt med sina sex intillliggande trianglar. 2.3 Generering av terräng För att få en naturlig höjdvariationen i terrängen genomfördes en traversering genom alla vertex punkter, vertex punkterna förflyttades på höjden med Stefan Gustavssons förbättrade pearlin noise[2] enligt följande: Displacement = A * pnoise.noise(f1 *x/periodx, f2 *z/periodz); A = Maximala förflyttningen som får göras (amplituden) f1, f2 = frekvensen (hur ofta den ska variera) x, y = vart vi är i rutnätet (vilket ger upphov till annorlunda noise från ställe till ställe) Med amplituden satt till 100 och f1,f2 till 3 fick genererades en terräng enligt figur 6. Figure 6. Terräng genererad med perlin noise, amplituden 100, f1,f2 = 3. Naturen har en förmåga att återupprepa mönster därför applicerades sex oktaver noise, för att få en mer naturlig terräng (figur 7).
Figure 7. Terräng enligt figur 6, men nu med 5 oktaver noise. I en naturlig miljö finns det dock mycket mer plana ytor, därför dämpades noise värden som blivit under noll med 70% vilket gav upphov till den genereade terrängen enligt figur 8. Figure 8. Terräng enligt figur 7, men nu också med dämpad noise. Detta är terrängen som valdes att bygga vidare på, dock är det möjligt att genom parametrar enkelt ändra utformningen av terrängen.
2.4 Ljussättning Med ljus kan vi simmulera djup och skuggor, därav är det också en viktig del av datorgrafiken. Ljusmodellen som används är Point Light per pixel[3] (per fragment), och detta beräknas således på GPUn. Eftersom vertex normalerna är beräknade är det bara att börja beräkna ljuset i GLSL enligt[3]. Ljusmodellen tar både obeservatörer (med half-vektorn) och sträckan till föremålet (punkten) i akt. 2.5 Texturering av terräng För att få en naturliga känsla är det viktiga med färgläggning/texturieng, därför delades terrängen upp i fyra olika områden baserat på höjden. Dem fyra områdena blev snö (toppen av bergen), sten (nedanför snön), gräs (nedanför stenen) och sand längst ner (figur 9). Figure 9. Terrängen med sina fyra olika lager (snö, sten, gräs och sand). För bli av med lite av den monotoma färgerna applicerades noise på texturerna för att få en mindre variation (figur 10). Figure 10. Noise på texturerna. En naturlig miljö har också en viss övergångsfas mellan olika typer av terräng, detta utfördes med smoothstep funktionen (se figur 11).
Figure 11. Terräng med en övergångsfas mellan olika områden. För att övergången inte ska vara lika stor överallt applicerades noise på smoothstep funktionen vilket gav upphov till figur 12. Figure 12. Terräng med noise i övergångsfasas mellan olika områden. 2.6 Skybox En skybox används för att få någon form av omgivande miljö (vad beror på vad man väljer att göra). Det går till så att man stänger av djuptestet och flyttat kameran till origo och sedan renderar man sin sfär i origo. Efter renderingen flyttar man kameran tillbaka till ursprunglig position och fortsätter rendera det man vill rendera. I detta fall applicerades flera oktaver noise med avseende på dem globala koordinaterna (och tid för att få dem att röra på sig) på sfären i GLSL för att få något molnliknade. Smoothstep funktionen användes också, för att mjuka till molnen (figur 13).
Figure 13. Moln i sfären 3.0 Resultat Resultat enligt bilder, terrängen på bilderna har renderats med olika noise (figur 14 16). Figur 14. En del av resultatet.
Figur 15. En del av resultatet. Figur 16. En del av resultatet. 4.0 Diskussion Terräng är ett stort område inom datorgrafik, det finns många delar man skulle kunna förbättra i terrängen jag har skapat. T.ex så är det onödigt att ha lika många trianglar i ett plant område som i ett område med varierande höjd, man skulle vilja ha nån form av decimation algoritm för att fördela trianglarna bättre. Jag försökte också få till nångon form av variation på texturerna beroende på terrängens lutning, men fick inte till det så att det blev snyggt (vertex normalerna anger lutningen), kanske hade jag fått till det om jag hade haft mer tid och/eller jag hade haft någon bättre datastruktur (t.ex [4])för meshen så att jag t.ex kunde räkna ut kurvatur. En tanke från början var att få till mer verklighetstroget gräs, vilket man skulle kunna få till med såkallade billdboards enligt[5], viket jag valde att avstå pga. tidsbrist. Stjärnor på himlen är något som jag hade planerat att göra genom att rendera punkter, men valde istället göra moln då detta är en kurs i proceduella metoder för bilder. GLSL är ett relativit enkelt språk, men det tog ett tag att komma igång och förstå hur det fungerar, t.ex hur man skulle uppdatera variablar som skickas till shadern (och hur man skickar in texturer). Slutsatsen är att jag är nöjd med det jag har lyckats åtstakomma. Om jag skulle ha mer tid skulle jag börja med att göra ett GUI där man kan ändra på parametrarna själv och sedan sätta upp en bättre data-struktur (halfedge mesh) för att kunna applicera en decimation algoritm (och därav kunna generera större terrängen). Efter
det skulle jag ta och implementera billboard tekniken[5] och använda det för gräs. 5.0 Tillägg - Vatten Vatten implementerades i projektet i efterhand, i stora drag enligt metoden som Habib beskriver [6]. Denna metod brukas kallas simple water. Man har endast ett plan som i detta fall byggs upp av fyra vertex-punkter, och sedan renderar man en reflekterad och en refrakterad bild av terrängen till en textur. Texturerna mixas sedan ihop. För att bestämma hur mycket av refraktionen respektive reflektionen som ska mixas ihop kan fresnel termen beräknas, den är dock komplicerad och beräkningstung därav används Schlick s approximation [7] vilket ger ett någorlunda bra visuellt utseende. Om kameran ligger i punkt A (enligt figur 17) så lägger man ett clipping plane i höjd med vattnet som klipper av allt ovanför vatten, för att sedan därifrån ta den refrakterade bilden. Den refrakterade bilden tar man genom att spegla kameran i vatten-planet. Figur 17. Punkt A är kameran därifrån den refrakterade bilden tas och B är var man tar den reflekterade bilden ifrån. Bilden är från Habis guide [6]. Sedan projeceras texturerna på planet och mixas ihop enligt [7]. Slutligen används perlin noise för att disorientera texturkoordinaterna något på vattenplanet, för att få en visuell effekt av att det är vågigt vatten. Slutligen har man vatten enligt figur 18 och 19.
Figur 18. Vatten med distortion på vattenplanets textur-koordinater. Figur 19. Vatten utan någon distortion på vattenplanets textur-koordinater. 6.0 Referenser http://en.wikipedia.org/wiki/perlin_noise [1] http://staffwww.itn.liu.se/~stegu/aqsis/aqsis-newnoise/noise1234.zip [2] http://www.lighthouse3d.com/tutorials/glsl-tutorial/point-light-per-pixel/ [3] http://www.flipcode.com/archives/the_half-edge_data_structure.shtml [4]
http://developer.nvidia.com/content/gpu-gems-2-chapter-1-toward-photorealism-virtual-botany-0 [5] http://habibs.wordpress.com/lake/ [6] http://en.wikipedia.org/wiki/schlick's_approximation [7]