Malskapada v Henrik UTVÄRDERING AV ALGORITMER FÖR BRED KOLLISIONSDETEKTERING MED HJÄLP AV BOIDS ALGORITM EVALUATION OF ALGORITHMS USED FOR BROAD PHASE COLLISION DETECTION USING BOIDS ALGORITHM Examensarbete inom huvudområdet Informationsteknologi Grundnivå 3 högskolepoäng Vårtermin 218 Jonathan Nilsson Handledare: Mikael Thieme Examinator: Mikael Johannesson
Sammanfattning Denna studie gick ut på att jämföra tre olika algoritmer som har använts för bred kollisionsdetektering, dessa algoritmer var Kd-tree, Octree och Sweep and prune. Kd-tree och Octree är spatiala datastrukturer, d.v.s. att de hanterar objekt inom specifika volymer. Sweep and prune använder istället listor för att ta reda på om objekt kolliderar. Fokus låg på att se hur stor skillnad algoritmernas exekveringstid hade jämfört med brute force - implementationen och jämfört med varandra. Det utfördes ett antal olika experiment på algoritmerna med ett antal olika inställningar för att kunna utvärdera hur de presterar i olika situationer. Dessa inställningar var t.ex. antalet boids, deras hastighet och hur långt de kunde se. Resultatet visade att Sweep and prune presterade bäst med en liten mängd boids medans de andra algoritmerna kom ikapp och presterade bättre när antalet objekt ökade, då Kd-tree presterade bäst överlag. Studien kan vara till hjälp med att välja vilken bred kollisionsdetekteringsalgoritm som kan tänkas implementeras för en applikation. Nyckelord: Kd-tree, Octree, Sweep and prune, kollisionsdetektering
Innehållsförteckning 1 Introduktion... 1 2 Bakgrund... 2 2.1 Boids... 4 2.2 Spatial datastruktur... 7 2.2.1 Kd-tree... 7 2.2.2 Octree... 8 2.3 Sweep and Prune... 9 3 Problemformulering... 11 3.1 Metodbeskrivning... 11 4 Implementation... 13 4.1 Unreal Engine 4... 13 4.2 Boids... 13 4.3 Kd-tree... 14 4.4 Octree... 15 4.5 Sweep and Prune... 16 5 Utvärdering... 17 5.1 Presentation av undersökning... 17 5.1.1 Skillnad mellan algoritmerna... 19 5.1.2 Kd-tree... 21 5.1.3 Octree... 27 5.1.4 Sweep and Prune... 33 5.1.5 Extra experiment... 37 5.2 Analys... 4 5.3 Slutsatser... 4 6 Avslutande diskussion... 42 6.1 Sammanfattning... 42 6.2 Diskussion... 42 6.3 Framtida arbete... 43 Referenser... 45
1 Introduktion Spel med många objekt i en scen behöver relativt snabbt kunna kolla på närliggande objekt för att komma åt deras information och om den naiva lösningen används växer beräkningstiden kvadratiskt, detta blir till slut ohanterbart för realtidsapplikationer. Därför utforskades en handfull av algoritmer som skulle kunna tänkas vara en lösning till problemet för att därmed kunna förminska beräkningstiden. På grund av att det fanns en mängd olika algoritmer som potentiellt hade kunnat vara en lösning på problemet så var det svårt att bestämma vilka av dem som passade bra för att implementera i projektet. De algoritmer som valdes för att utvärdera var de spatiala algoritmerna Kd-tree och Octree, men även Sweep and prune som använder sig av en helt annan teknik. För utvärderingen av algoritmerna implementerades Boids algoritm (Reynolds, 1987) för att den inte kan hantera en stor mängd agenter, utan behövs kompletteras med ytterligare tekniker för att få bättre prestanda. Algoritmerna implementerades i en 3D-miljö, där antalet boids reglerades för att se hur algoritmerna skulle hantera stora som små mänger av rörliga agenter. Rapporten beskriver hur de olika algoritmerna implementerades och de val som gjordes för respektive algoritm och varför de besluten gjordes. Utvecklingsmiljön diskuteras också, varför den miljön valdes och hur den användes i arbetet. 1
2 Bakgrund Spel blir större och större för varje år och med det kommer spelen potentiellt innehålla fler objekt. Med detta blir beräkningar större eftersom antalet objekt i en scen ökar, vilket kan begränsa mängden av objekt som kan existera samtidigt. Det går att se när spel från 1998 och 216 jämförs, där Figur 1 visar det äldre spelet Half Life (1998) och i Figur 2 ABZÛ (216). I dessa bilder syns det hur stor skillnad det har blivit mellan hur stora flockarna är. Figur 1 Skärmdump från spelet Half Life som i bakgrunden visar en flygande flock av ett mindre antal djur. 2
Figur 2 Skärmdump från spelet ABZÛ som visar ett stort antal fiskar som befinner sig i ett stim. Bredkollisionsdetektering används för att undersöka vilka objekt i en scen som kan potentiellt ha kolliderat och detta påverkas beroende på antalet objekt som existerar och är därmed något som kan bli ett problem om det existerar en stor mängd objekt. Om det inte implementeras en lösning växer beräkningstiden för bredkollisionsdetektering med n 2, där n är antalet objekt, det gör att till slut kommer applikationen inte kunna köras i realtid. Därför finns det algoritmer som kan användas som en lösning för att snabbare kunna få ut de objekt som ligger i närheten på ett mer effektivt och snabbt sätt. Några av dessa algoritmer kommer att förklaras mer detaljerat i detta kapitel. Algoritmerna valdes för att vissa av dem är populära alternativ inom industrin, men också för att kunna utvärdera tekniker som har stor respektive liten kontrast till varandra i hur de tacklar det angivna problemet. En av de algoritmer som kan dra nytta av dessa tekniker är Boids algoritm, av Reynolds (1987), som simulerar ett flockbeteende. Algoritmen jämför avståndet mellan medlemmar av flocken för att se om de påverkar varandra. Det naiva sättet är att kolla igenom hela flocken och jämföra avståndet till var och en av medlemmarna. Om implementeringen av Boids görs på detta sätt växer komplexiteten kvadratiskt, vilket inte är ett problem med en mindre flock, men som snabbt kan bli ett problem när flocken blir större. I Reynolds text nämner han även att det går att snabba upp beräkningarna med hjälp av en spatial datastruktur. Det finns många olika typer av spatiala datastrukturer som fungerar för olika dimensioner och därmed används för olika typer av problem. Reynolds algoritm kan användas för att visa hur olika tekniker kan öka prestandan hos applikationer. Detta är på grund av hur algoritmen i sig fungerar, att den behöver se närliggande objekt och därmed tar mycket beräkningstid. Ett exempel där Boids har använts för att visa prestandaförbättring är en video som visar nya tekniker som implementerats i 3
spelmotorn Unity, där det visas 2 fiskar som simmar omkring med stadig FPS (Ante, 216). 2.1 Boids Det var i en artikel som Reynolds (1987) först beskrev sin idé om en ny algoritm som skulle simulera ett flockbeteende med hjälp av tre stycken fundamentala regler som de agenter som kallas boids skall följa och använda som sin styrningsmekanism. Dessa regler är för att undvika kollision, för att försöka hålla samma hastighet som andra närliggande boids och för att centrera flocken så att de håller sig som en grupp. Med dessa regler uppnås en realistisk simulering av en flocks beteende. De tre reglerna går att reglera med hjälp av individuella vikter som bestämmer hur mycket ett beteende skall influera agenten. Den första regeln som är illustrerad i Figur 3 visar att agenten vill styra sig bort från närliggande boids för att undvika att kollidera med dem. Här räknas det ut en ny riktning för agenten som den vill styra till genom att lägga ihop differensen på en boids position till närliggande boids enligt Figur 4. bn B c Figur 3 Undvika kollision, försöker undvika kollision med närliggande boids inom synfältet. Vector c For each bn { If bn!= B & bn is in B s range of vision { c = c (bn.position - B.position) } } return c Figur 4 Pseudokod för att undvika kollision med andra boids. 4
Regel nummer två är att en agent vill hålla samma hastighet och riktning som närliggande boids, detta representeras i Figur 5. Detta gör att hela flocken vill hålla en gemensam hastighet och riktning, vilket leder till att hela flocken rör sig i samma generella riktning. Regeln uppnås med att ta medelvärdet av närliggande boids hastighet enligt Figur 6. c B bn Figur 5 Synka hastighet, försöker hålla samma fart och riktning som de närliggande boids inom synfältet. Vector c For each bn { If bn!= B & bn is in B s range of vision { c = c + b.velocity } } c = c / (amount of bn in range) return c Figur 6 Pseudokod för att synka hastigheten med andra boids. Den sista regeln, som visas i Figur 7, gör att agenten vill åka till det genomsnittliga centrum av närliggande boids. Detta ger flocken beteendet att hålla ihop, genom det genomsnittliga positionen av närliggande boids och det gör att individuella boids inte vill lämna gruppen, se Figur 8. 5
bn c B Figur 7 Centrering, försöker att hålla sig nära de boids inom synfältet. Vector c For each bn { If bn!= B & bn is in B s range of vision { c = c + bn.position } } c = c / (amount of bn in range) return c Figur 8 Pseudokod för centrering av boids. En annan viktig aspekt av boids är att de har ett synfält, vilket har representerats av den stora ringen i figurerna ovan och i Figur 9. Detta synfält kan implementeras på olika sätt beroende på vad som vill uppnås. Det vanligaste är att ge agenterna 36 graders syn, vilket inte är naturligt men ger fortfarande ett liv-likt simulerat beteende. Andra sorter av synfält har blivit testade av Holland och Semwal (29), där de kom fram till att det ger väldigt olika resultat beroende på hur synen begränsas och att 6 graders syn inte var tillräckligt för att producera bra beteende. 6
Vinkel Avstånd Figur 9 En boids synfält, både hur långt, radien och hur många grader omkring sig den kan se. 2.2 Spatial datastruktur Spatial data består av punkter, linjer, regioner, volymer o.s.v. som används för att dela upp ett område i mindre delar vilket skapar sektioner (Samet, 1995), det vill säga det är geometrisk data. Detta gör att spatiala datastrukturer kan hålla denna data på ett resonabelt sätt och det innebär att det möjliggör operationer för att få fram objekt som ligger nära varandra i en miljö, vilket används för kollisionsdetektering med mera. Några av de strukturer som finns är: Kdtree, Octree och R-tree. De två förstnämnda är de som är med i studien och kommer förklaras i mer detalj nedan. 2.2.1 Kd-tree Kd-tree, som står för k-dimensional tree, är en struktur som används för att lagra punkter från en k-dimensionell rymd. Kd-tree använder något som kallas hyperplan för att dela på det område som hanteras (Moore, 1991). Ett hyperplan är ett underrum som är en dimension lägre än den dimension den befinner sig i, t.ex. i 3D är ett hyperplan ett tvådimensionellt plan. Med hjälp av detta hyperplan skapas två nya områden, dock är hyperplanen begränsade till de axlar som finns tillgängliga. I dessa områden är punkterna uppdelade till sin respektive del. Denna delning av rymden med hjälp av hyperplan kan göras flera gånger på de underrum som skapas och därmed fås det en uppdelad datastruktur i korrelation till var punkterna befinner sig, som i Figur 1. 7
X=6 1 P1 P7 Y=2 Y=8 8 6 P2 P4 (P3) (P1, P2) (P4, P5, P6) (P7) 4 2 P5 P6 P3 2 4 6 8 1 Figur 1 Representation av hur ett Kd-tree i 2D-miljö kan se ut. Med den datastruktur som skapats kommer t.ex. P1 bara att behöva jämföra avståndet till P2 för att ha kontroll på om den befinner sig inom sitt synfält och det gör den genom att kontrollera vilka lövnoder som befinner sig i synfältet. Om P3 gör samma beräkning kommer istället avståndet till alla andra punkter förutom P7 att jämföras och det är för att de två lövnoderna som innehåller resterande punkter kommer att befinna sig i P3:s synfält. 2.2.2 Octree Octree är en struktur som används för 3D och som namnet antyder är Octree en trädstruktur som hanterar områden indelade i åtta noder. Meagher (1982) förklarar att strukturen är uppbyggd med kuber och hela tanken är att den består av åtta kuber, som sedan individuellt kan fördelas in i åtta kuber o.s.v., vilket visas i Figur 11. Detta är för att i teorin kan alla 3D objekt representeras av kuber, bara kuberna är tillräckligt små och det är detta som är anledningen till att just kuber används. Figur 11 Representation av hur Octree strukturen ser ut och hur den delar en cell i åtta mindre delar. 8
Octree börjar med en rotnod som representerar den totala volymen som strukturen skall hantera, denna volym börjar med att delas upp i åtta lövnoder och det är detta som är början på Octree strukturen. När en av dessa lövnoder blir ockuperad av data som trädet skall hantera, kommer den lövnoden att delas in i ytterligare åtta noder, vilket algoritmen kommer att kunna fortsätta med tills trädet når maxgränsen av sitt djup som blivit specificerad. Fördelen med denna struktur är att de övriga lövnoderna, som inte har någon data att hantera, inte behöver fördelas i mindre delar vilket minskar det minne strukturen använder. För att hitta de agenter som ligger inom en viss agents synfält undersöks cellen som den befinner sig i för att få fram de andra agenterna som också befinner sig i samma cell, där avståndet sedan kan jämföras. Om synfältet är större än den cell som agenten befinner sig i undersöks även de andra ockuperade celler som överlappar med synfältet för att kunna jämföra avståndet till resterande närliggande agenter. 2.3 Sweep and Prune I kontrast till att dela upp en volym med spatiala datastrukturer finns algoritmen Sweep and prune, förkortad som SAP, som istället är baserad på att projicera 3D-objektens volymer på x- axeln Figur 12, y-axeln och z-axeln (Cohen, Lin, Manocha & Ponamgi, 1995). För att se om två objekts volymer överlappar varandra undersöks det om projiceringarna i de lägre dimensionerna överlappas eller inte. SAP tar volymers start- och slutkoordinater och projicerar dessa på koordinataxlarna och för varje axel lagras intervallerna i en sorterad lista, det vill säga i 3D kommer det skapas tre separata listor. För att undersöka om några volymer överlappar kontrolleras listorna och om två volymer överlappar varandra på alla axlar är de förmodligen i samma område. För att uppdatera listorna när objekten får nya koordinater används en sorteringsalgoritm kallad insättningssortering, vilket fungerar bra på en lista som är nästan sorterad (Baraff, 1992). Detta är för att efter att positionerna uppdaterats, så går det att ta en av de gamla listorna och då är det stor sannolikhet att majoriteten av listan redan är i en sorterad ordning som sedan sorteringsalgoritmen kan köras på. Med Boids algoritm är det deras synfält som kommer agera som den volym som SAP hanterar, detta gör att om två synfält överlappar är de potentiellt i varandras synfält och därmed kommer de att kunna påverka varandra. 9
Y X Figur 12 Exempel på hur Sweep and prune använder sig av projicering av objekt på x-axeln. 1
3 Problemformulering När spel blir större och större kommer det finnas väldigt många objekt i en scen samtidigt. Detta i sin tur leder till att beräkningstiden för att undersöka närliggande objekt kan bli för lång och därmed försämras prestandan av ett spel, eller så skapas begränsningar för hur många objekt som kan existera samtidigt. Anledningen till att ta reda på närliggande objekt till ett annat objekt i en scen kan t.ex. vara för att utföra kollisionsdetektering, eller för att få tillgång till annan lagrad information som ett objekt kan förvara m.m. Problemet ökar om dessa objekt även kan flytta omkring i sin omgivning, då de inte längre har statiska positioner. Det är just därför som den tidigare nämnda Boids algoritm var ett perfekt exempel på det valda problemet. Arbetets syfte har gått ut på att jämföra tre olika algoritmer, som kan leda till reducerad beräkningstid för det angivna problemet, för att kunna utvärdera vilken av dessa som ger störst prestandaökning. De algoritmer som har använts i studien var Kd-tree, Octree, vilka valdes för att de båda inkluderade olika tekniker inom samma område spatiala datastrukturer och Sweep and prune, som använder en helt annan teknik för att lösa problemet. Det togs även med i åtanke att några av de algoritmer som undersöktes verkade vara de som vanligtvis används och därmed gjorde dem intressanta att få med i analysen (Abbasifard, Ghahremani & Naderi 214). Hypotesen var att SAP skulle visa sig att vara den överlägsna algoritmen jämfört med de två andra, detta var för att det skulle kunna varit svårt att få optimerade cellstorlekar på de spatiala datastrukturerna vilket skulle ha lett till längre beräkningstider (Cohen, Lin & Ponamgi, 1995). Den tänkta huvudsakliga datan som samlades in under studien för jämförelsen av algoritmerna var: hur många par av agenter som kollades igenom av enskilda boids och exekveringstiden för respektive algoritm. Detta var ett sätt att kunna undersöka hur de olika algoritmerna skilde sig från det värsta scenariot, det så kallade naiva sättet, där det sammanlagt gås igenom n 2 par, där n är hur många agenter det existerar, vilket har en snabbt växande exekveringstid. 3.1 Metodbeskrivning De algoritmer som studien innefattade blev implementerade i en 3D-miljö i samband med Boids algoritm, vilket innebar att det fanns objekt som konstant rörde sig i 3D-rummet och som interagerade med närliggande agenter. Boids användes i detta sammanhang som ett sätt att kunna utvärdera de andra algoritmerna och detta var på grund av att Boids var ett bra exempel på ett problem som kan förbättras avsevärt. Detta gjorde att det var en algoritm som passade bra när de olika algoritmerna skulle jämföras. Det låg inget fokus på hur estetiskt boids såg ut i studien och därför var inga av testerna utförda för att kunna undersöka om deras beteende upplevdes naturligt. Likt tidigare arbeten inom området var det via experiment som arbetet kunde utvärderas. Ett antal olika experiment har applicerats på de olika algoritmerna och det var för att kunna få en större bild över hur deras prestanda påverkades i olika situationer. De olika experimenten inkluderade ändringar på boid-agenternas beteende samt olika inställningar på de algoritmer som var med i de experiment som utfördes. 11
Med Boids algoritm ändrades agenternas synfält, dock bara i relation till hur lång radien var och då inte synvinkeln, som istället var statisk, vilket potentiellt kunde ändra på hur många andra agenter som behövdes gås igenom. Hastigheten varierade också, då detta ändrade hur algoritmerna behövde anpassa sig till större koordinatförändringar. Antalet agenter som existerade varierade även, i detta fall började det med en liten flock av 1 agenter och sedan trappades det upp till 25, 5, 1, 15 och till sist 2 boids. Antalet agenter var den viktigaste variabeln, eftersom det påverkade beräkningstiden mest och det var även här som den största förbättringen förmodligen skulle ske. Om algoritmerna kunde hantera 2 agenter utan några problem fortsatte flockstorleken att ökas med intervall av 1 boids upp till en gräns på 1 boids. Det sista som förändrades var den volym som agenterna fick röra sig inom, för att kunna jämföra hur algoritmerna kunde hantera stora volymer jämfört med små volymer. Ett större område skulle kunna lett till att ett flertal mindre flockar skapats inom området. Storleken på det område som agenterna ockuperade skulle kunna leda till att det var större chans att det kunde skapas mindre grupper av boids som åkte omkring. Det var ett antal olika kombinationer av vikter som bestämde hur mycket en boid följde de tre reglerna, d.v.s. undvika kollisioner, synka hastigheten och centrering. Detta var för att kunna utvärdera hur algoritmerna t.ex. hanterade situationen då agenterna var utspridda kontra väldigt nära varandra. Det som har fåtts av experimenten var en utvärdering av hur många par av boids som agenterna behöver kolla igenom för respektive algoritm, datan visualiserades med hjälp av låddiagram, från den bästa till den sämsta utkomsten, för att tydligare kunna visa utspridningen av resultaten. Utöver detta analyserades mängden boids relaterat till hur mycket FPS (frames per second) artefakten hade, vilket kopplades till mängden par som behövde gås igenom. Det gick också att se maxgränsen för hur många agenter som algoritmen kunde hantera, det vill säga om antalet agenter skulle blir för stort skulle artefakten troligen få låg FPS. Exekveringstiden av de olika algoritmerna analyserades även i form av ett medelvärde på hur lång tid det tar att utföra beräkningarna per frame. FPS:en och exekveringstiden visualiserades med olika typer av grafer. 12
4 Implementation De valda algoritmerna för arbetet implementerades i en existerande spelmotor och den spelmotorn som valdes var Unreal Engine 4 (1999-218). Under implementation av algoritmerna fanns det naturligtvis olika sätt som de kunde skapas på och därmed behövdes olika val göras. De val som gjordes och varför de gjordes kommer att diskuteras i detta kapitel, men även förklaringar till hur den slutgiltiga implementationen fungerade. 4.1 Unreal Engine 4 Som tidigare nämnts implementerades artefakten med hjälp av spelmotorn Unreal Engine 4. Huvudsakligen användes motorn för att kunna visualisera algoritmerna under implementationen för att lättare kunna bekräfta att algoritmerna fungerade som de skulle, t.ex. att Boids algoritm betedde sig som den skulle i samband med de övriga algoritmerna. Unreal möjliggjorde så att experimenten kunde utföras snabbare genom att i programmets editor direkt ha tillgängligt att ändra på de värden som skulle variera mellan experimenten istället för att ändra värden i programmets kod och slösa tid på omkompilering efter varje förändring, Figur 13. För att enklare kunna ställa in de olika värdena användes en central klass där alla variabler som skulle kunna ändras på fanns, klassen applicerade sedan dessa värden på respektive klass när programmet kördes, detta var till fördel när en stor mängd av objekt skulle hanteras. Figur 13 Skärmbild av Unreals editor, där på höger sida kan ändra på de olika variablerna, t.ex. antalet boids och vilken algoritm som skall användas. 4.2 Boids Implementationen av Boids algoritm, exempel exekvering med brute force i Figur 14, var relativ simpel, en boid kollar igenom en lista som innehåller andra boids. Avståndet jämförs mellan de boids som finns i listan och boiden som håller i listan, detta görs för att se vilka av de boids i listan som ligger inuti synfältet för den boid som undersöks och med detta kan 13
algoritmens tre regler appliceras. Dessa tre reglerna var att undvika kollision, synka hastigheten och att försöka hålla ihop som en flock med närliggande boids. En extra beräkning utfördes även för att kolla om en boid låg inom synvinkeln, eftersom att i denna implementation kunde en boid inte se hela området bakom sig och därmed skulle inga av reglerna ta med de boids som inte ligger inom synvinkeln. I applikationen gick det att snabbt ändra på synfältets räckvidd och de individuella vikterna för de tre reglerna, detta gjordes att det gick att ändra på flockens beteende på ett ögonblick. Det var en ytterligare regel som applicerades utöver detta och det var att om en boid åkte utanför en satt volym styrdes den tillbaka in i volymen, detta gjordes för att kunna hålla dem inom ett hanterbart område. Figur 14 Skärmbild från resultatet av implementation med en flock som innehåller 75 stycken boids. 4.3 Kd-tree Implementationen av Kd-tree algoritmen använde sig av medianen av positionerna (Moore, 1991) för de boids som befinner sig i en nod för att kunna bestämma var ett hyperplan skulle skapas och delade upp noden i två barnnoder som i Figur 15. Detta gjordes för att dela upp mängden boids så att cirka halva mängden gick till den ena noden och resten till den andra noden, detta gjorde att det var mindre sannolikhet att en onödigt stor mängd boids delades in i en av noderna. På detta vis behövde Boids algoritmen inte hantera stora mängder boids, vilket var det som skulle uppnås med hjälp av Kd-tree. Kd-tree algoritmen skapade ett nytt hyperplan och delade på sig då det var mer än tio boids i en nod, detta gjordes för att det inte skulle bli för många boids i en och samma nod på en gång. Men den delade på sig till ett max djup baserat på den volym som skapades mellan hyperplan, som i sin tur bestämdes av radien på en boids synfält, för om den skapade volymen skulle vara mindre än radien skulle det inte vinnas något på det. Algoritmen kontrollerade dynamiskt vilka boids som hade åkt utanför den volym den befann sig i och sorterade sedan in den i rätt nod. Samtidigt skapade den och tog bort noder från trädet efter behov, t.ex. om både höger och vänster nod var lövnoder och de var tomma behövde de inte finnas längre och togs därmed snarast bort från strukturen. 14
Figur 15 Skärmbild på ett exempel på hur Kd-tree-algoritmen delar upp volymen med hyperplan. 4.4 Octree Implementationen av Octree var av typen dynamiskt Octree, vilket betydde att noderna i trädet dynamiskt delade på sig till åtta mindre noder där alla lövnoderna inte behövde ha samma djup som i Figur 16, i kontrast till ett linjärt Octree. Ett linjärt Octree representeras av en enda array och brukar vara ett komplett träd, d.v.s. ha samma djup i varje gren. Det finns för- och nackdelar med båda implementationerna. En fördel med ett linjärt Octree är att det går att använda aritmetik för hitta grannoder, vilket är snabbare än att behöva leta igenom trädstrukturen som skapas i ett dynamiskt Octree. Ett dynamiskt Octree har stor chans att använda mycket mindre minne istället. Men implementationen använde inte denna teknik, för det intressanta var att se hur ett Octree hanterar att omstrukturera sig med ett stort antal objekt och trädets storlek blir därmed istället dynamiskt. På detta sätt blev det även mer rättvist mellan Kd-tree och Octree algoritmen, då båda behövde omstrukturera dess trädstruktur under exekveringen. Nya noder skapades bara om vissa kriterier uppfylldes. De kriterier som undersöktes var hur många boids som befann sig i en nod, när det befann sig mer än tio skapade noden åtta nya noder och sorterade in alla boids in i rätt nod. Detta var för att göra så den inte delade på sig med för få antal boids, då detta inte skulle hjälpa till med att öka prestandan. Det andra kriteriet var att algoritmen endast fick dela på sig till ett djup där en nods volym inte var mindre än en boids synfält, då det inte skulle ha varit till stor hjälp. När åtta sub-noder blev tomma fanns det ingen anledning för dem att existera längre och de togs därmed bort. Under körningen av algoritmen så undersökte algoritmen varje lövnod efter några av de boids som befann sig i algoritmens lista som hade åkt utanför dess volym och då togs de bort från listan och skickades till föräldernoden där de därefter försökte att sorteras om till rätt nod igen, om en sådan inte hittades direkt i föräldernoden skickades den ytterligare ett steg upp. Detta fortsatte tills att alla boids fann sig sorterade i trädet. 15
Figur 16 Skärmbild på ett exempel på hur ett Octree fungerar, där det inte finns några boids skapas det inga barnnoder och där det finns boids är det uppdelat enligt kriterierna. 4.5 Sweep and Prune I implementationen av SAP-algoritmen var det boidsens synfälts min- och max-värden som infogades i de tre listorna för varje axel, vilket representerades av halva synfältets radie. Detta var för att om hela synfältets radie användes skulle boids som inte befann sig inom ett synfält fortfarande tagits med som potentiellt överlappande boids, men om halva radien användes skulle det istället ge det önskade resultatet att bara boids som befinner sig inuti ett synfält ska komma med i algoritmens beräkningar för det kollades inte om ett synfält och en boid överlappade utan algoritmen kollade efter boids synfält som överlappade. När insättningssorteringen kördes för att sortera de tre listorna fick algoritmen samtidigt ut de potentiella paren som skall rapporteras som potentiella överlappningar. Varje gång en insättningssortering utfördes blev en flagga satt till sann eller falsk för varje axel, när ett par fått sina flaggor sanna i respektive axel lades detta par till som ett aktivt par och det var utifrån dessa par som Boids algoritm använde i sin exekvering. Vanligtvis finns det en extra lista som håller koll på de par som potentiellt är aktiva, d.v.s. när en flagga blir sann så läggs detta paret till i listan om den inte redan finns i den. Men detta skulle lagt till större beräkningstid till algoritmen då det finns ytterligare en lista som behövs sökas igenom då en potentiell överlappning händer. Därför implementerades det istället en triangulär matris som redan innehåller alla potentiella par, där positionen i matrisen korresponderades till de par som existerar (Buss, Tracy & Woods, 29; Cohen, Lin & Ponamgi, 1995). Denna implementation gav en snabbare algoritm i utbyte mot att den använde mer minne. Generellt sett brukar inte minne vara det största problemet i en applikation vilket gjorde detta till en acceptabel kompromiss. 16
5 Utvärdering 5.1 Presentation av undersökning Experimenten som utfördes kördes i 15 sekunder för att kunna samla ihop den data som analyserades, med ett visst undantag som kommer att tas upp senare i kapitlet, samt att varje experiment kördes fem gånger för att försöka undvika så kallade corner cases. För varje frame sparades FPS:en, exekveringstiden samt totala mängden par av boids som hanterades. Renderingen av alla boids stängdes av under experimenten för att få ut bättre data från respektive algoritm, om detta inte gjorts skulle en stor del av beräkningstiden gå åt till att rendera objekten och därmed minska antalet boids som en algoritm potentiellt skulle kunna klara av. Alla experiment utfördes på en och samma dator som använde sig av en Intel(R) Core(TM) i7-67k CPU @ 4.GHz, d.v.s. de resultat som visas skulle kunna variera om annan hårdvara användes. Resultaten illustreras med hjälp av grafer och låddiagram beroende på vad som anses frambära resultatet tydligast. Figur 17 visar ett exempel på hur ett låddiagram kommer att kunna se ut under kapitlets gång, där lådan representerar 5% av resultatet. Linjerna som går ut ur lådan är de max 25% respektive lägsta 25% av resultatet. Utöver detta finns det kryss och ringar, kryssen representerar värden som ligger mer än 1.5% ifrån lådan, medan ringarna istället är värden som ligger mer än 3% ifrån lådan. Under experimenten var applikationens max FPS satt till 12, vilket gör att vissa av graferna ibland inte visar någon skillnad även om antalet boids ökar. 125 12 115 11 15 1 95 9 85 8 75 7 65 6 55 5 45 4 35 3 25 2 15 1 5 Exempel Figur 17 Ett exempel på hur låddiagramen kommer att se ut i kapitlet. 17
Antalet par boids som undersöks I relation till graferna kommer det förekomma BoidS1 och BoidS2, dessa representerar de två olika inställningarna som applicerades på boidsen under de olika experimenten. BoidS1 ger ett beteende där boidsen för det mesta inte påverkar varandra så mycket och därmed åker omkring i stort sätt slumpmässigt inom den satta volymen. BoidS2 gör så att de istället vill hålla ihop som en flock, d.v.s. de försöker åka i samma riktning samtidigt som att de vill hålla ihop som en grupp. Som tidigare nämnt har n 2, där n är antalet boids, par av boids gåtts igenom när den naiva lösningen implementerades, d.v.s. 1 boids ger 1 par av boids totalt varje frame o.s.v. och detta visas i Figur 18. Det var den implementationen som användes som standardjämförelse, detta var för att det sågs som den sämsta utkomsten som skulle kunna fås. 12 Genomsnittlig Antal Boids som undersöks per Frame 1 8 6 4 2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Figur 18 Antalet boids som undersöks varje frame beroende på antalet boids som existerar i applikationen. När antalet par av boids som var med i beräkningen växte, så ökade antalet boids som undersöktes varje frame med n 2 vilket gjorde det naturligt för exekveringstiden att också öka med samma hastighet. Det är det faktum som gör att Figur 18 och Figur 19 visar snarlika kurvor. 18
FPS Millisekunder 275 Genomsnittlig Exekveringstid 25 225 2 175 15 125 1 75 5 25 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Figur 19 Den genomsnittliga exekveringstiden av algoritmen beroende på antalet boids som existerar i applikationen. När beräkningstiden ökade sjönk istället applikationens FPS och det går att se i Figur 2 där det snabbt sjunker till en icke acceptabel mängd på 11 FPS vid 2 boids. 14 12 1 Genomsnittlig FPS 8 6 4 2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Figur 2 Den genomsnittliga FPS:en som applikationen har beroende på antalet boids som existerar i applikationen. 5.1.1 Skillnad mellan algoritmerna Figur 21 och Figur 22 visar den enorma skillnaden som det var mellan brute force - implementationen och de övriga algoritmerna när det gällde exekveringstiden. När den naiva lösningen nådde 1 millisekunder vid cirka 6 boids så befann sig inga av de andra algoritmerna i närheten, då den som är näst sämst med 8 millisekunder är SAP med BoidS1. 19
Millisekunder Resterande grafer som visar exekveringstiden mellan de olika algoritmerna med olika kombinationer av inställningar för applikationen finns i appendix A. Hastighet: 25, Synfällts radie: 2, BoundingBox Storlek: 1 275 25 Brute 225 2 175 15 125 SaP, BoidS1 1 75 SaP, BoidS2 5 Octree, BoidS1 25 Kd-Tree, BoidS2 Octree, BoidS2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Kd-Tree, BoidS1 Brute Kd-Tree, BoidS1 Kd-Tree, BoidS2 Octree, BoidS1 Octree, BoidS2 SaP, BoidS1 SaP, BoidS2 Figur 21 Den genomsnittliga exekveringstiden av algoritmerna med båda inställningarna av boids beteende visas, BoidS1 och BoidS2. 2
Millisekunder Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 4 275 25 Brute 225 2 175 15 125 SaP, BoidS1 1 SaP, BoidS2 75 Octree, BoidS1 5 Octree, BoidS2 25 Kd-Tree, BoidS1 1 25 5 1 15 2 3 4 5 6 7 8 Kd-Tree, BoidS2 9 1 Brute Kd-Tree, BoidS1 Kd-Tree, BoidS2 Octree, BoidS1 Octree, BoidS2 SaP, BoidS1 SaP, BoidS2 Figur 22 Den genomsnittliga exekveringstiden av algoritmerna med båda inställningarna av boids beteende visas, BoidS1 och BoidS2. 5.1.2 Kd-tree Figur 23 Figur 26 visar utspridningen av antalet boids som undersöks per frame beroende på antalet boids. Där går det att se att resultatet blev mycket mer utspritt om boidsen var bundna till en mindre volym kontra en större. 21
Antalet par boids som undersöks Antalet par boids som undersöks 6 som undersöks per frame (BoidS1), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 1 5 4 3 2 1 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Figur 23 Antalet boids som undersöks varje frame beroende på antalet boids som existerar i applikationen. 6 som undersöks per frame (BoidS1), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 4 5 4 3 2 1 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Figur 24 Antalet boids som undersöks varje frame beroende på antalet boids som existerar i applikationen. 22
Antalet par boids som undersöks Antalet par boids som undersöks 6 som undersöks per frame (BoidS2), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 1 5 4 3 2 1 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Figur 25 Antalet boids som undersöks varje frame beroende på antalet boids som existerar i applikationen. 6 som undersöks per frame (BoidS2), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 4 5 4 3 2 1 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Figur 26 Antalet boids som undersöks varje frame beroende på antalet boids som existerar i applikationen. 23
Millisekunder Millisekunder Graferna nedan, Figur 27 och Figur 28, visar skillnaden av den genomsnittliga exekveringstiden beroende på antalet boids och hur den presterar gentemot brute force - implementationen. Med jämförelsen med brute force går det att se att Kd-tree ser ut att vara ganska så plan från 1 till 1 boids. 275 25 225 2 175 15 125 1 75 5 25 Hastighet: 25, Synfällts radie: 2, BoundingBox Storlek: 1 Kd-Tree, BoidS2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Brute Kd-Tree, BoidS1 Kd-Tree, BoidS2 Brute Kd-Tree, BoidS1 Figur 27 Den genomsnittliga exekveringstiden 275 25 225 2 175 15 125 1 Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 4 Brute 75 5 25 Kd-Tree, BoidS1 Kd-Tree, BoidS2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Brute Kd-Tree, BoidS1 Kd-Tree, BoidS2 Figur 28 Den genomsnittliga exekveringstiden 24
FPS Graferna från Figur 29 till och med Figur 32 visar istället hur FPS:en låg till under exekveringen gentemot antalet boids. Det går även att se hur algoritmen mäter sig gentemot brute force -algoritmen. 14 12 1 FPS(BoidS1), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 1 8 6 4 2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Brute Figur 29 Utspridningen av FPS under körning 25
FPS FPS FPS(BoidS1), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 4 14 12 1 8 6 4 2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Brute Figur 3 Utspridningen av FPS under körning FPS(BoidS2), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 1 14 12 1 8 6 4 2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Brute Figur 31 Utspridningen av FPS under körning 26
FPS FPS(BoidS2), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 4 14 12 1 8 6 4 2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Brute Figur 32 Utspridningen av FPS under körning 5.1.3 Octree I graferna nedan, Figur 33 Figur 36, visas det hur många boids som undersöks när Octree algoritmen körs. Det syns tydligt att den planar ut en liten stund och har samma mönster om volymen var av storleken 4 enheter, enligt Figur34 och Figur 36. 27
Antalet par boids som undersöks Antalet par boids som undersöks 35 som undersöks per frame (BoidS1), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 1 3 25 2 15 1 5 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Figur 33 Antalet boids som undersöks varje frame beroende på antalet boids som existerar i applikationen. 35 3 25 2 15 1 5 som undersöks per frame (BoidS1), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 4 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Figur 34 Antalet boids som undersöks varje frame beroende på antalet boids som existerar i applikationen. 28
Antalet par boids som undersöks Antalet par boids som undersöks 35 som undersöks per frame (BoidS2), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 1 3 25 2 15 1 5 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Figur 35 Antalet boids som undersöks varje frame beroende på antalet boids som existerar i applikationen. 35 som undersöks per frame (BoidS2), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 4 3 25 2 15 1 5 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Figur 36 Antalet boids som undersöks varje frame beroende på antalet boids som existerar i applikationen. 29
Millisekunder Millisekunder Graferna nedan, Figur 37 och Figur 38, visar skillnaden av den genomsnittliga exekveringstiden beroende på antalet boids och hur den presterar gentemot brute force - implementationen. 275 25 225 2 175 15 125 1 75 5 25 Hastighet: 25, Synfällts radie: 2, BoundingBox Storlek: 1 Octree, BoidS1 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Brute Octree, BoidS1 Octree, BoidS2 Brute Octree, BoidS2 Figur 37 Den genomsnittliga exekveringstiden 275 25 225 2 175 15 125 Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 4 Brute 1 75 Octree, BoidS1 5 25 Octree, BoidS2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Brute Octree, BoidS1 Octree, BoidS2 Figur 38 Den genomsnittliga exekveringstiden 3
FPS FPS Från Figur 39 till Figur 42 visas Octree algoritmens FPS och det verkar vara en avvikelse i Figur 39 vid 3 boids, då låddiagrammets morrhår kommer under brute force implementationens genomsnittliga FPS. 14 12 1 FPS(BoidS1), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 1 8 6 4 2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Brute Figur 39 Utspridningen av FPS under körning FPS(BoidS1), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 4 14 12 1 8 6 4 2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Brute Figur 4 Utspridningen av FPS under körning 31
FPS FPS FPS(BoidS1), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 1 14 12 1 8 6 4 2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Brute Figur 41 Utspridningen av FPS under körning FPS(BoidS2), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 4 14 12 1 8 6 4 2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Brute Figur 42 Utspridningen av FPS under körning 32
Antalet par boids som undersöks Antalet par boids som undersöks 5.1.4 Sweep and Prune Figurerna som visar SAP s antal boids som undersöks, Figur 43 46, visar att utspridningen av låddiagrammen växer ganska mycket mellan 6 och 1 boids. 14 som undersöks per frame (BoidS1), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 1 12 1 8 6 4 2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Figur 43 Antalet boids som undersöks varje frame beroende på antalet boids som existerar i applikationen. 14 som undersöks per frame (BoidS1), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 4 12 1 8 6 4 2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Figur 44 Antalet boids som undersöks varje frame beroende på antalet boids som existerar i applikationen. 33
Antalet par boids som undersöks Antalet par boids som undersöks 14 som undersöks per frame (BoidS2), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 1 12 1 8 6 4 2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Figur 45 Antalet boids som undersöks varje frame beroende på antalet boids som existerar i applikationen. 14 som undersöks per frame (BoidS2), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 4 12 1 8 6 4 2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Figur 46 Antalet boids som undersöks varje frame beroende på antalet boids som existerar i applikationen. 34
Millisekunder Millisekunder Exekveringstid grafen Figur 47 visar att SAP-algoritmen presterar avsevärt mycket sämre i en mindre volym jämfört en större som i Figur 48, där det går att se att det finns en stor skillnad mellan resultaten. 275 25 225 2 175 15 125 1 75 5 25 Hastighet: 25, Synfällts radie: 2, BoundingBox Storlek: 1 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Brute SaP, BoidS1 SaP, BoidS2 Brute SaP, BoidS1 SaP, BoidS2 Figur 47 Den genomsnittliga exekveringstiden 275 25 225 2 175 15 125 1 75 5 Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 4 Brute 25 SaP, BoidS2 SaP, BoidS1 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Brute SaP, BoidS1 SaP, BoidS2 Figur 48 Den genomsnittliga exekveringstiden 35
FPS FPS Även graferna som visar FPS:en av applikationen när SAP körs förstärker ovannämnda grafer, för i Figur 49 och Figur 51 håller sig hela spridningsmåttet mellan ca 15 FPS och två FPS och dessa siffror befinner sig inom det spektrum då illusionen av en rörlig bild förloras. 14 12 1 FPS(BoidS1), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 1 8 6 4 2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Brute Figur 49 Utspridningen av FPS under körning FPS(BoidS1), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 4 14 12 1 8 6 4 2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Brute Figur 5 Utspridningen av FPS under körning 36
FPS FPS FPS(BoidS1), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 1 14 12 1 8 6 4 2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Brute Figur 51 Utspridningen av FPS under körning 14 FPS(BoidS2), Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 4 12 1 8 6 4 2 1 25 5 1 15 2 3 4 5 6 7 8 9 1 Brute Figur 52 Utspridningen av FPS under körning 5.1.5 Extra experiment Extra experiment kördes för att kunna analysera varför SAP presterade dåligt i de tidigare experimenten. De experiment som utfördes var då att istället för att köra applikationen i 15 sekunder så kördes den i 3 sekunder, detta påverkade hur långt alla boids hann röra sig. Sedan var det även då Boids algoritmen inte längre var bunden till någon volym, vilket betydde 37
Millisekunder att boidsen inte längre blev påverkade och behövde inte rotera runt och potentiellt åka igenom grannar, Figur 53 och Figur 54. Den sista inställningen var att volymen som de var bundna till ökades till 1 enheter Figur 55. 5 Hastighet: 25, Synfällts radie: 2, BoundingBox Storlek: 1 SAP, BoidS1 25 SAP 3, BoidS1 SAP, BoidS2 SAP 3, BoidS2 SAP No Bound, BoidS1 2 6 1 SAP, BoidS1 SAP, BoidS2 SAP 3, BoidS1 SAP 3, BoidS2 SAP No Bound, BoidS1 SAP No Bound, BoidS2 SAP No Bound, BoidS2 Figur 53 Exekverings skillnad mellan vanlig implementation av SAP, SAP som kördes i 3 sekunder och SAP som hanterade boids som inte var bundna till en volym. 38
Millisekunder Millisekunder 5 45 Hastighet: 25, Synfällts Radie: 2, BoundingBox Storlek: 4 4 35 3 25 2 15 1 5 2 6 1 SaP, BoidS1 SaP, BoidS2 SAP 3, BoidS1 SAP 3, BoidS2 SAP No Bound, BoidS1 SAP No Bound, BoidS2 SAP 3, BoidS1 SAP 3, BoidS2 SaP, BoidS1 SaP, BoidS2 SAP No Bound, BoidS2 SAP No Bound, BoidS1 Figur 54 Exekverings skillnaden mellan vanlig implementation av SAP, SAP som kördes i 3 sekunder och SAP som hanterade boids som inte var bundna till en volym. 5 45 4 35 Hastighet: 25, Synfällts radie: 2 SaP, BoidS1 3 25 2 15 1 5 SaP, BoidS2 Sap1, BoidS2 Sap1, BoidS1 2 6 7 8 9 1 SaP, BoidS1 SaP, BoidS2 Sap1, BoidS1 Sap1, BoidS2 Figur 55 Exekverings skillnaden mellan vanlig implementation av SAP och SAP som hanterar en volym på 1 enheter 39
5.2 Analys Graferna ovan visar att Kd-tree, Octree och SAP gav en förbättring överlag jämfört med brute force -implementationen, men de presterade olika bra beroende på situationen. SAP var dominant när det gällde små antal boids och gav den bästa prestandaökningen från 1 till 5 boids. Efter detta började algoritmerna ligga rätt så nära varandra upp till minst 3 boids. De variabler som påverkade algoritmernas prestanda mest var antalet boids i kombination med storleken på den volym som de var bundna till. T.ex. blev prestandan sämre när det var en liten volym i kombination med stort antal boids. Efter dessa variabler hade även hur långt en boid kunde se en ganska stor påverkan, när radien var större sänktes applikationens prestanda. Kd-tree var den algoritm som presterade bäst i majoriteten av experimenten som hanterade 3 boids och uppåt. Men i vissa fall visade det sig att Octree kunde prestera lika bra och ibland bättre än Kd-tree algoritmen. Enligt hypotesen skulle SAP inte presterade så dåligt jämfört med de två andra algoritmerna som den gjorde och det var därför som några extra experiment utfördes för att få reda på mer information om varför detta hände. Med hjälp av resultaten från dessa experiment visade det sig att algoritmen kanske inte var så dålig som den verkar vara. Bara genom att öka längden av experimenten från 15 sekunder till 3 blev det helt annorlunda resultat som visas i Figur 53. Men enligt Figur 54 fick den istället sämre resultat av längre körningstid och det beror förmodligen på att alla boids är bundna till en volym. Detta gjorde att en stor mängd boids samtidigt roterade tillbaka in i volymen och det gav en enorm mängd överlappningar, vilket ledde till att beräkningstiden för att sortera SAP s tre listor ökade enormt mycket. Med det sagt gjordes ytterligare ett test, då boidsen inte längre var bundna till en volym och då gav algoritmen bättre resultat än tidigare experiment. Till sist kördes ett experiment där de fortfarande var bundna, men volymen var av storlek 1 enheter och detta gav de bästa resultatet för SAP algoritmen. Detta visade att SAP potentiellt kan hantera stora mängder av rörliga objekt, det beror mestadels på om objekten är utspridda i en stor volym eller inte. Detta faktum kan man se i Figur 55, där den enda skillnaden på dem är hur stor volym som hanteras. Kd-tree presterade bättre än Octree i majoriteten av experimenten, förmodligen på grund av hur de olika träden byggdes upp. När Octree delar på sig kommer det alltid att skapas åtta nya noder kontra Kd-tree som bara behöver skapa två stycken noder. Detta kan ha lett till att Kdtree trädet som skapades hade färre förgreningar än Octree och därmed så tog det inte lika lång tid att söka igenom Kd-tree strukturen jämfört med Octree i de flesta fall. 5.3 Slutsatser Oavsett vilken algoritm som kan tänkas användas av de som var med i experimenten, så kommer den att ge en prestandaökning gentemot den naiva lösningen. Skillnaden på prestandan växer snabbt med antalet boids och därmed går det att vinna mer på att implementera lösningarna om det är stora antal objekt som skall hanteras. Därför kan brute force fortfarande vara en tillräckligt bra lösning om det redan är planerat hur många objekt en applikation skall kunna hantera. 4
Hypotesen som var baserad på att de spatiala algoritmerna skulle vara svårare att få rätt inställningar till(cohen, Lin & Ponamgi, 1995) och satte SAP som den algoritm som skulle prestera bäst stämde inte helt med de resultat som fåtts. SAP gav det bästa resultatet när algoritmen hanterade en mindre mängd objekt, men hade tendens att ge sämre resultat desto mer boids som existerade. Detta berodde förmodligen på grund av att algoritmen inte är så bra på att hantera en stor mängd ihopklumpade objekt och speciellt om majoriteten av objekten inte åkte i samma riktning. Det var för att det blev större chans att stora förändringar hände och därmed blev beräkningstiden av insättningssorteringen allmänt sett för lång för att producera användbara resultat. Utifrån graferna gick det att se att det inte fanns några större skillnader mellan Kd-tree och Octree algoritmerna, men det gick fortfarande att se att algoritmerna presterar annorlunda beroende på de inställningar som användes. Då Kd-tree presterade bättre i majoriteten av experimenten hade Octree en tendens att prestera lika som Kd-tree i de experiment med en boundingbox av storlek 1, men trots allt i ett enstaka fall var det Octree som hade bäst prestanda. 41
6 Avslutande diskussion 6.1 Sammanfattning Arbetet undersökte tre olika algoritmer som kan användas för bred kollisionsdetektering. Dessa algoritmer var Kd-tree, Octree och Sweep and prune, algoritmerna använde olika tekniker för att lösa Boids algoritmens dåliga prestanda. Algoritmerna jämfördes gentemot varandra och brute force -implementationen, vilket kan anses som ett värstafallscenario. För att kunna utvärdera algoritmerna implementerades Boids algoritm, vilket skall simulera en flock och för att göra det behöver en boid kunna få information ifrån närliggande andra boids. Sweep and prune verkade vara den algoritm som skulle prestera bäst, för det kan förekomma svårigheter att justera cellstorlekar med de spatiala datastukturerna beräkningstider (Cohen, Lin & Ponamgi, 1995). Men experimenten visade att detta var sant för en mindre mängd boids och falskt när antalet ökade. Experimenten utfördes på Unreal Engine 4, där experimenten kördes i ett antal sekunder då applikationen samtidigt sparade ner ett antal olika värden som sedan kunde användas för att jämföra algoritmernas prestanda. Resultatet från experimenten visade att det inte var stor skillnad på prestanda mellan Kd-tree och Octree, men majoriteten av tiden så var det Kd-tree som presterade bäst. Sweep and prune var den algoritm som presterade sämst i nästan alla experiment och det berodde på att insättningssorteringen var tvungen att köras mycket och därmed ledde det till lång exekveringstid. SAP-algoritmen, som tidigare nämnts, var istället den algoritm som fick bäst prestanda när det fanns små mängder av boids, men blev snabbt övertagen av de andra algoritmerna. 6.2 Diskussion Artefakten fungerade på ett sådant sätt att den skapade varje boid till en slumpad position inom den volym som de skulle existera, detta kan ha lett till att i vissa fall så har boidsens positioner oturligt nog kommit i sämre eller bättre lägen för en viss algoritm. Därför skulle det gå att argumentera att experimenten inte var rättvisa nog för att det fanns en viss chans, även om den var liten, att en algoritm alltid hade fördel på grund av boidsens slumpade positioner. Studiens experiment utgick ifrån att alla objekt var i konstant rörelse, d.v.s. en miljö där det förekommer ett stort antal överlappningar och därmed är det ett scenario som kanske inte förekommer lika ofta beroende på vilket typ av spel det handlar om. Därför betyder det att resultatet inte skall användas som ett verktyg för att utesluta vissa av algoritmerna bara för att de kan ha presterat sämre än de andra. En stor faktor som har kunnat påverka resultatet är att i denna studies implementation av objekten användes inte rigida kroppar, rigida kroppar är solida, det vill säga att boidsen kan ha åkt igenom varandra. Detta kan ha möjliggjort att i vissa tidpunkter kan det ha hänt att ett orimligt stor antal boids befunnit sig i varandra eller på väg att åka igenom varandra, vilket skapar en situation som är dåligt för SAP. För det gör att insättningssorteringen behöver sortera extra mycket, på grund av att det abrupt blev för stora skillnader på positionerna och därmed försämrades prestandan. Det var dock inte bara SAP som blev påverkad av detta, de andra algoritmerna påverkades också. Det kan då förekomma att det befinner sig en stor 42