• No results found

Procedurellt genererade träd som påverkas av vind i realtid

N/A
N/A
Protected

Academic year: 2021

Share "Procedurellt genererade träd som påverkas av vind i realtid"

Copied!
41
0
0

Loading.... (view fulltext now)

Full text

(1)

Procedurellt genererade träd som

påverkas av vind i realtid

Oskar Nordquist

Kalmar, 2008-04-08 C-nivå, 10p

Examensarbete Datateknik 10p

Handledare: Martin Blomberg, Högskolan i Kalmar, Institutionen för teknik Examinator: Martin Blomberg, Högskolan i Kalmar, Institutionen för teknik

(2)

Sammanfattning

Denna rapport behandlar en metod f¨or generering av tr¨ad och skapandet av en applika-tion som f˚ar tr¨aden att svaja i vinden i realtid med hj¨alp av multitr˚adade tekniker. Procedurell generering ¨ar ett snabbt v¨axande omr˚ade inom spelindustrin d¨ar lagrings-utrymme har blivit ett bekymmer f¨or att lagra stora m¨angder av unikt inneh˚all. Med hj¨alp av procedurell generering ber¨aknas modeller, texturer, animationer och liknande matematiskt under uppstart eller k¨orning utifr˚an n˚agra f˚a parametrar.

Tr¨adgenereringen bygger p˚a en geometrisk metod snarare ¨an en strikt botanisk. Algo-ritmen har visat sig kunna producera otroligt realistiska tr¨ad av en m¨angd olika arter. Ett tr¨ad representeras enbart av vissa parametrar. Detta kan vara saker som: grenarnas skala i f¨orh˚allande till sin “f¨or¨alder”; riktningen grenarna tenderar att v¨axa ˚at; antal l¨ov per gren, osv. Alla tr¨adarter anv¨ander sig av samma upps¨attning parametrar. Givet ett visst random seed kan tr¨adet ˚aterskapas deterministiskt.

En “tr˚ad” (thread ) inom datateknik ¨ar en f¨orkortning f¨or thread of execution och inneb¨ar att tv˚a eller flera processer exekverar samtidigt (simulerad samtidighet eller fysisk). Multitr˚adad programmering inneb¨ar att man delar upp arbete i flera tr˚adar f¨or att f¨orhoppningsvis f¨orb¨attra prestandan. F¨orfattaren av denna rapport ville unders¨oka hur tr˚adade l¨osningar kan utnyttjas f¨or ¨andam˚al som procedurell generering.

Detta examensarbete resulterade i en applikation d¨ar man kan g˚a runt i en lagom stor skog och alla tr¨ad runt omkring svajar i vinden. Ett speciall¨age d¨ar ett tr¨ad kan kon-strueras fr˚an grunden i realtid implementerades i testsyfte.

(3)

Summory

The focus of this report is a method to generate trees and the creation of an application which simulates wind sway by utilizing multi-threaded techniques.

Procedural generation is a vastly growing area in game development where disk usage has become an issue to store large amounts of unique content. By generating models, textures, animations and similar content mathematically during run-time using only some parameters as input one can decrease the memory storage.

The tree generation uses a geometrical method rather than some strict botanical model. The algorithm has been shown to produce incredibly realistic looking trees for a vast amount of species. A tree is represented only by a few parameters. These could include such things as: the scale of branches defined relative their “parent”; the direction branches tend to grow; the number of leaves per branch, and so on. All species uses the same set of parameters. Given a specific random seed a tree can be reproduced deterministically.

A “thread” in the context of computer science is an abbrevation for thread of execution and involves two or more processes executing in parallel (simulated or physical). Multi-threaded programming involves the process of splitting work load across multiple threads in the hope of increasing run-time performance. The author of this report wanted to examine how multi-treaded solutions can be put to use for the purpose of procedural generation.

The results of this thesis project was a demo where one can walk around in a moderately large forest where each surrounding tree sway in the wind. A special mode was developed where a tree can be designed from the ground up in real-time for testing purposes.

(4)

Abstract

Denna rapport behandlar procedurell generering av tr¨ad och skapandet av en applikation som med hj¨alp av multitr˚adade tekniker genererar och renderar dessa i realtid. Detta exemplifieras av att tr¨aden p˚averkas av vindkrafter. Algoritmen f¨or tr¨adgenerering be-skrivs utf¨orligt samt redog¨or f¨or olika tekniker f¨or att uppn˚a realtidsprestanda n¨ar tr¨aden p˚averkas av vindkrafter.

Nyckelord: procedurell generering, tr¨ad, vind, multitr˚adning, realtidsrendering, da-torgrafik, spel

(5)

Inneh˚

all

1. Introduktion 7 1.1. Bakgrund . . . 7 1.2. Syfte . . . 7 1.3. M˚al . . . 7 1.3.1. Huvudm˚al . . . 7 1.3.2. Delm˚al. . . 8 2. Teori 9 2.1. Tr¨adgenerering . . . 9 2.1.1. Introduktion . . . 9 2.1.2. Stamkurvan . . . 10 2.1.3. F¨orgreningar . . . 11

2.1.3.1. Klonade stammar (splitting) . . . 11

2.1.3.2. Barnstammar (branching). . . 12

2.1.4. Stamradie . . . 14

2.1.4.1. Avsmalning (tapering). . . 14

2.1.4.2. Exponentiell expansion av basstammen (flaring ) . . . 15

2.1.4.3. Sv¨angande radie (lobing) . . . 16

2.1.5. L¨ov . . . 17

2.1.5.1. Distribution . . . 17

2.1.5.2. Orientering . . . 18

2.1.6. Vindp˚averkan . . . 18

2.2. Multitr˚adning . . . 19

2.2.1. Kritiskt omr˚ade. . . 19

2.2.2. Omsesidig uteslutning¨ . . . 19 2.2.2.1. L˚asningsstrategier . . . 19 2.2.3. Vanliga f¨allor . . . 20 2.2.3.1. Deadlock . . . 20 2.2.3.2. Starvation . . . 20 2.2.3.3. Race condition . . . 20 2.2.4. Interlock-operationer . . . 21 2.3. Realtidsrendering . . . 21 2.3.1. Utgallring . . . 21 2.3.2. Level of Detail . . . 21 2.3.3. Impostoring . . . 22 2.3.4. Instancing . . . 22

(6)

3. Metod 23

3.1. Val av metod . . . 23

3.2. Kritik till vald metod. . . 23

3.3. Bibliotek. . . 23

3.3.1. Allm¨anna ¨andam˚al . . . 23

3.3.2. Multitr˚adad programmering . . . 24

3.3.3. Ovrigt¨ . . . 24 4. Resultat 26 4.1. Implementation . . . 26 4.1.1. Vindp˚averkan . . . 26 4.2. Portabilitet . . . 27 4.3. Prestanda . . . 28 4.4. Utseende . . . 29

5. Analys och diskussion 30 5.1. Tr¨adgenerering . . . 30 5.2. Multitr˚adning . . . 30 5.3. Vindp˚averkan . . . 31 5.4. Level of Detail . . . 31 5.5. Instancing . . . 31 6. Slutsatser 33 A. Sk¨armdumpar 35 B. Exempelkod 37 B.1. Uppdatering. . . 37 B.2. Rendering . . . 40

(7)

1. Introduktion

1.1. Bakgrund

Procedurell generering inneb¨ar att n˚agonting skapas med hj¨alp av algoritmer (d.v.s. procedurer ). Detta kan vara saker som texturer, modeller, animationer eller till och med musik. Endast n˚agra f˚a parametrar som definierar karakteristiska egenskaper anv¨ands i ett matematiskt uttryck eller algoritm som genererar ett slumpm¨assigt utseende av godtycklig uppl¨osning.

Procedurella tekniker ¨ar ett snabbt v¨axande omr˚ade inom spelindustrin. Att p˚a ett realistiskt vis visualisera saker som landskap, vegetation, tr¨ad och moln blir allt viktiga-re. Genom att ist¨allet generera content kan man sk¨ara ner p˚a utvecklingskostnader som annars skulle anv¨andas till att modellera detta f¨or hand. Samtidigt kan detta l¨osa pro-blemet med lagringsutrymme eftersom content ber¨aknas fram endast n¨ar det verkligen beh¨ovs.

Realistisk visualisering av tr¨ad och vegetation i allm¨anhet har l¨ange varit ett stort forskningsomr˚ade inom datorgrafik. De f¨orsta studierna g˚ar s˚a l˚angt bak som p˚a 1970-talet. F¨orst p˚a senare tid har det blivit m¨ojligt att presentera denna niv˚a av realism och fortfarande bibeh˚alla interaktivitet.

Samtidigt blir processorer med flera k¨arnor allt vanligare, inte minst med senaste generationens spelkonsoler. Detta inneb¨ar ett helt nytt tankes¨att f¨or programmerare och kommer blir en enorm utmaning i framtiden.

Fr˚agor som ligger till grund f¨or detta examensarbete grundar sig i hur multitr˚adning kan f¨orb¨attra prestandan, samt att finna en praktisk till¨ampning av detta med procedu-rellt genererade tr¨ad.

1.2. Syfte

Syftet med detta examensarbete ¨ar att unders¨oka hur multitr˚adade paradigmer kan prestera b¨attre ¨an den enkeltr˚adade varianten. En praktisk till¨ampning av procedurellt genererade tr¨ad ska unders¨okas om det g˚ar utnyttja f¨or n¨amnda ¨andam˚al.

1.3. M˚

al

1.3.1. Huvudm˚al

Att konstruera en applikation som p˚a ett matematiskt och algoritmiskt vis genererar tr¨ad samt renderar dessa i realtid med hj¨alp av multitr˚adade tekniker f¨or f˚a tr¨aden att svaja i vinden.

(8)

1.3.2. Delm˚al

Portabilitet

Att skriva portabel kod ¨ar n˚agot jag anser ¨ar v¨aldigt viktigt. D¨arf¨or har jag satt upp som m˚al att applikationen ska kunna k¨oras p˚a s˚a v¨al Linux som Windows, men ¨aven vara portabel nog att kunna portas till fler plattformar utan st¨orre sv˚arigheter.

Utseende

Inga st¨orre krav st¨alls p˚a utseende men saker som per-pixelbelysning, bump mapping och skuggor vore ¨onskv¨art. ¨Aven terr¨ang och annan form av vegetation om tid finns.

(9)

2. Teori

2.1. Tr¨

adgenerering

Tr¨adgenereringen i detta examensarbete bygger p˚a en geometrisk metod snarare ¨an en strikt botanisk. Flera studier har gjorts inom b˚ada omr˚aden men den geometriska metoden presenterad av Weber et al. [5] ligger till grund f¨or detta examensarbete. Deras arbete kommer beskrivas h¨ar men f¨or mer ing˚aende detaljer h¨anvisas till originalarbetet. Algoritmen har visat sig kunna producera otroligt realistiska tr¨ad av en m¨angd olika arter. Se figur 2.1och [5] f¨or vackra visualiseringar.

Figur 2.1.: Tre olika tr¨adarter producerade av algoritmen. Bilder fr˚an “Creation and Rendering of Realistic Trees” av Jason Weber och Joseph Penn. ACM SIGGRAPH ’95 Conference Proceedings, pp. 119-128.

2.1.1. Introduktion

Ett tr¨ad representeras enbart av vissa parametrar. Detta kan vara saker som: grenarnas skala i f¨orh˚allande till sin “f¨or¨alder”; riktningen grenarna tenderar att v¨axa ˚at; antal l¨ov per gren, osv. Alla tr¨adarter anv¨ander sig av samma upps¨attning parametrar. Givet ett visst random seed kan tr¨adet ˚aterskapas deterministiskt.

Parametrarna f¨oljer en viss namnstandard i form av CurveResn d¨ar n betecknar

den aktuella rekursionsniv˚an. Tr¨adstammen anses befinna sig p˚a niv˚a 0 och de f¨orsta f¨orgreningarna ¨ar rekursionsniv˚a 1, osv. I det fall niv˚an ¨ar explicit s˚a skrivs CurveRes0

(10)

och CurveRes1. Ett postfix V anv¨ands f¨or att indikera en slumpterm som adderas

till tillh¨orande parameter. Observera att parametrar skrivs med CamelCasing och bold italic och kan antas finnas f¨ordefinierade.

F¨or att ta ett exempel: SplitAnglenoch SplitAngleVnbetyder att den resulterande parametern blir φ = SplitAnglen+ Random(SplitAngleVn) d¨ar Random() returnerar i intervallet [−SplitAngleVn, SplitAngleVn]. I formler skrivs enbart SplitAnglen± SplitAngleVn f¨or att mena samma sak. Alla vinklar och l¨angdenheter ¨ar i grader och meter, om inget annat anges.

2.1.2. Stamkurvan

Modellen bygger p˚a tv˚a element; stammarna och l¨oven. En “stam” ¨ar i modellen en generell term f¨or tr¨adstammen och alla grenar och kvistar. Varje stam har sitt eget koordinatsystem med sin centrala axel parallellt med sin lokala y-axel. Det globala ko-ordinatsystemet ¨ar ett h¨ogerhandssystem med marken som xz-planet och y-axeln pekar upp mot skyn. Observera att [5] l˚ater ist¨allet z-axeln vara den axel som ¨ar vinkelr¨att med markplanet.

En stam delas in i flera “segment” som best˚ar av flera punkter formade i ett semi-cirkul¨art m¨onster. Dessa segment ¨ar de som kopplas samman f¨or att rita en triangul¨ar mesh.

Antalet segment per stam f¨or en given rekursionsniv˚a n best¨ams av parametern CurveResn. Varje segment har i sin tur en egen orientering relativt f¨oreg˚aende

seg-ment. Detta best¨ams av ett antal parametrar p˚a f¨oljande vis:

i f ( CurveBack [ n ] == 0 ) a n g l e = Curve [ n ] / CurveRes [ n ] ; e l s e { i f ( s e g m e n t s < ( CurveRes [ n ] + 1 ) / 2 ) a n g l e = 2 . 0 ∗ Curve [ n ] / CurveRes [ n ] ; e l s e a n g l e = 2 . 0 ∗ CurveBack [ n ] / CurveRes [ n ] ; }

F¨or att f˚a lite variation adderas CurveVnenligt:

i f ( CurveV [ n ] >= 0 ) {

f l o a t v a r = random ( CurveV [ n ] ) ; // [−CurveV , CurveV ]

a n g l e += v a r / CurveRes [ n ] ;

} // S p e c i a l mode

e l s e a n g l e = CurveVary [ n ] ; // − form a s a h e l i x

Varje segment roterar sig runt sin lokala x-axel angle grader relativt det f¨oreg˚aende segmentet. Antalet segment som hittils skapats anges av segments och anv¨ands f¨or att rotera ena halvan av segment p˚a ett vis och andra halvan p˚a ett annat. Detta ger m¨ojlighet till en S-formad kurva f¨or stammen.

(11)

2.1.3. F¨orgreningar

Tr¨adet best˚ar av en tr¨adstam med en varierande kurvstruktur som liknar en kon. Denna struktur kan i sig dela sig l¨angs sin egen l¨angd enligt de olika typerna av f¨orgreningar (se figur2.2) och som formar respektive antingen “kloner” (stem splits) eller “barn” (stem branches), vilka i sin tur kan dela sig p˚a liknande vis och s˚a vidare.

Figur 2.2.: Olika typer av f¨orgreningar: (I) Splitting (II) Branching 2.1.3.1. Klonade stammar (splitting)

L¨angs l¨angden av en stam kan flera kloner bildas som anses befinna sig p˚a samma rekursiva niv˚a som sin tvilling och ¨arver alla dess egenskaper. Frekvensen av splittringar f¨or en stam anges av SegSplitsn. Detta ¨ar oftast ett v¨arde mellan 0 - 1 d¨ar 1 betyder en vanlig tudelning p˚a alla segment. Ett v¨arde av 2 indikerar en tredelning.

Som Weber et al. [5] po¨angterar s˚a kan andelen splittringar snabbt n˚a o¨onskade v¨arden. Ett exempel ges med CurveResn= 5 och SegSplitsn= 2 vilket resulterar i att en enda

stam splittras till 81 separata kloner: (SegSplitsn+ 1)CurveResn−1 = 34 = 81

En ytterligare parameter BaseSplitsn fungerar p˚a samma vis som SegSplitsn men anv¨ands enbart p˚a f¨orsta segmentet p˚a sj¨alva tr¨adstammen. P˚a s˚a vis kan man modellera tr¨ad som enbart splittras vid r¨otterna och som sedan ej har n˚agra splittringar f¨or resten av tr¨adet.

F¨or att distributionen av splittringar ska f¨ordelas j¨amnt l¨angs stammen anv¨ands en teknik liknande Floyd-Steinberg Error Diffusion [3]. Mer detaljer om detta h¨anvisas till [5].

(12)

Klonerna roteras bort φsplitgrader fr˚an sin y-axel relativt y-axeln till det segment som

skapade dem, enligt: − →v = R   0 1 0   φdeclination = 180 arccos −→vy π (grader)

φsplit= max (0, (SplitAnglen± SplitAngleVn) − φdeclination) (grader) (2.1)

d¨ar −→v ¨ar stammens riktningsvektor i sitt globala koordinatsystem (d.v.s. enhetsvektorn parallell med lokala y-axeln som transformerats av segmentets globala rotationsmatris R). Vinkeln φdeclination definieras som vinkeln mellan en stam och tr¨adets y-axel.

Ori-ginalstammen och dess kloner roterar runt en axel parallell med den globala y-axeln, enligt: φrotate= ±  20 + 3 4(30 + |φdeclination− 90|) ε 2  (grader) (2.2) d¨ar ε slumpas mellan [0, 1].

Ekvation 2.2 beskriver hur tv˚a horisontella grenar (φdeclination = 90 ± 10)

diverge-rar fr˚an varandra mellan 20 – 50 grader runt den parallella axeln och vertikala grenar (φdeclination = 0 ± 40) mellan 20 – 140 grader. Detta d˚a horisontella grenar tenderar att

se onaturliga ut om de divergerar med f¨or stora vinklar. 2.1.3.2. Barnstammar (branching)

Barnstammar ¨ar stammar som skapats via en vanlig f¨orgrening fr˚an stam till gren, gren till kvistar, kvistar till l¨ov. Till skillnad fr˚an klonade stammar (splitting) d¨ar stammar-na delade sig och fortsatte under samma rekursionsniv˚a anses barnstammar vara en rekursionsniv˚a under sin f¨or¨alder. Oftast har ett tr¨ad enbart 4 rekursionsniv˚aer, d¨ar hu-vudstammen ¨ar 0, grenarna 1, kvistarna 2 och l¨oven 3. D¨aremot st¨alls inga begr¨ansningar vad g¨aller algoritmen utan det ¨ar bara vad som anv¨ands f¨or de flesta tr¨adarter. Skulle ett tr¨ad anv¨anda sig av fler rekursionsniv˚aer ¨an vad som definierats s˚a anv¨ands sista niv˚an till n¨astf¨oljande f¨orgreningar.

Det maximala antalet barn-stammar som en stam kan skapa under f¨orloppet av alla sina segment anges av stemsmax = Branchesn+1. Det faktiska antalet kan vara mindre

¨

an detta v¨arde. Andelen barn-stammar f¨or en stam ber¨aknas: stems = stemsmax



0.2 + 0.8 lengthchild

lengthparentlengthchildmax



(2.3) f¨or rekursionsniv˚a 1, och

stems = stemsmax



1.0 − 0.5 of f setchild lengthparent



(13)

f¨or resterande. D˚a huvudstammen ej har n˚agon f¨or¨alder anges antalet barnstammar enligt stems = stemsmax.

of f setchild ¨ar barnstammens position angivet i meter och utg˚ar fr˚an basen av sin

f¨or¨alder. Barnstammens maximala l¨angd lengthchildmax definieras relativt sin f¨or¨alders

l¨angd som en faktor [0,1] och anges som Lengthn± LengthVn. Barnstammens l¨angd lengthchild givet en normaliserad position ratio [0,1] ber¨aknas:

lengthchild= lengthtrunk· lengthchildmax· ShapeRatio (Shape, ratio) (2.5)

f¨or f¨orsta rekursionsniv˚an, och

lengthchild= lengthchildmax (lengthparent− 0.6 · of f setchild) (2.6)

f¨or resterande.

Tr¨adstammens l¨angd lengthtrunkoch l¨angden av tr¨adstammens nedre region lengthbase

anges i meter och definieras enligt:

lengthtrunk = (Length0± LengthV0) (Scale ± ScaleV) (2.7)

lengthbase = BaseSize (Scale ± ScaleV) (2.8)

vilket efter den normaliserade positionen ratio kan ber¨aknas: ratio = lengthtrunk− of f setchild

lengthtrunk− lengthbase

(2.9) Funktionen ShapeRatio anv¨ands f¨or att f˚a en viss distinkt form p˚a grenarna som utg˚ar fr˚an tr¨adstammen. Relationerna redovisas i tabell 2.1.

Tabell 2.1.: Definition av ShapeRatio()

Shape Resultat

0 (conical) 0.2 + 0.8 ∗ ratio

1 (spherical) 0.2 + 0.8 ∗ sin(π ∗ ratio) 2 (hemispherical) 0.2 + 0.8 ∗ sin(0.5 ∗ π ∗ ratio) 3 (cylindrical) 1.0

4 (tapered cylindrical) 0.5 + 0.5 ∗ ratio

5 (flame) ratio/0.7 (ratio ≤ 0.7)

(1.0 − ratio)/0.3 (ratio > 0.7) 6 (inverse conical) 1.0 − 0.8 ∗ ratio

7 (tend flame) 0.5 + 0.5 ∗ ratio/0.7 (ratio ≤ 0.7) 0.5 + 0.5 ∗ (1.0 − ratio)/0.3 (ratio > 0.7)

En ytterligare Shape = 8 (pruning envelope) beskrivs i [5]. Parametern anv¨ands f¨or att klippa ¨overfl¨odig l¨angd p˚a stammar p˚a tr¨ad som inte riktigt l¨ampar sig f¨or l¨angdber¨akningen ovan. Metoden implementeras ej i detta examensarbete.

(14)

Orienteringen av en barnstam anges av en vinkel downanglechild som ¨ar

rotatio-nen l¨angs barnstammens x-axel relativt f¨or¨alderns y-axel. Om DownAnglen ¨ar positiv ber¨aknas vinkeln enligt DownAnglen± DownAngleVn, men om negativ ber¨aknas den p˚a f¨oljande vis:

r = 1 − 2 · ShapeRatio 

0,lengthparent− of f setchild lengthparent− lengthbase



downanglechild= DownAnglen± (r DownAngleVn) (2.10)

Detta uttryck anv¨ands f¨or att linj¨art kunna p˚averka vinkeln baserat p˚a positionen av barnstammen l¨angs en f¨or¨alder. P˚a s˚a vis kan man f˚a grenar att b¨oja sig ned˚at l¨angs nedre del av en stam och som sedan allt mer b¨ojer sig lodr¨att ju h¨ogre upp i stammen barnstammarna v¨axer. J¨amf¨or beteendet med en gran.

2.1.4. Stamradie

D˚a radien f¨or stammar (vid segmentet de skapades vid) ej f˚ar ¨overskrida sin f¨or¨alders stamradie definieras radien som en funktion av f¨or¨aldens. Detta g¨aller som vanligt inte huvudstammen d˚a den ej har n˚agon f¨or¨alder, utan definieras ist¨allet proportionellt mot skalan f¨or hela tr¨adet. Huvudstammen definieras enligt

radiustrunk = lengthtrunk· Ratio · Scale0 (2.11)

och ¨ovriga enligt

radiuschild= radiusparent

 lengthchild

lengthparent

RatioPower

(2.12)

2.1.4.1. Avsmalning (tapering)

Radien f¨or en stam kan ¨aven smalnas av l¨angs sin egen l¨angd enligt olika metoder redo-visade i tabell2.2. Detta g¨ors d˚a tr¨adarter kan ha helt olika utseede p˚a sina stammar. En periodisk avsmalning anv¨ands fr¨amst f¨or kaktusar och palmer. Algoritmen som ber¨aknar radien f¨or ett segment f¨or en given normaliserad position Y l¨angs en stam redovisas i listing2.1.

Tabell 2.2.: Metoder f¨or avsmalning Tapern Effekt

0 Ingen avsmalning

1 Avsmalnar till en punkt (kon) 2 Avsmalnar till en sf¨arisk ¨and

(15)

Listing 2.1: Avsmalning l¨angs l¨angden av en stam f l o a t t a p e r = Taper [ t h i s −>l e v e l ] ; f l o a t u n i t t a p e r = 0 . 0 ; i f ( t a p e r < 1 ) u n i t t a p e r = t a p e r ; e l s e i f ( t a p e r < 2 ) u n i t t a p e r = 2− t a p e r ; /∗ ’Y ’ d e f i n e d e l s e w h e r e a s t h e u n i t p o s i t i o n [ 0 , 1 ] ∗/ f l o a t r a d i u s = t h i s −>r a d i u s ∗ (1− u n i t t a p e r ∗Y) ; i f ( t a p e r >= 1 ) { f l o a t Y2 = (1−Y) ∗ t h i s −>l e n g t h ; /∗ p e r i o d i c t a p e r i n g ∗/ f l o a t depth ; i f ( t a p e r <2 | | Y2<r a d i u s ) depth = 1 ; e l s e depth = t a p e r −2; f l o a t Y3 ; i f ( t a p e r <2) Y3 = Y2 ; e l s e Y3 = s t d : : f a b s ( Y2 − 2∗ r a d i u s ∗ i n t ( Y2 / 2 . 0 / r a d i u s + 0 . 5 ) ) ; i f ( t a p e r >=2 | | Y3<r a d i u s ) r a d i u s = (1− depth ) ∗ r a d i u s +

depth ∗ s t d : : s q r t ( r a d i u s ∗ r a d i u s − ( Y3−r a d i u s ) ∗ ( Y3−r a d i u s ) ) ; }

2.1.4.2. Exponentiell expansion av basstammen (flaring)

Radien i tr¨adets nedre region tenderar att variera kraftigt vilket varf¨or en exponentiell utveckling av segment appliceras med en varierande radie ber¨aknad som f (r, Y ) med stamradie r och en normaliserad position Y [0,1] l¨angs stambasen. Se figur 2.3 f¨or ett exempel med CurveRes0 = 10.

Funktionen f (r, Y ) definieras enligt f (r, Y ) =  z = max (0, 1 − 8 ∗ Y ) f lare = 1 + Flare (100100z−1)  = r · f lare (2.13) vilket skalar radien med en faktor f lare begr¨ansad i intervallet [1, 1 + Flare).

(16)

Figur 2.3.: Exponentiell expansion vid tr¨adets nedre region. 2.1.4.3. Sv¨angande radie (lobing)

“Lobing” ¨ar en term som anv¨ands inom elektromagnetism och inneb¨ar en variation av min- och max-niv˚aer 1. H¨ar inneb¨ar det en variation av radien f¨or varje punkt i ett segment hos tr¨adstammen f¨or att f˚a ett mer organiskt utseende i tr¨adets nedre region.

F¨or varje punkt adderas till radien en procentuell andel LobeDepth ∗ sin(Lobes ∗ i ∗ angle) vilket resulterar i en sinuskurva med Lobes antal perioder och en amplitud som sv¨anger runt originalradien med en LobeDepth procentuell ¨okning och minskning. Figur 2.4 visar hur LobeDepth p˚averkar radien f¨or tv˚a olika v¨arden. Ett v¨arde som LobeDepth = 0.0 inneb¨ar helt enkelt att radien minskar och ¨okar med 0.0% av den ursprungliga radien, medan ett v¨arde som 1.0 skulle inneb¨ara en radie som sv¨anger fr˚an 0.0 till dubbla radien. Algoritmen redovisas i listing2.2.

(17)

Figur 2.4.: T.v. LobeDepth = 0.0; T.h. LobeDepth = 0.1

Listing 2.2: Implementation av “Lobing” som varierar radien f¨or varje punkt i ett seg-ment. f l o a t a n g l e = 2 . 0 ∗ math : : PI / P o i n t s [ l e v e l ] ; f o r ( i n t i =0; i <P o i n t s [ l e v e l ] ; i ++) { f l o a t l o b e d r a d i u s = r a d i u s ∗ ( 1 . 0 + LobeDepth ∗ s i n ( Lobes ∗ i ∗ a n g l e ) ) ; x = c o s ( i ∗ a n g l e ) ∗ l o b e d r a d i u s ; y = 0 . 0 ; z = s i n ( i ∗ a n g l e ) ∗ l o b e d r a d i u s ; l o c a l p o i n t s [ i ] = V e c 3 t ( x , y , z ) ; } 2.1.5. L¨ov 2.1.5.1. Distribution

En parameter Levels anv¨ands f¨or att definiera det maximala antalet niv˚aer som ett tr¨ad har och n¨ar det ska b¨orja v¨axa l¨ov ist¨allet f¨or fler grenar. N¨ar maxniv˚an n˚as anv¨ands para-metrarna DownAnglen, DownAngleVn, Rotatenoch RotateVnfr˚an f¨orra niv˚an f¨or

att orientera l¨ovet. Antalet l¨ov anges av Leaves som motsvarar parametern Branchesn

f¨or att definiera t¨atheten f¨or skapande av l¨ov. Det faktiska antalet l¨ov per stam ber¨aknas enligt

leaves = Leaves · ShapeRatio 

4, of f setchild lengthparent



· quality (2.14) d¨ar quality ¨ar en faktor som kan anv¨andas till att finjustera antalet efter behov. En definition av ShapeRatio redovisades i tabell2.1.

(18)

I [5] diskuteras ¨aven ett speciall¨age n¨ar Leaves ¨ar negativ f¨or att f˚a l¨oven att dis-tribueras fr˚an en central punkt liknande palmblad. Metoden implementeras ej i detta arbete.

L¨oven kan visualiseras som olika geometriska figurer enligt en parameter LeafShape. Bland figurerna finns en sexkantig oval, triangel eller mer komplicerade figurer som liknar l¨onnblad. Varje figur sparas med en normaliserad l¨angd och bredd d¨ar de faktiska v¨ardena ber¨aknas under genereringen enligt f¨oljande skalningsfaktorer:

lengthscale= LeafScale/

p

quality widthscale= (LeafScale · LeafScaleX) /

p

quality (2.15)

2.1.5.2. Orientering

I verkligheten tenderar l¨ov att orientera sig med ovansidan upp˚at och ut˚at med den mest t¨ankbara anledningen att optimera m¨angden solljus. Detta simuleras genom att orientera l¨oven relativt det segment som skapade dem och mot en riktning s˚a l¨ovets normal hamnar vinkelr¨att mot stammens lokala y-axel.

Vinklarna ber¨aknas enligt

θposition= arctan2(pz, px)

θbend= θposition− arctan2(nz, nx)

φbend= arctan2 p n2 x+ n2z, ny  (2.16)

d¨ar p ¨ar l¨ovets position och n ¨ar l¨ovets normal. Rotationen runt den lokala y-axeln och lokala x-axeln anges respektive av θbendoch φbend.

2.1.6. Vindp˚averkan

Vindkrafterna simuleras som en oscillering f¨or varje enstaka stam med ett segment fixerat i ena ¨anden. Sv¨angningen ber¨aknas f¨or varje segment l¨angs en stam vid en viss tidpunkt time (sekunder) och roteras relativt det f¨oreg˚aende segmentet runt x- och z-axeln med vinklarna −→ψswayx och

− →

ψswayz och ber¨aknas enligt

− → b =−→ψof f set+ radiusstem lengthstem   1 0 1   time 15 − → ψsway = a1 sin − → b + a2 sin  0.7−→b CurveResn (2.17)

d¨ar koefficienterna a1 och a2 definieras enligt

a0 = 4 ∗ lengthstem (1 − Y ) /radiusy

a1 = windspeed/50 ∗ a0

a2 = windgust/50 ∗ a0+ a1/2

(19)

d¨ar Y ¨ar den normaliserade positionen l¨angs stammen och radiusy ¨ar det aktiva

seg-mentets radie.

Den totala vindkraften anges av (windspeed+ windgust) d¨ar windgust kan anv¨andas

till att introducera hastiga vindpustar. En f¨orskjutning i x- och z-led angivet av−→ψof f set

slumpas f¨or varje stam. Ett mer praktiskt exempel av ovan ges i listing4.1.

2.2. Multitr˚

adning

En “tr˚ad” (thread ) inom datateknik ¨ar en f¨orkortning f¨or thread of execution och inneb¨ar att tv˚a eller flera processer exekverar samtidigt (simulerad samtidighet eller fysisk). Multitr˚adad programmering inneb¨ar att man delar upp arbete i flera tr˚adar f¨or att f¨orhoppningsvis f¨orb¨attra prestandan.

Ett av de m˚anga problem som kan uppst˚a ¨ar n¨ar tv˚a eller fler tr˚adar anv¨ander sig av delade resurser samtidigt. Om en tr˚ad skriver till resursen och en annan samtidigt l¨aser, eller om b˚ada tr˚adar skriver till resursen samtidigt blir resultatet odefinierat [2].

2.2.1. Kritiskt omr˚ade

Ett kritiskt omr˚ade (critical section) ¨ar det omr˚ade i koden som en och endast en tr˚ad ˚at g˚angen f˚ar befinna sig i n¨ar den anv¨ander sig av en delad resurs. F¨or att garantera detta m˚aste olika typer av synkronisering till¨ampas, antingen mjukvarubaserade eller h˚ardvarubaserade.

Detta omr˚ade ¨ar ytterst viktig att vara s˚a optimal som m¨ojligt d˚a andra tr˚adar kan ej utf¨ora n˚agot arbete under tiden om de kr¨aver tillg˚ang till samma resurs [2].

2.2.2. ¨Omsesidig uteslutning

F¨or att garantera att en och endast en tr˚ad ˚at g˚angen l¨aser eller skriver till en delad resurs kr¨avs olika typer av synkronisering. En av de mest grundl¨aggande formerna av synkronisering ¨ar en s˚a kallad “mutex” (mutual exclusion) eller ¨omsesidig uteslutning p˚a svenska. Genom att f¨orst l˚asa den mutex som h¨or till resursen f¨ore resursen anv¨ands garanteras att ingen annan tr˚ad l¨aser fr˚an eller skriver till samma resurs – f¨orutsatt att l˚asningen g¨ors varje g˚ang resursen anv¨ands.

2.2.2.1. L˚asningsstrategier

Det finns i huvudsak tre olika l˚asningsstrategier f¨or en mutex. Dessa ¨ar: • Lock

• Try Lock • Timed Lock

(20)

En vanlig Lock fungerar s˚a att n¨ar en tr˚ad lyckats l˚asa en resurs s˚a blockeras de andra tr˚adarna som f¨ors¨oker l˚asa resursen under tiden l˚aset ¨ar aktivt. N¨ar l˚aset upph¨or s˚a f˚ar n¨asta tr˚ad chansen att l˚asa och de andra v¨antar. En Try Lock testar f¨orst om en mutex ¨ar l˚ast och l˚aser den om den lyckas. Exekveringen forts¨atter som vanligt oavsett om resursen var l˚ast eller ej, vilket efter man kan testa om den lyckades l˚asa. F¨ordelen med detta ¨ar att tr˚aden ej blockeras om resursen skulle visa sig vara l˚ast, utan tr˚aden kan forts¨atta med n˚agot annat under tiden. En Timed Lock ¨ar en hybrid mellan dem b˚ada d¨ar den f¨orst blockerar och sedan forts¨atter exekverar om den inte skulle lyckas l˚asa resursen inom rimlig tid.

2.2.3. Vanliga f¨allor

Inte nog med att det kan vara sv˚art att synkronisera delade resurser enligt ovan s˚a kan synkroniseringarna i sig st¨alla till stora problem. Nedan beskrivs kortfattat ett urval av de vanligaste f¨allorna men detta ¨ar l˚angt ifr˚an alla.

2.2.3.1. Deadlock

En “deadlock” inneb¨ar att tv˚a eller fler tr˚adar v¨antar p˚a varandra i ett s˚adant tillst˚and att de v¨antar i all evighet. Detta kan intr¨affa om l˚asningar av resurser g¨ors i omv¨and ordning vilket illustreras b¨ast av ett litet exempel:

• Tr˚ad 1 l˚aser A • Tr˚ad 2 l˚aser B

• Tr˚ad 1 l˚aser B – B redan l˚ast, v¨antar... • Tr˚ad 2 l˚aser A – A redan l˚ast, v¨antar...

Ovanst˚aende problem skulle l¨osas genom att l˚asa resurserna i samma ordning f¨or b˚ada tr˚adar men detta ¨ar inte alltid s˚a enkelt i praktiken.

2.2.3.2. Starvation

Begreppet “starvation” inneb¨ar att en tr˚ad v¨antar p˚a n˚agot som kanske aldrig intr¨affar. Om en tr˚ad har r¨attigheterna till en resurs som en annan beh¨over men den ena tr˚aden ger aldrig ifr˚an sig den s˚a ¨ar det en form av starvation (som namnet antyder; den andra tr˚aden “sv¨alter ihj¨al”). Detta kan ¨aven ses som en form av deadlock men skillnaden ¨ar att med deadlock v¨antar tr˚adar p˚a varandra medan starvation ¨ar det en tr˚ad som v¨agrar ge ifr˚an sig n¨odv¨andiga resurser.

2.2.3.3. Race condition

En “race condition” intr¨affar n¨ar flera tr˚adar beg¨ar tilltr¨ade till samma resurs och ordningen de tilldelas tilltr¨ade sker p˚a ett ober¨akneligt vis. Detta kan st¨alla till med sv˚aruppt¨ackta problem n¨ar ordningen tr˚adarna exekverar ¨ar viktigt [2].

(21)

2.2.4. Interlock-operationer

Interlock-operationer ¨ar en form av synkronisering som g¨ors i h˚ardvara. Det kan vara operationer som swap/compare/exchange av data i minnet som m˚aste exekvera atomiskt. En atomisk operation inneb¨ar att den ¨ar odelbar i det avseendet att ingen annan proces-s/tr˚ad kan l¨asa eller modifiera det data som en annan tr˚ad anv¨ander. I och med att detta g¨ors i h˚ardvara s˚a ¨ar det mycket snabbare ¨an vanlig mjukvarusynkronisering i form av en mutex eller semafor. Detta ¨ar d¨aremot inget som ers¨atter traditionell synkronisering utan ¨

ar mer ett komplement. Kritiska omr˚aden har sina till¨ampningar n¨ar hela datastrukturer m˚aste synkroniseras medan interlock-operationer kommer till anv¨andning f¨or att imple-mentera s˚a kallade lock-free and wait-free algorithms 2. D˚a lock-free-programmering ¨ar s˚a otroligt komplicerat implementeras ist¨allet enkla datastrukturer som en stack, k¨o eller l¨ankad lista. Dessa kan sedan anv¨andas utan n˚agon form av extern synkronisering — alla tr˚adar kan modifiera strukturen samtidigt.

2.3. Realtidsrendering

2.3.1. Utgallring

Utgallring ¨ar en m¨angd olika metoder f¨or att f¨orb¨attra prestandan genom att endast rendera s˚ant som verkligen syns p˚a sk¨armen. Detta kan vara tekniker som back-face culling som endast renderar fram˚atv¨anda trianglar, eller frustum culling som j¨amf¨or om ett objekts gr¨anser befinner sig inom kamerans vyfrustum. Andra tekniker kan vara occlusion culling som ber¨aknar objekt som skyms av st¨orre objekt och kan p˚a s˚a vis utesluta dem ur renderingen.

Den algoritm som ¨ar mest anv¨andbar f¨or tr¨ad skulle vara frustum culling d¨ar ett r¨atblock eller en sf¨ar (vanligen ben¨amnt bounding box och bounding sphere) ber¨aknas som innesluter hela tr¨adet och som j¨amf¨ors med kamerans sex stycken s˚a kallade vyfrustum-plan. Om boxen eller sf¨aren visar sig ligga utanf¨or alla sex plan s˚a ritas tr¨adet inte ut [1].

2.3.2. Level of Detail

D˚a objekt som befinner sig l˚angt ifr˚an bektraktaren och upptar en v¨aldigt liten del av sk¨armytan s˚a kan detta accelereras genom att rendera en f¨orenklad modell av objektet. Dessa tekniker kallas f¨or “Level of Detail” (LOD) och finns i alla m¨ojliga former. En av de enklaste kallas f¨or Discrete LOD (DLOD) och inneb¨ar att objektet byts ut s˚a fort den anses befinna sig p˚a ett visst avst˚and fr˚an betraktaren. Ett ¨ok¨ant problem med DLOD-algoritmer ¨ar artefakter som popping n¨ar bytet mellan en niv˚a till en annan blir synlig. Detta kan f¨orb¨attras genom att g¨ora bytet mer progressivt med blending.

Till mer avancerade algoritmer h¨or “Continuous LOD” (CLOD) och “Geomorph LOD” som dynamiskt f¨orenklar geometrin och interpolerar punkterna emellan niv˚aerna f¨or att undvika popping [1].

2

(22)

2.3.3. Impostoring

En impostor ¨ar en billboard som skapas dynamiskt genom att rendera ett komplicerat 3d-objekt till en textur fr˚an den aktiva vyn som sedan mappas till billboarden [1]. Ist¨allet f¨or att rendera tusentals trianglar blir komplexiteten ist¨allet det antal pixlar objektet upptar p˚a sk¨armen. Detta fungerar b¨ast f¨or statiska objekt och objekt som befinner sig p˚a l˚angt avst˚and d˚a annars m˚aste impostorn uppdateras oftare f¨or att illusionen inte ska g˚a f¨orlorad.

Impostorn skapas genom att rendera objektet till en offscreen buffer med vyn orien-terad s˚a att den kollar i objektets bounding box centrum. En polygon med texturen mappad s¨atts att peka i betraktarens riktning. Detta g˚ar g¨ora v¨aldigt effektivt med dagens grafikkort d˚a renderingen kan g¨oras direkt till texturen.

2.3.4. Instancing

En anv¨andbar optimeringsteknik ¨ar en metod som kallas instancing (se figur 2.5). Tek-niken inneb¨ar att man anv¨ander samma geometriska data f¨or flera instanser av objekt. Detta accelererar renderingen avsev¨art d˚a man enbart beh¨over skicka geometrin till gra-fikkortet en enda g˚ang f¨or ett unikt objekt och flera instanser. Dessa instanser kan f˚as att ha en helt annan skala, orientering, position, material, textur, shader, osv. Men ¨and˚a gemensamt anv¨anda samma geometri. Detta ¨ar speciellt f¨ordelaktigt f¨or objekt som tr¨ad d˚a det kan vara v¨aldigt sv˚art att se att det faktiskt ¨ar samma geometri.

(23)

3. Metod

3.1. Val av metod

Implementationen bygger p˚a ett och samma paper f¨or tr¨adgenerering med viss modifika-tion f¨or att uppn˚a realtidsprestanda. Vindsvaj valdes att implementeras p˚a CPU:n med hj¨alp av multitr˚adade tekniker.

Det programspr˚ak som anv¨andes var C++. F¨or ˚atkomst till grafikh˚ardvaran anv¨andes OpenGL och GLSL. Utvecklingsmilj¨on bestod av Emacs, gcc, gdb och SCons. ¨Aven MS Visual Studio 2005 anv¨andes m˚attligt f¨or testning vad g¨aller m˚alen st¨allda i 1.3.2. Versionshantering gjordes genom Subversion. Rapporten ¨ar typsatt med LATEX.

3.2. Kritik till vald metod

Enligt kraven p˚a portabilitet f¨oljer vissa kompromisser. D¨aribland tekniker som interlock-operationer (se 2.2.4) som ej finns tillg¨angligt i linux under userspace pga att vissa arkitekturer ej har st¨od f¨or det. Detta gjorde att det inte var m¨ojligt att testa olika tekniker av synkronisering.

¨

Aven valet av att implementera vindsvaj p˚a CPU:n hade varit intressant att implemen-tera p˚a grafikkortet (GPU:n) f¨or att j¨amf¨ora prestanda. Andra praktiska till¨ampningar hade varit ¨onskv¨art att testa som m¨ojligheten att befinna sig i en “o¨andlig” skog d¨ar nya tr¨ad genereras under k¨orning.

3.3. Bibliotek

Av de bibliotek jag anv¨ant mig av fogar sig efter m˚alen st¨allda i1.3.2.

3.3.1. Allm¨anna ¨andam˚al

Boost 1 ¨ar en organisation som str¨avar efter att skriva anv¨andbara och portabla C++-bibliotek av h¨og kvalit´e som l¨ampar sig f¨or eventuell standardisering. N˚agra av utveck-larna ¨ar medlemmar i C++ Standards Committee Library Working Group. I skrivande stund har tio Boost-bibliotek inkluderats i Standard C++ Library Technical Report 1 (TR1), vilket inneb¨ar att de kommer i en eller annan form vara del av n¨asta standardisering av standardbiblioteket till C++. Ytterligare bibliotek har f¨oreslagits till TR2.

Bland biblioteken i Boost h¨or saker som (ej en fullst¨andig lista):

(24)

• Regulj¨ara uttryck

• Generaliserade funktioner (funktio-ner/objekt/pekare och medlemsfunk-tioner)

• Lambda-uttryck • Signaler

• Algebra / Linj¨ar algebra

• Slumptal • Smart pointers

• Portabel ˚atkomst av filsystem • Portabel multitr˚adning • Hashmaps och hashfunktioner Alla bibliotek i Boost bygger p˚a ¨oppen k¨allkod och ¨ar fritt att anv¨anda f¨or b˚ade kommerciella och icke-kommerciella syften.

3.3.2. Multitr˚adad programmering

I Standard C++ finns ¨annu ingen m¨ojlighet att skriva tr˚adade program. Enda utv¨agen har varit att anv¨anda sig av rutinerna tillg¨angliga f¨or respektive operativsystem eller att anv¨anda sig av ett portabelt bibliotek.

Pthreads ing˚ar i POSIX-standarden och ¨ar ett API skrivit i C. Pthreads anv¨ands fr¨amst p˚a UNIX-varianter av operativsystem men implementationer f¨or Windows finns att tillg˚a. Pthreads ¨ar dock varken en C- eller en C++-standard vilket inneb¨ar att det ej finns p˚a alla plattformar som t ex spelkonsoler [?].

Boost.Threads ¨ar ett i m¨angden anv¨andbara bibliotek som h¨or till Boost. Boost.Threads m¨ojlig¨or att skriva tr˚adade och portabla objekt-orienterade program i C++. Tekniker som Scoped Locking garanterar att l˚asningen av l˚asta resurser upph¨avs vid ett intr¨affande av en exception eller en “kortsluten exekvering” via return/break/continue. Detta imple-menteras med hj¨alp av spr˚akmekaniker som konstruktion/destruktion.

¨

Aven funktioner i C++ som templates och funktionsobjekt anv¨ands flitigt. F¨ordelarna med att anv¨anda fullfj¨adrade funktionsobjekt framf¨or vanliga funktionspekare ¨ar att objekten kan spara tillst˚and. N¨ar man startar en ny tr˚ad kan man skicka med ett funk-tionsobjekt och p˚a s˚a vis ¨aven p˚a ett praktiskt vis skicka med data till tr˚aden.

Boost.Threads ¨ar det bibliotek som valdes till detta examensarbete dels p˚a grund av att det ¨ar relativt enkelt att s¨atta sig in i och dels av anledning av ovan n¨amnda finesser och dess h¨oga portabilitet. Att det ¨aven kommer med stor sannolikhet inkluderas i n˚agon form till standardbiblioteket i framtiden gjorde Boost.Threads till ett sj¨alvklart alternativ.

3.3.3. ¨Ovrigt

• F¨onsterhantering/input: SDL2

2

(25)

• Texturinladdning: DevIL 3

• Fontrendering: FreeType 4

3

http://openil.sourceforge.net/ 4http://freetype.sourceforge.net/

(26)

4. Resultat

Examensarbetet resulterade i en applikation d¨ar man kan g˚a runt i en lagom stor skog och alla tr¨ad runt omkring svajar i vinden. Ett speciall¨age d¨ar ett tr¨ad kan konstrueras fr˚an grunden i realtid implementerades i testsyfte.

4.1. Implementation

Vid uppstart l¨ases ett antal f¨ordefinierade tr¨adarter in ifr˚an textfiler och en dedikerad tr˚ad drar ig˚ang som har till uppgift att generera geometri av dessa definitioner som enbart inneh˚aller parametrar enligt2.1. Detta kan ¨aven g¨oras under k¨orning d¨ar tr¨aden genereras i bakgrunden.

Efter att tr¨aden genererats f¨ardigt s˚a g˚ar tr˚aden ¨over till att uppdatera geometrin enligt en algoritm f¨or vindp˚averkan som beskrivs i 2.1.6och implementeras i 4.1.1.

H¨ar g¨ors ¨aven ett optimeringspass d¨ar en renderingsteknik v¨aljs efter avst˚and till be-traktaren och en impostor eller en 3d-mesh v¨aljs att renderas. Observera att renderingen g¨ors i en skild tr˚ad.

N¨ar ett tr¨ad har uppdaterats s˚a kopieras geometrin till en buffert. Denna buffert anv¨ands gemensamt mellan tr˚adarna. Synkroniseringen sker enbart n¨ar pekaradressen byts ut att peka p˚a den nya bufferten. Samtidigt som tr˚aden bygger geometrin och fyller bufferten s˚a kan renderingen ske parallellt.

Om renderingstr˚aden lyckas l˚asa resursen s˚a streamas geometrin till grafikkortet via en VBO (Vertex Buffer Object ) [4]. Om resursen skulle vara l˚ast s˚a till¨ampar tr˚aden en Try Lock-strategi och forts¨atter renderingen med att rendera f¨oreg˚aende uppdatering av tr¨adet. Ingen ¨overf¨oring ¨ar n¨odv¨andig d˚a geometrin finns redan sparad p˚a grafikkortet.

F¨or att optimera uppdateringen och renderingen s˚a existerar enbart en handfull unika tr¨ad som sedan klonas till hundratals.

4.1.1. Vindp˚averkan

En absolut tid time (i sekunder) ber¨aknas in i algoritmen nedan. Funktionen evalueras p˚a varje segment som h¨or till en viss stam, d¨ar radiusY betecknar den aktuella radien vid ett segment och Y ¨ar en normaliserad position [0,1] l¨angs stammen. Den resulterande orienteringen f¨or ett segment anv¨ands till n¨astf¨oljande segment i den ordning som de ursprungligen skapades. Parametern orientation ¨ar en quaternion som representerar en orientering av f¨oreg˚aende segment.

Listing 4.1: Implementation av vindsvaj

(27)

f l o a t radiusY , f l o a t Y) { /∗ w i n d s p e e d / w i n d g u s t a r e c o n s t a n t ∗/ f l o a t a0 = 4∗ t h i s −>l e n g t h ∗(1−Y) / r a d i u s Y ; f l o a t a1 = w i n d s p e e d /50∗ a0 ; f l o a t a2 = w i n d g u s t /50∗ a0 + a1 / 2 ;

/∗ NOTE: Weber & Penn d o e s n o t mention t h e f a c t o r 1 0 0 0 , t h o u g h i t was deemed n e c e s s a r y . Perhaps t h e i r ’ t i m e ’ was a c t u a l l y d e f i n e d i n m i l l i s e c o n d s ? ∗/ f l o a t tmp = t h i s −>r a d i u s / t h i s −>l e n g t h ∗ t i m e / 1 5 ∗ 1 0 0 0 ; /∗ s w a y o f f s e t x d e f i n e d e l s e w h e r e ∗/ /∗ s w a y o f f s e t z d e f i n e d e l s e w h e r e ∗/ f l o a t bx = s w a y o f f s e t x + tmp ; f l o a t bz = s w a y o f f s e t z + tmp ;

f l o a t sway x = ( a1 ∗ s i n ( bx )+a2 ∗ s i n ( 0 . 7 ∗ bx ) ) / CurveRes [ l e v e l ] ; f l o a t s w a y z = ( a1 ∗ s i n ( bz )+a2 ∗ s i n ( 0 . 7 ∗ bz ) ) / CurveRes [ l e v e l ] ;

/∗ r o t a t i o n around X a x i s ∗/

Q u a t t rotX =

Q u a t t : : fromAxisRot ( math : : VEC3 UNIT X , sway x ) ;

/∗ r o t a t i o n around Z a x i s ∗/

Q u a t t r o t Z =

Q u a t t : : fromAxisRot ( math : : VEC3 UNIT Z , s w a y z ) ; o r i e n t a t i o n = o r i e n t a t i o n ∗ rotX ∗ r o t Z ;

}

4.2. Portabilitet

Applikationen kompilerar med GNU C++ (gcc/g++) 4.1.1-r3 och ¨aven MS Visual C++ 8 (vc8). Flaggorna -ansi och -pedantic anv¨andes i gcc. Applikationen f¨oljer med andra ord strikt ANSI/ISO-standard.

K¨orning av applikationen fungerar utan problem under Gentoo GNU/Linux x86 64 och Windows XP SP2 (Win32) som ¨ar de plattformar som varit tillg¨angliga f¨or testning. D¨armed fullf¨oljs m˚alen som st¨alldes i 1.3.2.

Applikationen l¨ar d¨ar med ¨aven utan st¨orre sv˚arigheter kunna portas till de flesta UNIX-baserade system som *BSD, Solaris och Mac OS X.

(28)

4.3. Prestanda

Testburken som anv¨andes under utvecklingen bestod av en AMD Athlon64 X2 Dual-Core “Manchester” 3800+ 2.0GHz med 1GB RAM samt ett NVIDIA GeForce 7600 GT med 256MB grafikminne.

Testet gjordes med ett tr¨ad som har 17430 trianglar. Enheten som anv¨andes var “mil-lisekunder per frame” (mspf). L¨agre ¨ar b¨attre.

Antal unika tr¨ad Rendering (mspf) Uppdatering (mspf)

1 1.54 10.70 2 2.32 22.73 3 3.08 35.09 4 3.90 47.62 5 4.40 57.14 10 10.31 117.65 20 14.81 222.23 40 27.78 500.00 80 -

-N¨ast sista m¨atningen med 40 tr¨ad resulterade i ungef¨ar 36 fps f¨or renderingen och 2 fps uppdatering. Man kan snabbt dra slutsatsen att ¨okningen ¨ar linj¨ar f¨or b˚ade rendering och uppdatering, men d¨ar uppdateringen snabbt blir oacceptabel d˚a varje tr¨ad tar ca 10 ms att uppdatera. Med 80 tr¨ad tog minnet slut.

Genom att h˚alla sig till n˚agra f˚atal unika tr¨ad och ist¨allet rendera tr¨aden flera ggr som kloner s˚a kan ett mycket b¨attre resultat f˚as utan att f¨or den sakens skull l¨agga m¨arke till att tr¨aden ¨ar klonade.

F¨or att g¨ora en bed¨omning om antalet renderade tr¨ad p˚averkar uppdateringstr˚aden s˚a gjordes f¨oljande test med 1 unikt tr¨ad och som sedan klonades flera ggr f¨or rendering.

Antal kloner Rendering (mspf) Uppdatering (mspf)

1 1.53 10.58 2 2.92 10.81 3 4.55 10.58 4 5.88 10.58 5 7.46 10.31 10 14.39 10.47 20 28.57 10.25 40 54.05 11.11 80 105.26 10.93

H¨ar kan man konstatera att uppdateringen p˚averkas knappt alls medan renderingen blev l˚angsammare ¨an f¨orra testet. Detta kan bero p˚a att m¨angden synkroniseringar till

(29)

en och samma resurs ¨okade.

M¨atdata f¨or en enkeltr˚adad l¨osning blir knappt l¨ont d˚a andelen unika tr¨ad ¨okar. Vid en villkorlig kompilering d¨ar all ber¨akning och rendering skedde i 1 tr˚ad och med 40 unika tr¨ad hamnade renderingen p˚a 250 mspf (∼ 4 fps) och den multitr˚adade renderade samma scen p˚a 51 mspf (∼ 19 fps).

4.4. Utseende

Av m˚alen st¨allda s˚a uppfyller applikationen delvis kravet p˚a utseende med per-pixelbelysning och bumpmapping. Skuggor, terr¨ang och annan form av vegetation utel¨amnades p˚a grund av tidsbrist. Geometriskt sett ¨ar procedurell generering v¨aldigt tacksamt f¨or pro-grammerare som saknar grafisk talang. Tyv¨arr undkommer man aldrig texturer vilket g¨or att utseendet p˚averkas dramatiskt och tr¨aden blir inte s¨arskilt imponerande.

(30)

5. Analys och diskussion

5.1. Tr¨

adgenerering

Algoritmen f¨or tr¨adgenereringen funkar bra och det g˚ar f˚ar till en m¨angd olika tr¨adarter. M˚anga saker beskrivna i originalrapporten hann tyv¨arr inte implementeras. L¨oven ¨ar t¨ankt att vara riktig geometri med en m¨angd polygoner men implementerades h¨ar enbart som en enkel quad med en textur. Detta g¨or att orienteringen av l¨oven ser ganska konstigt ut d˚a det ¨ar sv˚art att f˚a placeringen p˚a l¨oven att hamna s˚a det ser ut som l¨ovet verkligen v¨axer p˚a kvisten.

Min implementation har vissa artefakter som jag inte blivit klok p˚a. D¨aribland att l¨angden p˚a grenarna verkar inte st¨amma riktigt vilket p˚averkar m¨angden l¨ov och fortsatta niv˚aer av grenar. ¨Aven slumptermen som adderas verkar st¨alla till problem f¨or vissa random seeds. Om detta ¨ar en artefakt som jag orsakat eller originalrapporten f¨orblir oklart.

M¨angden tr¨adarter som jag testat har varit ganska begr¨ansat d˚a enbart ett par stycken f¨ardiga tr¨adarter fanns tillg¨angliga. Fler implementationer finns med en m¨angd olika arter men konvertering mellan mitt filformat och deras hade d˚a varit n¨odv¨andig vilket p˚a grund av tidsbrist aldrig blev av.

5.2. Multitr˚

adning

N¨ar geometrin skapas s˚a kopieras detta till en separat buffert som sedan kopieras in till en VBO. Detta skulle kunna f¨orb¨attras genom att mappa minnet mellan VBO:n och klientapplikationen och kopiera rakt in i VBO:n fr˚an uppdateringstr˚aden. Detta ¨ar d¨aremot sv˚art att genomf¨ora d˚a en renderingskontext g˚ar ej vanligen att dela mellan olika tr˚adar. Idealt hade man velat att uppdateringstr˚aden mappar bufferten till grafikkortet utan mellanh¨ander.

Det finns m¨ojlighet att dela renderingskontext mellan olika tr˚adar men detta ¨ar inget som finns tillg¨angligt via SDL. F¨or att byta renderingskontext skulle man f˚a anropa glXMakeCurrent och liknande plattformsberoende funktioner. En annan m¨ojlighet vore f¨or renderingstr˚aden att mappa bufferten och l˚ata uppdateringstr˚aden streama direkt till VBO:n. Detta ¨ar t¨ankbart men problem som hur renderingstr˚aden vet n¨ar den ska kunna unmappa bufferten igen dyker upp. Under tiden bufferten ¨ar mappad s˚a kan inte grafikkortet utf¨ora n˚agot arbete.

Den enklaste l¨osningen valdes ist¨allet d¨ar uppdateringstr˚aden allokerar en buffert och kopierar ¨over geometrin. Pekaren till denna buffert delas med renderingstr˚aden som streamar ¨over geometrin till grafikkortet. N¨asta uppdatering allokeras en ny buffert och

(31)

den gamla avallokeras. Detta skulle kunna optimeras rej¨alt eftersom antalet vertexar ¨ar konstant fr˚an frame till frame, men d˚a har man inte l¨angre m¨ojlighet att kunna variera m¨angden vertexar p˚a ett flexibelt vis i syfte att snabba upp uppdateringen/renderingen. I 2.2.4 n¨amns interlock-operationer men detta ¨ar inget jag kan se n˚agon anv¨andning av d˚a tekniken anv¨ands fr¨amst till cirkul¨ara bufferts d¨ar en tr˚ad fyller i ena ¨anden och en annan t¨ommer i andra. Kanske f¨or att visualisera en “animering” av ett tr¨ads uppbyggnad kunde vara relevant i detta avseende. Samtidigt n¨amns i 3.2 att detta ej finns tillg¨angligt i userspace linux vilket varf¨or jag ej kunnat anv¨anda mig av detta.

5.3. Vindp˚

averkan

Enligt [5] kr¨avs att tr¨aden genereras om med samma random seed f¨or att uppdatera positionerna efter en viss tid med vind aktiverat. Om man analyserar lite vad som egentligen ¨andras s˚a r¨acker det att uppdatera orienteringen i en viss ordning. Under genereringspasset sparar jag undan de stammar och l¨ov som ett visst segment har skapat och kan p˚a s˚a vis rekursivt traversera igenom hela tr¨adet i samma ordning som de skapades. Ingen omber¨akning av radier, l¨angder eller annat ¨ar n¨odv¨andigt. Detta b¨or vara en relativt bra optimering men inga profileringar har utf¨orts f¨or att bevisa detta.

5.4. Level of Detail

Ett metod vore att helt enkelt generera om tr¨aden med ett f¨arre antal polygoner beroende p˚a avst˚and fr˚an objektet till betraktaren. Detta motsvarar tekniker som Discrete LOD (DLOD) men skillnaden ¨ar att man genererar LOD-niv˚aer dynamiskt ist¨allet f¨or att spara det som f¨orgjorda modeller. Tr¨aden genereras med samma random seed.

Detta diskuteras ¨aven i [5] med argument som att genereringen tar mellan 1 - 10 sekunder f¨or ett enstaka tr¨ad och s˚aledes oacceptabelt. Ist¨allet valde de att beh˚alla den geometriska datan intakt och omtolka datan p˚a olika vis. Avst˚and som varierar fr˚an medel till l˚anga kan man utan problem rendera tr¨aden som enbart en tr¨adstam och l¨ov utan att det blir n˚agon st¨orre m¨arkbar skillnad. Denna l¨osning har ett problem; de f¨oruts¨atter att renderingen g¨ors med immediate mode rendering d¨ar man l¨att kan hoppa ¨

over enstaka niv˚aer av grenar. De n¨amner heller ingenting om krav p˚a realtid.

Min metod med att generera om tr¨aden visar sig inte vara n˚agot problem med da-gens processorer. En generering av ett h¨oguppl¨ost tr¨ad g¨ors bara p˚a n˚agon br˚akdel av en sekund och genom att ¨aven kombinera detta med multitr˚adning kan man undvika ryckigheter under tiden tr¨aden genereras. Tyv¨arr l¨ampar sig inte metoden n¨ar kloner av tr¨ad ¨ar inblandade d˚a alla kloner anv¨ander sig av samma geometri.

5.5. Instancing

I skrivande stund har enbart Direct3D 9 st¨od f¨or s˚a kallad Hardware Instancing d¨ar alla instanser kan ritas via ett enda ritanrop och sedan anger man hur var och en ska transformeras individuellt. Detta s¨ags dock inte vara n˚agot som beh¨ovs med OpenGL

(32)

d˚a ett ritanrop anses var mindre kostsamt ¨an motsvarande i Direct3D. En extension f¨or OpenGL finns tydligen utvecklad av NVIDIA som heter NVX instanced arrays1och som m¨ojlig¨or ¨akta hardware instancing. Vill man uppn˚a ett liknande resultat i OpenGL utan att f¨orlita sig p˚a h˚ardvaruspecifika extensions s˚a finns en teknik kallad pseudo-instancing

2. Ingen av metoderna har testats i arbetet och huruvida dessa p˚averkar prestandan ¨ar

ok¨ant.

1http://developer.nvidia.com/object/opengl-nvidia-extensions-gdc-2006.html 2

http://developer.download.nvidia.com/SDK/9.5/Samples/DEMOS/OpenGL/src/ glsl pseudo instancing/docs/glsl pseudo instancing.pdf

(33)

6. Slutsatser

Detta examensarbete har resulterat i en applikation som uppfyllde de uppsatta kraven med att generera tr¨ad helt procedurellt vid uppstart och rendera tr¨aden med p˚averkan av vind i realtid med hj¨alp av multitr˚adade tekniker.

Metoden med att uppdatera geometri i en skild tr˚ad och rendering i en annan f¨orbeh˚aller sig ganska naturlig d˚a det kan ske n¨astintill helt parallellt utan n˚agra st¨orre synkronise-ringsproblem. N˚agra av f¨ordelarna med att implementera det hela p˚a CPU:n kan vara att man avlastar grafikkortet och kan p˚a s˚a vis ¨agna stora delar p˚a grafiska effekter. D¨aremot ¨ar det inte sagt att detta ¨ar en optimal metod och ett b¨attre resultat hade f¨ormodligen kunnat ˚astadkommas genom att ber¨akna all uppdatering av geometri p˚a grafikkortet.

Av de fr˚agor st¨allda om en multitr˚adad l¨osning presterar b¨attre en den enkeltr˚adade varianten s˚a ¨ar svaret definitivt ja, men f¨or att uppn˚a bra prestanda i spel s˚a ¨ar en GPU-baserad l¨osning att f¨oredra.

(34)

Litteraturf¨

orteckning

[1] Akenine-M¨oller, Tomas, Eric Haines. “Real-Time Rendering”, Second Edition. Wel-lesley, MA, USA. A K Peters, Ltd., 2002.

[2] Deitel, Harvey M., Paul J. Deitel, David R. Choffnes. “Operating Systems”, Third Edition. Prentice Hall, 2003.

[3] Floyd, R.W., L. Steinberg, “An adaptive algorithm for spatial grey scale”. Procee-dings of the Society of Information Display 17, 75-77, 1976.

[4] Shreiner, Dave, Mason Woo, Jackie Neider, Tom Davis. “OpenGL(R) Program-ming Guide: The Official Guide to Learning OpenGL(R), Version 2”, Fifth Edition. Addison-Wesley Professional, 2005.

[5] Weber, Jason, Joseph Penn. “Creation and Rendering of Realistic Trees”, Computer Graphics (Proc. Siggraph 95), ACM Press, New York, 1995.

(35)

A. Sk¨

armdumpar

(36)
(37)

B. Exempelkod

B.1. Uppdatering

Listing B.1: tree.cpp

void Tree : : update ( ) {

params−>r e s e t s e e d ( ) ; t i m e = t i m e r −>s e c o n d s ( ) ;

/∗ u p d a t e t r u n k stem and a l l i t s sub−s t e m s r e c u r s i v e l y ∗/

T r a n s f o r m a t i o n t r f = s e g m e n t b e y o n d f l a r i n g −>t r a n s f o r m a t i o n ( ) ; t r u n k −>update ( t r f ) ; /∗ u p d a t e c o m p l e t e , c o n s t r u c t mesh ∗/ f i l l b u f f e r s ( ) ; } void Tree : : f i l l b u f f e r s ( ) { Mesh b r a n c h e s ; { Mesh & tmp = b r a n c h e s ; /∗ p o i n t e r o f f s e t s ∗/ tmp . n p o i n t s = t o t a l p o i n t s ; tmp . v s i z e = s i z e o f ( math : : V e c 3 t ) ∗tmp . n p o i n t s ; tmp . t s i z e = s i z e o f ( math : : V e c 2 t ) ∗tmp . n p o i n t s ; tmp . i s i z e = s i z e o f ( unsigned i n t ) ∗tmp . n p o i n t s ∗ 6 ; tmp . n s i z e = s i z e o f ( math : : V e c 3 t ) ∗tmp . n p o i n t s ; /∗ a l l o c a t e b u f f e r ∗/ tmp . b u f f e r = ( unsigned char ∗ ) m a l l o c ( tmp . v s i z e + tmp . t s i z e + tmp . n s i z e + tmp . i s i z e ) ; tmp . v b u f = ( math : : V e c 3 t ∗ ) tmp . b u f f e r ; tmp . t b u f = ( math : : V e c 2 t ∗ ) ( tmp . b u f f e r+tmp . v s i z e ) ;

tmp . nbuf = ( math : : V e c 3 t ∗ ) ( tmp . b u f f e r+tmp . v s i z e+tmp . t s i z e ) ; tmp . i b u f = ( unsigned i n t ∗ ) ( tmp . b u f f e r+tmp . v s i z e+ tmp . t s i z e+tmp . n s i z e ) ; math : : V e c 3 t ∗ v b u f = tmp . v b u f ; unsigned i n t ∗ i b u f = tmp . i b u f ; unsigned i n t o f f s e t = 0 ; math : : V e c 2 t ∗ t b u f = tmp . t b u f ;

(38)

math : : V e c 3 t ∗ nbuf = tmp . nbuf ;

/∗ f i l l b u f f e r s r e c u r s i v e l y ∗/

t r u n k −> f i l l b r a n c h b u f f e r s (&vbuf , &i b u f , &o f f s e t , &t b u f , &nbuf ) ; a s s e r t ( v b u f <= tmp . v b u f+tmp . v s i z e ) ; a s s e r t ( t b u f <= tmp . t b u f+tmp . t s i z e ) ; a s s e r t ( nbuf <= tmp . nbuf+tmp . n s i z e ) ; a s s e r t ( i b u f <= tmp . i b u f+tmp . i s i z e ) ; } Mesh l e a v e s ; { Mesh & tmp = l e a v e s ; /∗ p o i n t e r o f f s e t s ∗/ tmp . n p o i n t s = t o t a l l e a v e s ; tmp . v s i z e = s i z e o f ( math : : V e c 3 t ) ∗4∗ t o t a l l e a v e s ; tmp . t s i z e = s i z e o f ( math : : V e c 2 t ) ∗4∗ t o t a l l e a v e s ; tmp . n s i z e = s i z e o f ( math : : V e c 3 t ) ∗4∗ t o t a l l e a v e s ; /∗ a l l o c a t e b u f f e r ∗/ tmp . b u f f e r = ( unsigned char ∗ ) m a l l o c ( tmp . v s i z e + tmp . t s i z e + tmp . n s i z e ) ; tmp . v b u f = ( math : : V e c 3 t ∗ ) tmp . b u f f e r ; tmp . t b u f = ( math : : V e c 2 t ∗ ) ( tmp . b u f f e r + tmp . v s i z e ) ; tmp . nbuf = ( math : : V e c 3 t ∗ ) ( tmp . b u f f e r + tmp . v s i z e + tmp . t s i z e ) ; math : : V e c 3 t ∗ v b u f = tmp . v b u f ; unsigned i n t ∗ i b u f = tmp . i b u f ; unsigned i n t o f f s e t = 0 ; math : : V e c 2 t ∗ t b u f = tmp . t b u f ; math : : V e c 3 t ∗ nbuf = tmp . nbuf ;

/∗ f i l l b u f f e r s r e c u r s i v e l y ∗/

t r u n k −> f i l l l e a f b u f f e r s (&vbuf , &i b u f , &o f f s e t , &t b u f , &nbuf ) ; a s s e r t ( v b u f <= tmp . v b u f+tmp . v s i z e ) ; a s s e r t ( t b u f <= tmp . t b u f+tmp . t s i z e ) ; a s s e r t ( nbuf <= tmp . nbuf+tmp . n s i z e ) ; a s s e r t ( i b u f <= tmp . i b u f+tmp . i s i z e ) ; } /∗ copy t o s h a r e d memory ( c r i t i c a l s e c t i o n ) ∗/ { /∗ b r a n c h e s ∗/ { b o o s t : : t r y m u t e x : : s c o p e d l o c k L1 ( b r a n c h e s . mutex ) ; f r e e ( b r a n c h e s . b u f f e r ) ; b r a n c h e s = b r a n c h e s ; }

(39)

/∗ l e a v e s ∗/ { b o o s t : : t r y m u t e x : : s c o p e d l o c k L2 ( l e a v e s . mutex ) ; f r e e ( l e a v e s . b u f f e r ) ; l e a v e s = l e a v e s ; } } } Listing B.2: stem.cpp

void Stem : : update ( const T r a n s f o r m a t i o n & a t r f ) { const i n t n c u r v e r e s = p a r −>nCurveRes [ l e v e l ] ; t r f = a t r f ; S e g m e n t p t r l a s t s e g m e n t = u p d a t e l a s t s e g m e n t ; S e g m e n t i t e r a t o r n e x t s e g m e n t = u p d a t e n e x t s e g m e n t ; l a s t s e g m e n t −>make ( t r f , l a s t s e g m e n t −> r a d i u s ) ; f o r ( i n t i =1; i <n c u r v e r e s ; ++i ) { T r a n s f o r m a t i o n t r f = s t e m d i r e c t i o n ( l a s t s e g m e n t −> t r f , i ) ; S e g m e n t p t r ps = ∗ n e x t s e g m e n t ++;

wind sway (& t r f , ps−> r a d i u s , Re al ( i ) / Re al ( n c u r v e r e s −1) , t r e e −> t i m e ) ; ps−>update ( t r f , ps−> r a d i u s ) ; typedef s t d : : v e c t o r <Stem ∗ > : : i t e r a t o r S t e m i t e r ; f o r ( S t e m i t e r j=ps−> s p a w n e d s u b s t e m s . b e g i n ( ) ; j != ps−> s p a w n e d s u b s t e m s . end ( ) ; ++j ) { Stem ∗ stem = ∗ j ; V e c 3 t s u b s t e m o r i g i n = t r f . g l o b a l o r i g i n + t r f . o r i e n t a t i o n ∗ V e c 3 t ( 0 , stem−> o f f s e t , 0 ) ; stem−>update ( T r a n s f o r m a t i o n ( t r f . o r i e n t a t i o n ∗ stem−> q u a t , math : : VEC3 ZERO, s u b s t e m o r i g i n ) ) ;

} typedef s t d : : v e c t o r <L e a f ∗ > : : i t e r a t o r L e a f i t e r ; f o r ( L e a f i t e r k=ps−> s p a w n e d l e a v e s . b e g i n ( ) ; k!= ps−> s p a w n e d l e a v e s . end ( ) ; ++k ) { L e a f ∗ l e a f = ∗k ; V e c 3 t l e a f o r i g i n = t r f . g l o b a l o r i g i n + t r f . o r i e n t a t i o n ∗ V e c 3 t ( 0 , l e a f −> o f f s e t , 0 ) ;

(40)

l e a f −>make ( T r a n s f o r m a t i o n ( l e a f −> q u a t ∗ t r f . o r i e n t a t i o n , math : : VEC3 ZERO, l e a f o r i g i n ) ) ;

}

l a s t s e g m e n t = ps ; }

}

B.2. Rendering

Listing B.3: mesh technique.cpp

void M e s h t e c h n i q u e : : d r a w b r a n c h e s ( const t r e e : : Mesh & b r a n c h e s ) { { l o c k t y p e L ( b r a n c h e s . mutex ) ; i f ( L . l o c k e d ( ) ) s t r e a m c o p y t o v b o ( b r a n c h e s ) ; } g l B i n d B u f f e r (GL ARRAY BUFFER, b r a n c h e s v b o [ 0 ] ) ; g l V e r t e x P o i n t e r ( 3 , GL FLOAT, 0 , 0 ) ; g l E n a b l e C l i e n t S t a t e (GL VERTEX ARRAY) ; g l B i n d B u f f e r (GL ARRAY BUFFER, b r a n c h e s v b o [ 0 ] ) ; g l T e x C o o r d P o i n t e r ( 2 , GL FLOAT, 0 , ( char ∗ ) b r a n c h e s p a s s . v s i z e ) ; g l E n a b l e C l i e n t S t a t e (GL TEXTURE COORD ARRAY) ;

g l B i n d B u f f e r (GL ARRAY BUFFER, b r a n c h e s v b o [ 0 ] ) ;

g l N o r m a l P o i n t e r (GL FLOAT, 0 , ( char ∗ ) b r a n c h e s p a s s . v s i z e+ b r a n c h e s p a s s . t s i z e ) ;

g l E n a b l e C l i e n t S t a t e (GL NORMAL ARRAY) ;

g l B i n d B u f f e r (GL ELEMENT ARRAY BUFFER, b r a n c h e s v b o [ 1 ] ) ; g l E n a b l e C l i e n t S t a t e (GL INDEX ARRAY) ;

glDrawElements (GL TRIANGLES, b r a n c h e s p a s s . n p o i n t s ∗ 6 , GL UNSIGNED INT , 0 ) ;

g l D i s a b l e C l i e n t S t a t e (GL VERTEX ARRAY) ; g l D i s a b l e C l i e n t S t a t e (GL INDEX ARRAY) ;

g l D i s a b l e C l i e n t S t a t e (GL TEXTURE COORD ARRAY) ; g l D i s a b l e C l i e n t S t a t e (GL NORMAL ARRAY) ;

}

void M e s h t e c h n i q u e : : s t r e a m c o p y t o v b o ( const t r e e : : Mesh & b r a n c h e s ) {

(41)

b r a n c h e s p a s s . n p o i n t s = b r a n c h e s . n p o i n t s ; b r a n c h e s p a s s . v s i z e = b r a n c h e s . v s i z e ; b r a n c h e s p a s s . t s i z e = b r a n c h e s . t s i z e ;

g l B i n d B u f f e r (GL ARRAY BUFFER, b r a n c h e s v b o [ 0 ] ) ;

g l B u f f e r D a t a (GL ARRAY BUFFER, b r a n c h e s . v s i z e + b r a n c h e s . t s i z e + b r a n c h e s . n s i z e , NULL, GL STREAM DRAW) ;

GLvoid ∗ v b u f = g l M a p B u f f e r (GL ARRAY BUFFER, GL WRITE ONLY) ; g l B i n d B u f f e r (GL ELEMENT ARRAY BUFFER, b r a n c h e s v b o [ 1 ] ) ; g l B u f f e r D a t a (GL ELEMENT ARRAY BUFFER, b r a n c h e s . i s i z e , NULL,

GL STREAM DRAW) ;

GLvoid ∗ i b u f = g l M a p B u f f e r (GL ELEMENT ARRAY BUFFER, GL WRITE ONLY) ;

/∗ s a n i t y c h e c k ( v b u f != NULL && i b u f != NULL) ∗/

memcpy ( vbuf , b r a n c h e s . b u f f e r , b r a n c h e s . v s i z e + b r a n c h e s . t s i z e + b r a n c h e s . n s i z e ) ;

memcpy ( i b u f , b r a n c h e s . i b u f , b r a n c h e s . i s i z e ) ; g l B i n d B u f f e r (GL ARRAY BUFFER, b r a n c h e s v b o [ 0 ] ) ; GLboolean v s t a t u s = glUnmapBuffer (GL ARRAY BUFFER) ; g l B i n d B u f f e r (GL ELEMENT ARRAY BUFFER, b r a n c h e s v b o [ 1 ] ) ; GLboolean i s t a t u s = glUnmapBuffer (GL ELEMENT ARRAY BUFFER) ;

/∗ s a n i t y c h e c k ( v s t a t u s == GL TRUE && i s t a t u s == GL TRUE) ∗/

Figure

Figur 2.1.: Tre olika tr¨ adarter producerade av algoritmen. Bilder fr˚ an “Creation and Rendering of Realistic Trees” av Jason Weber och Joseph Penn
Figur 2.2.: Olika typer av f¨ orgreningar: (I) Splitting (II) Branching 2.1.3.1. Klonade stammar (splitting)
Tabell 2.1.: Definition av ShapeRatio()
Figur 2.3.: Exponentiell expansion vid tr¨ adets nedre region. 2.1.4.3. Sv¨ angande radie (lobing)
+5

References

Related documents

Exempelvis kan BCI2000 anv¨ andas f¨ or att l¨ asa in m¨ atv¨ arden, bearbeta dessa och skicka vidare dem till ett program som anv¨ ands f¨ or att styra en robotarm eller

stud€nt Ý,€cně a odboÍíě odPovíd2l Íla dots,T iednotliÚch členů koÍrrise (viŽ flíže). dn

podpis

- Om kursnämnd ej kan etableras, skall sektionens studienämndsordförande (SNO) kontaktas genast (se www.ths.kth.se/utbildning/utbildningsradet.html för kontaktuppgifter).

Till exempel fick jag inte med n˚ agot Ljus- och Optikland i f¨ orsta f¨ ors¨ oket, och pilen mot Kosmologi, som ligger utanf¨ or den h¨ ar kartan, borde peka mer upp˚ at,

L¨ angden (mm) av bultarna varierar p˚ a grund av ett slumpm¨ assigt fel som antas vara normalf¨ ordelat kring 0 med standardavvikelsen σ = 0.5 vilket motsvarar precisionen f¨

června 201l. podpis

Studier av eth i bananflugan kan d¨ arf¨ or leda till ¨ okad f¨ orst˚ aelse av ghrelin och ¨ ar ett potentiellt f¨ orsta steg i jakten p˚ a nya l¨ akemedel mot ¨ overvikt och