Das Raytracen von Punktwolken mit Hilfe der
Transcription
Das Raytracen von Punktwolken mit Hilfe der
Das Raytracen von Punktwolken mit Hilfe der Geometrischen Algebra Raytracing Point Clouds using Geometric Algebra Master-Thesis von Crispin Deul September 2010 Fachbereich Informatik Eingebettete Systeme und ihre Anwendungen Das Raytracen von Punktwolken mit Hilfe der Geometrischen Algebra Raytracing Point Clouds using Geometric Algebra vorgelegte Master-Thesis von Crispin Deul Gutachten: Prof. Dr. Andreas Koch Tag der Einreichung: Erklärung zur Master-Thesis Hiermit versichere ich die vorliegende Master-Thesis ohne Hilfe Dritter nur mit den angegebenen Quellen und Hilfsmitteln angefertigt zu haben. Alle Stellen, die aus Quellen entnommen wurden, sind als solche kenntlich gemacht. Diese Arbeit hat in gleicher oder ähnlicher Form noch keiner Prüfungsbehörde vorgelegen. Darmstadt, den 28.09.2010 (C. Deul) 1 Danksagung An erster Stelle möchte ich Herrn Prof. Andreas Koch danken, der mir ermöglicht hat an seinem Fachgebiet diese Arbeit zu schreiben. Ich möchte auch meinem Betreuer Dr. Dietmar Hildenbrand danken, der insbesondere für Fragen die Geometrische Algebra betreffend immer ein offenes Ohr hatte. Ich danke Michael Burger für wertvolle Diskussionen zum Thema der Arbeit und das Korrekturlesen des Inhalts. Des Weiteren danke ich Florian Stock für die unterstützende Arbeit mit den Fachgebietsrechnern, so dass die Aufnahme von zusätzlichen Ergebnissen in die Arbeit möglich war. Schließlich möchte ich meinen Eltern danken. Ihre Unterstützung, Geduld und Verständnis haben es erst erlaubt diese Arbeit anzufertigen. 3 Inhaltsverzeichnis 1 Einleitung 7 2 Grundlagen 2.1 Programmieren paralleler Prozessoren 2.1.1 OpenCL . . . . . . . . . . . . . . 2.1.2 Brook+ . . . . . . . . . . . . . . . 2.1.3 Hardware . . . . . . . . . . . . . 2.2 Raytracing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 11 11 13 14 16 3 Geometrische Algebra 3.1 Grundlagen . . . . . . . . . 3.2 Produkte . . . . . . . . . . 3.3 Konformer Raum . . . . . 3.4 Objekte . . . . . . . . . . . 3.4.1 Produktnullräume 3.4.2 Objektarten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 21 21 22 24 24 24 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Oberflächenmodell 4.1 Aufbau des Oberflächenmodells . . . . . . . . . . . 4.2 Algorithmus 1 . . . . . . . . . . . . . . . . . . . . . . 4.2.1 Ablauf von Algorithmus 1 . . . . . . . . . . 4.2.2 Suche der Punktnachbarschaft . . . . . . . 4.2.3 Berechnung des Surfels . . . . . . . . . . . . 4.2.4 Berechnung der Bounding-Sphere . . . . . 4.3 Algorithmus 2 . . . . . . . . . . . . . . . . . . . . . . 4.3.1 Berechnung der Surfel-Positionen mit LOP 4.3.2 Paralleles Fitten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 27 28 29 29 30 31 33 34 39 5 Raytracing 5.1 Raytracing mit Geometrischer Algebra . . 5.1.1 Berechnung der Strahlen . . . . . . 5.1.2 Iteration über die Surfel der Szene 5.1.3 Schnitttests . . . . . . . . . . . . . . 5.1.4 Berechnung der Schnittpunkte . . 5.1.5 Überprüfung der Schnittpunkte . . 5.1.6 Interpolation . . . . . . . . . . . . . 5.1.7 Beleuchtungsrechnung . . . . . . . 5.2 Raytracing mit Linearer Algebra . . . . . . 5.2.1 Schnittberechnung . . . . . . . . . . 5.2.2 Beleuchtungsrechnung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 51 52 53 60 61 62 64 66 70 70 71 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 6 Ergebnisse 6.1 Modellerzeugung . . . . . . . . . . . . . . . . . . . 6.1.1 Bunny . . . . . . . . . . . . . . . . . . . . . 6.1.2 Variable Surfelanzahl . . . . . . . . . . . . 6.1.3 Dilo . . . . . . . . . . . . . . . . . . . . . . . 6.1.4 Performance-Vergleich . . . . . . . . . . . . 6.1.5 Auswertung . . . . . . . . . . . . . . . . . . 6.2 Vorteil durch Parallelisierung . . . . . . . . . . . . 6.2.1 Zeitmessungen mit Brook+ und OpenCL 6.2.2 Statische Analyse der Kompilate . . . . . 6.2.3 Dynamische Analyse . . . . . . . . . . . . . 6.2.4 Einfluss der OpenCL-Umgebung . . . . . 6.2.5 Folgerung . . . . . . . . . . . . . . . . . . . 6.3 Vergleich mit anderen Verfahren . . . . . . . . . . 6.4 Fazit . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.5 Ausblick . . . . . . . . . . . . . . . . . . . . . . . . . 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 73 73 75 75 75 77 78 78 79 81 83 87 88 90 91 1 Einleitung Lineare Algebra (LA) wird in vielen technischen Anwendungen verwendet. Sie ist die Algebra, die hauptsächlich in der Schule gelehrt wird. Während die Lineare Algebra zu dem weit verbreiteten Werkzeug wuchs, das sie heute ist, wurden auch andere Algebren entwickelt. Eine dieser Algebren ist die Konforme Geometrische Algebra (kurz: Geometrische Algebra oder GA). Sie hat eine lange Geschichte wobei die Wurzeln der GA schon 300 Jahre vor unserer Zeit in der synthetischen Geometrie von Euklid [10] zu finden sind. Die weitere Entwicklung führte über die (Exterior Algebra 1844) von Grassmann [23], der Clifford Algebra (1878) [13] zur heutigen Geometrischen Algebra. Verglichen mit anderen Systemen wie der Vektoralgebra ist die Geometrische Algebra eine ernstzunehmende Alternative um geometrische Probleme und Algorithmen zu beschreiben. Mit der Geometrischen Algebra kann man auf geometrisch intuitive Weise arbeiten. Alle Operationen der Geometrischen Algebra haben eine geometrische Bedeutung und sind auf jedes Objekt der Algebra uniform anzuwenden. Im Konformen Model der Algebra, welches in dieser Arbeit verwendet wird, kann man sowohl Punkte und Geraden, wie sie ebenso in der Linearen Algebra vorkommen, als auch Ebenen, Punktpaare, Kreise und Kugeln direkt als Objekte der Algebra beschreiben. Auf diese Objekte können in gleicher Weise Operationen wie Reflektionen, Rotationen oder Translationen angewendet werden. Weiterhin ist die Darstellung von Algorithmen mit GA sehr kompakt. Alle Operationen lassen sich mit den binären Operatoren „.“, „∧“ und „∗“ welche für das innere, äußere und geometrische Produkt der Algebra stehen und dem exp-Operator darstellen. Der Schnitt P P zwischen der Kugel S und der Geraden L wird über die sehr kompakte Formel P P = S ∧ L dargestellt. Das Ergebnis der Schnittoperation, das Punktpaar P P ist selbst wieder ein Objekt der Algebra. Neben der Vielzahl von Objekten und Operationen enthält die Geometrische Algebra andere mathematische Systeme wie Quaternionen oder die Tensor Algebra. Des Weiteren lassen sich andere Systeme in der GA abbilden. Auf Grund dieser Eigenschaften ist GA in vielen Bereichen wie Robotik, Computer Vision, Computergrafik und physikalischen Simulationen sehr gut anwendbar. Die große Fülle von Objekten und die Vereinigung vieler anderer Systeme erfordert leider auch einen mathematischen Unterbau der Geometrischen Algebra der verglichen mit der Linearen Algebra auf den ersten Blick einen erhöhten Rechenaufwand bedeutet. Das Konforme Modell der GA verwendet als Basiselemente 32 Einträge zählende Vektoren für die Darstellung der Objekte der Algebra. Naiv auf einem Computer implementiert müssen alle 32 Einträge eines Ergebnisvektors einer Operation berechnet werden. In einer etwas clevereren Implementierung müssen für die häufigsten Operationen immer noch im Durchschnitt zehn Einträge berechnet werden [19]. Im Vergleich zur Linearen Algebra mit ihren drei Einträge zählenden Vektoren oder vierfachen Vektoren im projektiven Raum der LA, wie er in der Computergrafik verwendet wird, ist dies ein beträchtlicher Mehraufwand. Dieser erhöhte Aufwand ist wohl mit ein Kriterium dafür, dass sich die GA in technischen Gebieten, für die sie gut geeignet ist, und in kommerziellen Anwendungen noch nicht weiter verbreitet hat. 7 Es sind jedoch einige wenige Beispiele zu nennen, die heute schon die Geometrische Algebra erfolgreich anwenden. Die Firma Geomerics Ltd. hat mit Hilfe der geometrischen Algebra das Echtzeitbeleuchtungssystem Enlighten [35] für Anwendungen der Computergrafik entwickelt. Mit Hilfe von Enlighten können Effekte der globalen Beleuchtung wie die indirekte Beleuchtung berechnet werden. Enlighten selbst wird in der weit verbreiteten Unreal Engine 3 eingesetzt. Somit können Spieler zum ersten Mal in vielen Spielen eine realistische Beleuchtung der Szenen genießen, wie sie vorher nicht möglich war. Als weiteres Beispiel der Anwendung der Geometrischen Algebra soll ein Raytracer [19] der Universität von Amsterdam genannt werden. Forscher um Leo Dorst und Daniel Fontijne haben ein Framework [17] entwickelt, das die Berechnungen der GA auf eine clevere Weise angeht und somit die Anzahl der Rechenoperationen verringert. Mit Hilfe des Frameworks wurde ein Raytracer für dreiecksbasierte Szenen entwickelt, der zeigt, dass die Dauer der Berechnungen in Geometrischer Algebra in der Größenordnung der Dauer mit Linearer Algebra liegt. Beide Beispiele zeigen deutlich, dass die Geometrische Algebra bei geschickter Implementierung und geeigneter Problemformulierung sinnvoll und performant an Stelle der Linearen Algabra eingesetzt werden kann. Die Anforderungen einer cleveren Implementierung und der geeigneten Problemformulierung beziehungsweise Anwendungsgebiete der GA sind Thema einer aktiven Forschung. Franchini et al. [20] stellen einen speziell an die Bedürfnisse der Geometrischen Algebra angepassten Co-Prozessor vor. Mit Hilfe dieses Prozessor sind Berechnungen in Geometrischer Algebra bis zu einer Größenordnung schneller als auf einer Standard-CPU. Hildenbrand et al. zeigen in ihrem Paper [27], dass die inverse Kinematic eines Roboterarms mit Hilfe von geometrischer Algebra implementiert wesentlich performanter ist als bisherige Verfahren. Weiterhin haben Hildenbrand und Hitzer eine Arbeit veröffentlicht in der sie Methoden zur Untersuchung von Punktwolken mit Hilfe von GA beschreiben [26]. Punktwolken entstehen bei der dreidimensionalen Digitalisierung von realen Objekten. Es gibt verschiedene Verfahren um die realen Objekte abzutasten. Allen Verfahren ist gemein, dass sie als Ausgabe eine Menge von Punkten im 3D Raum erzeugen, welche Abtastpunkte des realen Objektes darstellen. Die Verwendung von Punktwolken ist vielfältig. Im Digital Michelangelo Project [33] werden Punktwolken zur Digitalisierung von Kulturgütern verwendet. Es wurden hierzu verschiedene von Michelangelo erstellte Statuen mittels eines Laserscanners abgetastet. Ein anderes Anwendungsgebiet der Punktwolken ist das Reverse Engineering [52]. Von vielen Objekten gibt es keinen Bauplan oder der Bauplan ist verloren gegangen, so dass man diese Objekte nicht einfach mittels Maschinen herstellen kann. Mit Hilfe der Punktwolken kann solch ein „Bauplan“ erstellt werden. Als Anwendung sind die Erstellung von Prothesen der Medizin, hier insbesondere in der Zahntechnik, und der Automobilbau zu nennen. Aber auch zur Formkontrolle von Werkzeugen und Werkstücken ist die Verarbeitung von Punktwolken geeignet. In der Film und Spieleindustrie finden Punktwolken als Zwischenprodukt Verwendung, wenn Designmodelle, die von Künstlern in der realen Welt erstellt wurden, als Grundlage für digitale Modelle dienen sollen. Eine Herausforderung bei der Verwendung von Punktwolken ist die Darstellung der Oberfläche, die die Punkte repräsentieren. Es stellen sich hierbei die Fragen, welche Punkte der Wolke direkt durch eine Oberfläche miteinander verbunden werden sollen, wie die Oberflächen repräsentiert werden und wie die Oberfläche in einem Bild dargestellt wird. Eine der am häufigsten eingesetzten Repräsentationen sind die sogenannten Point Set Surfaces (PSS) [6]. Die Oberfläche wird hierbei über einen Moving Least Squares 8 (MLS) Ansatz definiert [32]. Die Eingabepunkte der Punktwolke werden dabei mit Hilfe von MLS auf eine MLS-Oberfläche projeziert. Während der Projektion entsteht als Nebenprodukt ein Polynom, das die MLS-Oberfläche nahe des projezierten Punktes approximiert. Zum Rendern der Oberfläche werden die Polynome verwendet um mit Hilfe von Quads, deren Eckpunkte auf die Polynome projeziert werden, die Oberfläche visuell darzustellen. Guennebaud und Gross verändern die PSSs zu Algebraic Point Set Surfaces (APSS) [24], indem sie die während der Projektion lokal in die Punktwolke eingepassten (gefitteten) Ebenen durch algebraisch gefittete Kugeln ersetzen. Der Vorteil dieses Ansatzes ist, dass er sich stabil verhält, wenn zwei Teile der Oberfläche nahe beieinander liegen oder wenn die Oberfläche durch sehr wenige Punkte repräsentiert wird (Undersampling). Zu den weiteren Ansätzen die Oberfläche einer Punktwolke zu repräsentieren gehören surfel-basierende Verfahren (Surfel kurz für Surface Element). Die Oberfläche wird hierbei nicht mehr zusammenhängend definiert, sondern mit Hilfe vieler lokaler Oberflächenrepräsentationen dargestellt. Als Beispiel für die Surfel-Oberflächen sollen die SLIMOberflächen [43] dienen (SLIM kurz für Sparse low degree implicits). Die lokalen Oberflächen werden bei diesem Verfahren über implizite Funktionen mit geringem Grad definiert. Der Einflussbereich der Oberflächenelemente wird über eine Kugel um das Oberflächenelement festgelegt. Zum Rendern reicht es in einem Raytracingverfahren die Schnitte mit den impliziten Funktionen zu berechnen. Da die Funktionen einen geringen Grad haben ist dies direkt mit den Lösungsformeln für Polynome zweiten oder dritten Grades möglich und damit sehr schnell. Ziel dieser Arbeit ist die Entwicklung eines Oberflächenmodells für Punktwolken. Es soll ein Surfel-basierter Ansatz verfolgt werden, der die geometrischen Objekte der GA verwendet. Weiterhin soll überprüft werden, ob die Geometrische Algebra einen Vorteil gegenüber der Linearen Algebra auf parallelen Plattformen erhält. Als Datengrundlage für das Oberflächenmodell sollen Punktwolken dienen. 9 2 Grundlagen Dieses Kapitel enthält einführende Erläuterungen über grundlegende Softwarekonzepte und Algorithmen, die in dieser Arbeit verwendet werden. Weiterhin enthält das Kapitel eine kurze Einführung in die verwendete Hardware, um einige Designentscheidungen verständlich zu machen. Es wird im Folgenden die OpenCL-Programmierumgebung, Grundlagen des Raytracings sowie der kd-tree als Beschleunigungsstruktur für das Raytracing eingeführt. 2.1 Programmieren paralleler Prozessoren Mit der Abkehr von immer höher taktenden Hauptprozessoren hin zu Prozessoren mit mehreren Kernen aber auch dem Aufkommen der immer flexibler programmierbaren Grafikprozessoren wuchsen die Bedürfnisse an Programmiersprachen. Selbst im Personalcomputer, der fast in jedem Haushalt steht, befindet sich oft schon diese heterogene Prozessorlandschaft aus Mehrkernprozessoren und Grafikkarten (Grafikprozessoren == GPUs). Dabei dürfen auch die von Sony entwickelten Playstation3-Systeme nicht vergessen werden, die in ihrer ersten Version auch Linux ausführen können. Der Prozessor der Playstation3 unterscheidet sich grundlegend sowohl von Mehkernprozessoren heutiger PCs als auch von GPUs. Er enthält mehrere Kerne, die aber im Gegensatz zu CPUs und GPUs nicht gleichartig sind. Einige wissenschaftliche Projekte setzen sogar die Sony Spielkonsolen ein um wissenschaftliche Berechnungen durchzuführen [36]. Bei dieser Vielzahl an verschiedenen Prozessoren entsteht der Wunsch das Rad nicht jedesmal neu erfinden zu müssen, sondern eine Software-Umgebung zu haben in der ein einmal entwickelter Algorithmus auf den verschiedenen Prozessoren mit höchstens minimalen Änderungen laufen kann. Die OpenCL-Umgebung versucht diesem Wunsch gerecht zu werden. 2.1.1 OpenCL OpenCL wurde entwickelt um parallele Algorithmen auf einfache Weise auf verschiedenster Berechnungshardware wie CPUs, GPUs, der CellBE oder DSPs (digitale Signalprozessoren) ausführen zu können. Auf diese Weise können große Performanzschübe, verglichen mit der Ausführung auf einem Einkernprozessor, erreicht werden. Weiterhin soll OpenCL die Portabilität des Algorithmus gewährleisten. Dadurch ist es möglich, den auf einer bestimmten Hardware entwickelten Algorithmus auch auf anderer paralleler Hardware auszuführen. Dies bedeutet meist einen geringen Performanzverlust, da der Algorithmus nicht exakt an die Gegebenheiten der anderen Hardware angepasst ist. Trotzdem kann der Algorithmus meist noch von der parallelen Ausführung profitieren. Am wichtigsten ist dabei natürlich, dass der Algorithmus nicht in einer neuen Sprache der spezifischen Hardware neu verfasst werden muss. Des Weiteren soll OpenCL eine effiziente Programmierung und somit eine schnelle, fehlerfreie Fertigstellung des parallelen Algorithmus er11 möglichen. Die Programmiersprache von OpenCL basiert daher auf der weithin bekannten C-Programmiersprache. Weiterhin abstrahiert OpenCL von der eigentlichen Hardware auf der der Algorithmus ausgeführt werden soll. Diese Abstraktion macht erst die einfache Portierbarkeit möglich. Die Abstraktion erfordert aber auch ein festgelegtes Programmiermodell. Dieses Modell wird im folgenden erläutert. Die OpenCL-Welt ist zweigeteilt. Es gibt einen Host, der ein mit beliebiger Sprache programmiertes Anwendungsprogramm ausführt. Der Host ist mit ein oder mehreren OpenCL-Devices verbunden. Diese OpenCL-Devices (Einheiten) führen den parallelen Algorithmus aus. Der Host bereitet in seinem Programm die Berechnung des Algorithmus auf einem spezifischen OpenCL-Device vor. Hierzu erzeugt er einen OpenCL-Context mit dem Device. Dieser Context ist sozusagen der Kommunikationsrahmen mit dem Device. In dem Kontext erzeugt der Host sogenannte Programme. Programme sind Sammlungen von Berechnungsfunktionen. Diese Berechnungsfunktionen sind die eigentlichen Codeeinheiten, die von den Devices ausgeführt werden. Die Berechnungsfunktionen werden Kernel genannt. Kernel können einem Hostprogramm vorkompiliert vorliegen oder erst zur Laufzeit kompiliert werden. Des Weiteren alloziert der Host Speicherbereiche auf dem Device und kopiert die für den Algorithmus benötigten Daten aus dem Host-Speicher in den Speicherbereich des OpenCL-Devices. Wenn das Setup für das Device beendet ist, kann die Berechnung gestartet werden. Ein paralleler Algorithmus führt seine Berechnungn mit vielen einzelnen Aufgaben (Tasks) durch. Diese Aufgaben können zum Beispiel die Berechnung einzelner Pixel eines Bildes, die Einträge einer Matrix aus einer Matrizenmultiplikation oder der Vergleich von verschiedenen DNA-Sequenzen sein. Die OpenCL-Umgebung ordnet diese Aufgaben in einer ein-, zwei- oder dreidimensionalen Domäne an und nennt sie Work-Items. Auf dieser so angeordneten globalen Domäne von Aufgaben wird ein Kernel für jedes Work-Item ausgeführt. Für einige Algorithmen ist es erforderlich oder performanter, wenn einzelne Work-Items Daten untereinander austauschen. Die meisten Devices haben nicht die Ressourcen diesen Austausch in der gesamten globalen Domäne zu ermöglichen. Wenn der Austausch in der Mitte eines Algorithmus statt findet, müssten alle Work-Items zur Hälfte abgearbeitet werden und der Status der Work-Items gespeichert werden. Für das Beispiel der Berechnung eines Bildes mit der Größe von 640x480 Pixeln, müsste dann der Status von 307200 Work-Items zwischengespeichert werden. Für größere Bilder steigt diese Zahl noch stark an. Um den Ressourcenbedarf für den Datenaustausch in Grenzen zu halten, werden die Work-Items in Gruppen, genannt Work-Groups, eingeteilt. Die Work-Items sind innerhalb der Work-Groups entsprechend der globalen Domäne auch in einer, zwei oder drei Dimensionen angeordnet. Innerhalb der Work-Group können die Work-Items über einen gemeinsam genutzten lokalen Speicher (Local-Memory) Daten austauschen und sich synchronisieren. Neben dem Local-Memory steht jedem Work-Item noch ein privater Speicherbereich für die eigenen Berechnungen zur Verfügung. Daten die für den gesamten parallelen Algorithmus gebraucht werden befinden sich im Global-Memory, auf das jedes Work-Item zugreifen kann. In diesem Global-Memory, dass sich auf dem OpenCLDevice befindet, alloziert der Host auch die Speicherbereiche. 12 Abbildung 2.1: Beispiel für das OpenCL-Programmiermodell. Die einzelnen Aufgaben eines parallelen Algorithmus sind Work-Items zugeteilt. Die Work-Items werden auf einer zweidimensionalen Domäne angeordnet. Des Weiteren sind die Work-Items in Work-Groups gruppiert. Work-Items können nicht beliebig mit anderen Work-Items in der globalen Domäne kommunizieren. Innerhalb einer Work-Group sieht OpenCL einen gemeinsamen Speicher vor, so dass Work-Items einer Work-Group über diesen Speicher kommunizieren können. 2.1.2 Brook+ In dieser Arbeit wird Brook+ aufgeführt, da es als Entwicklungsumgebung für parallele Berechnungen auf AMD GPUs genutzt wurde, bevor OpenCL für diese Hardware zur Verfügung stand. Brook+ basiert auf der BrookGPU-Umgebung [12] der StanfordUniversität. Der Unterschied zwischen beiden Umgebungen liegt in der Erweiterung des BrookGPU-Programmiermodells von einem reinen Stream-Modell hin zu einem OpenCL ähnlichen Berechnungsdomänen-Modell und die Anpassung an die AMD eigene Grafikhardware. Da die Brook+-Kernel in einer C-ähnlichen Sprache geschrieben werden, war der Umstieg zu OpenCL einfach durchfürbar. Auch wenn Algorithmen in Brook+ anfangs performanter rechneten als Algorithmen in AMDs OpenCL-Umgebung, war der Umstieg zu OpenCL auf Grund von Aspekten des Software-Engineerings und der Stabilität während der Ausführung unumgänglich. Brook+ unterstützt in seinen Kerneln wie auch OpenCL Subfunktionen. Mit diesen Subfunktionen kann der Programmcode auf einfache Weise strukturiert werden. Da Brook+ aber weder Pointerarithmetik noch andere Rückgabetypen als die Brook+-Standarddatentypen unterstützt, können Subfunktionen maximal 13 vier Gleitkomma- oder Ganzzahlwerte berechnen. Die verwendete Geometrische Algebra erfordert meist die Berechnung von Zwischenergebnissen mit mehr als vier Werten. Als Konsequenz kann der Programmcode nicht ausgelagert werden. Der gesamte Kernel wird sehr unübersichtlich. Als zweiter Nachteil zeigte sich, dass die Verwendung von Brook+ den Absturz des Grafikkartentreibers hervorrufen kann, wenn Kernel mehrmals für große Datenmodelle ausgeführt werden. Ein Vorteil von Brook+ ist der relativ leichte Einstieg in das Programmieren von GPUs. Auf dem Host müssen nur die Datenstreams in Ihren Datentypen und Dimensionen definiert, die Daten den Streams zugewiesen und der Kernel gestartet werden, um die Berechnungen auszuführen. Dies wird mit sehr wenigen Befehlen umgesetzt. 2.1.3 Hardware Ein AMD Athlon X2 5600+ Prozessor und eine AMD Radeon HD4850 Grafikkarte stellen die Berechnungshardware für die Algorithmen dieser Arbeit dar. Dieses Kapitel beschreibt den Aufbau der Recheneinheiten der Grafikkarte und ihrer GPU (Grafikprozessor), da der Aufbau einige Designentscheidungen der Algorithmen beeinflusst. Mit der Beschreibung wird des Weiteren versucht einen Zusammenhang mit den OpenCL-Ausdrücken herzustellen. Abbildung 2.2 zeigt den Aufbau der verwendeten Grafikkarte schematisch. Die Grafikkarte, das OpenCL-Device, besteht hauptsächlich aus einem Grafikprozessor (GPU) und zusätzlich mehreren Speicherchips, die den globalen Speicher für OpenCL stellen. Die GPU enthält als Hauptrecheneinheiten zehn SIMD-Einheiten, die gemeinsam eine Kernel-Domäne bearbeiten. Die Abkürzung SIMD (Single Instruction Multiple Data) bedeutet, dass ein Befehl im Algorithmus auf mehrere Datenelemente (Work-Items) parallel angewendet wird. Als logische Konsequenz werden die OpenCL-Work-Groups als Einheit paralleler Datenelemente jeweils von genau einer SIMD-Einheit bearbeitet. Die SIMD-Einheiten enthalten zur parallelen Berechnung mehrerer Work-Items jeweils 16 Arithmetisch-LogischeEinheiten (ALUs). Des Weiteren ist jeder SIMD-Einheit ein Registersatz von 256 float4Vektorregistern, eine Textureinheit für den Zugriff auf den globalen Speicher und ein weiterer GPU-interner Speicherbereich von 16kB, der sogenannte Local Data Share (LDS), zugeteilt. Der LDS ist dazu gedacht, dass auf einer SIMD-Einheit Daten zwischen verschiedenen Work-Items ausgetauscht werden können. Die GPU wurde zu einer Zeit entwickelt als OpenCL noch nicht als Standard verabschiedet war. Somit schreibt OpenCL ein anderes Zugriffsmuster auf das Local-Memory vor, als es mit dem LDS umgesetzt wird. Jedem AMD-Hardware-Work-Item wird im LDS ein Speicherbereich zugewiesen in dass nur das Work-Item schreiben darf. Lesen dürfen die AMD-Hardware-Work-Items hingegen auch aus anderen LDS Bereichen. OpenCL schreibt hingegen vor, dass die Work-Items beliebig im gesamten Local-Memory einer Work-Group schreiben dürfen. Damit AMD dennoch OpenCL-Kernel auf den HD4850 GPUs ausführen kann wird Local-Memory in einem Teil der Speicherchips der Graffikkarte emuliert. Als Konsequenz ist der Zugriff auf das LocalMemory genau so langsam wie der Zugriff auf das OpenCL-Global-Memory. Die ALUs wenden jeden Befehl sukzessive auf insgesamt vier Hardware-Work-Items an. Bei der Anzahl von 16 ALUs pro SIMD werden pro Befehl mindestens 64 Hardware-Work-Items bearbeitet. 14 Abbildung 2.2: Schematische Darstellung der AMD HD4850 Grafikkarte. Auf der Grafikkarte befinden sich der Grafikchip und mehrere Speicherchips. Der Grafikchip enthält als Hauptrecheneinheiten zehn SIMD-Einheiten. Jede SIMDEinheit besitzt einen eigenen Speicherbereich, den Local-Data-Share (LDS), über den Threads miteinander kommunizieren können. Des Weiteren beinhaltet eine SIMD-Einheit eine Textureinheit (TEX) für den Zugriff auf den Grafikspeicher. Die Berechnungen werden innerhalb der SIMD-Einheit von 16 ALUs (arithmetic logical unit) durchgeführt. Jede ALU enthält fünf Rechenwerke und kann somit Parallelität innerhalb eines Programmablaufs ausnutzen. Die Recheneinheiten w bis z sind gleich aufgebaut. Sie berechnen die häufigsten Operationen wie Additionen und Multiplikationen. Die tranzendale Einheit t kann zusätzlich seltenere mathematische Funktionen wie die Dreiecksfunktionen oder den Logarithmus berechnen. 15 Um die Hardware voll auszulasten müssen die OpenCL-Work-Groups ein ganzes Vielfaches von 64 Work-Items enthalten. Neben der Parallelität zwischen den Work-Items eines algorithmischen Problems, kann auch der Algorithmus zur Bearbeitung eines Work-Items Berechnungen enthalten, die parallel abgearbeitet werden können (Instruction Level Parallelism; ILP). In der Grafikberechnung wird oft mit Vektoren mit vier Einträgen, wie Positionen, Normalen oder Farbwerten inklusive Transparenz gearbeitet, deren Einträge parallel berechnet werden können. AMD versucht bei der HD4850 GPU diese Parallelität auszunutzen und stattet die ALUs mit fünf Rechenwerken aus. Vier dieser Rechenwerke sind einfacher aufgebaut. Sie können nur Multiplikationen und Additionen ausführen. Die fünfte ALU kann zusätzlich noch tranzendente Berechnungen wie Dreiecksfunktionen, Logarithmen und Wurzeln durchführen. Um die ALUs trotzdem möglichst einfach zu halten, wird die Parallelität vor der Ausführung auf der Host-CPU ermittelt und in den kompilierten Maschinencode mittels langer Befehlswörter (Very Long Instruction Word; VLIW) geschrieben. Die langen Befehlswörter enthalten für jede der fünf Rechenwerke, den auszuführenden Befehl und die beteiligten Operanden. Die Ausnutzung des ILP ist ein Grund für die Wahl der AMD Hardware, da ILP Vorteile für die verwendeten Algorithmen verspricht. 2.2 Raytracing Raytracing ist ein Verfahren der Computergrafik. Es erzeugt an Hand einer Szenenbeschreibung und einer in dieser Szene platzierten virtuellen Kamera ein Bild. Basierend auf der Ausrichtung der Kamera und einer für die Kamera festgelegten Bildebene werden für jeden Pixel des resultierenden Bildes Strahlen von der Kamera durch die Pixelposition auf der Bildebene in die Szene verfolgt. Für jeden einzelnen Strahl wird der zur Kamera nächste Schnittpunkt mit einem Objekt der Szene gesucht. Wenn ein solcher Schnittpunkt gefunden wurde, wird an Hand der Materialeigenschaften des geschnittetenen Objekts und der in der Szene definierten Lichtquellen ein Farbwert für den zum Schnittstrahl gehörenden Pixel berechnet. Auf Grund des Strahlenparadigmas können mit Raytracing einige physikalische Effekte auf einfache Weise umgesetzt werden. Die von einem Objekt auf ein anderes Objekt geworfenen Schatten werden sichtbar gemacht, indem für ein vom Kamerastrahl geschnittenes Objekt vom Schnittpunkt zur Lichtquelle ein Strahl geschickt wird. Wenn dieser Strahl, der Schattenfühler, kein Objekt schneidet wird die Lichtquelle in die Beleuchtungsrechnung des Schnittpunkts einbezogen. In jedem anderen Fall wird die Lichtquelle durch ein Objekt der Szene verdeckt. Weitere einfach umzusetzende Effekte sind die Reflektion und Refraktion. Für ein Objekt, dessen Darstellung die genannten Effekte zeigen soll, werden am Schnittpunkt zwei neue Strahlen erzeugt. Der Reflektionsstrahl wird nach dem Reflektionsgesetz und der Refraktionsstrahl nach dem snelliusschen Brechungsgesetz berechnet. Die beiden Strahlen werden daraufhin auf weitere Schnitte mit der Szene überprüft, wobei für den Refraktionsstrahl beachtet werden muss, dass er sich nach der Brechung innerhalb eines Objekts befindet. Da ein Bild nach endlicher Zeit berechnet sein soll, werden für einen Strahl nur eine endliche Anzahl von Reflektionen und Brechungen berechnet. In dieser Arbeit wird auf die Berechnung von Sekundärstrahlen, wie Schattenfühler, Reflektions und Refraktionsstrahlen verzichtet, da sie keine zusätzlichen Erkenntnisse für die aufgestellte Fragestellung liefern. Diese Einschränkung auf die Berechnung von Primärstrahlen, die von der Kamera ausgehen, wird auch Raycasting ge16 nannt. Ohne ein zusätzliches Hilfsmittel müssen für jeden Strahl alle Objekte einer Szene auf den Schnitt mit dem Strahl getestet werden. Die Anzahl der Schnitttests wird selbst bei Szenen mit sehr wenigen Objekten so hoch, dass schon bei einer geringen Bildauflösung keine interaktive Darstellung der Szene möglich ist. Mit sogenannten räumlichen Datenstrukturen kann der Aufwand der Schnitttests drastisch reduziert werden. Abbildung 2.3: Schematische Darstellung des Raytracing-Verfahrens. Von der Kamera, links unten im Bild, werden durch die Bildebene Strahlen in die Szene geschossen. Diese Primärstrahlen schneiden einige Objekte der Szene. An den Schnittpunkten treten verschiedene Sekundärstrahlen auf. Schattenfühler werden vom Schnittpunkt zu jeder Lichtquelle verfolgt. Der Schnittpunkt mit dem transparenten, spiegelnden Sechseck ruft einen Reflektionsstrahl und einen Refraktionsstrahl hervor. Der Reflektionsstrahl schneidet ein weiteres Objekt. Dieses Objekt wird im berechneten Bild in der Abbildung des Sechsecks als Spiegelung sichtbar sein. Der Refraktionsstrahl befindet sich innerhalb des Sechsecks. Beim Auftreffen auf den Rand des Sechsecks an der anderen Seite des Objekts wird wieder ein Reflektionsstrahl und ein Refraktionsstrahl auftreten. Der Reflektionsstrahl befindet sich dann innerhalb und der Refraktionsstrahl außerhalb des Sechsecks. kd-tree Der kd-tree ist eine räumliche Datenstruktur, die die Beantwortung von Fragen nach der Nachbarschaft oder dem Schnitt von Objekten beschleunigt. Der kd-tree unterteilt den k dimensionalen Raum rekursiv in zwei Unterräume. Das Startobjekt, der Wurzelknoten, des Baumes ist eine an den Koordinatenachsen ausgerichtete Box, die gerade so groß ist, dass alle Objekte der Szene in der Box enthalten sind (Axis Aligned Bounding Box; AABB). Die Wurzel-AABB wird mittels einer Ebene, auf der eine der Koordinatenachsen senkrecht steht, in zwei Kindknoten unterteilt. Die Kindknoten werden wiederum mittels einer Ebe17 ne in zwei Kindknoten unterteilt. Es entsteht eine baumartige Struktur. Die Unterteilung wird so lange fortgesetzt bis ein Abbruchkriterium erfüllt wird. Knoten, die Kindknoten enthalten, werden innere Knoten genannt, während Knoten, bei denen die Unterteilung abgebrochen wurde, Blattknoten heißen. Aufgrund der binären Struktur des kd-trees kann die Nachbarschaft oder der Schnitt von Objekten mit logarithmischen Aufwand ermittelt werden. Ein Baum mit N Blättern hat eine Höhe von ld(N ). Es müssen im Idealfall ld(N ) innere Knoten und ein Blatt des Baums inklusive dessen enthaltenen Objekten überprüft werden, um die Berechnung durchzuführen. Es gibt verschiedene Algorithmen, um die Unterteilungsebene in einem Knoten auszuwählen. Zwei der einfachen Unterteilungsalgorithmen sind die Teilung am Knotenmedian oder am Objektmedian. Bei der Teilung am Knotenmedian wird eine Achse der KnotenAABB ausgewählt und die Ebene genau auf der Mitte dieser Achse platziert. Die Kindknoten haben dann genau die gleiche Oberfläche und das gleiche Volumen. Daraus folgt, dass für einen beliebig in der Szene platzierten Strahl beim Raytracing die Wahrscheinlichkeit gleich groß ist einen der beiden Kindknoten zu treffen. Wird am Objektmedian geteilt, so teilt die Ebene die im Knoten enthaltenen Objekte so auf die Kindknoten auf, dass in jedem Knoten gleich viele Objekte enthalten sind. Es kostet dann für beide Knoten gleich viele Schnittberechnungen der Objekte mit einem Strahl um einen Schnitt zu finden. Einer der zur Zeit besten Algorithmen zur Unterteilung eines kd-tree für die Beschleunigung des Raytracing ist die Surface Area Heuristic (SAH) [37]. Der SAH-Algorithmus zieht sowohl die Wahrscheinlichkeit, dass ein Strahl einen Kindknoten trifft als auch den Aufwand, die im Kindknoten enthaltenen Objekte auf Schnitt zu Testen in Betracht. Die Wahrscheinlichkeit wird dabei über die Oberfläche des resultierenden Kindknoten im Verhältnis zum Vaterknoten repräsentiert. Für einen Kandidaten der Unterteilungsebene werden die Kosten wie folgt festgelegt: KKandidat = K Tr av ersierung + + F laecheK not en_l inks F laecheVat er F laecheK not en_ r echts F laecheVat er ∗ AnzahlO b jekt e_l inks (2.1) ∗ AnzahlO b jekt e_ r echts In der Gleichung 2.1 steht die Variable K Tr av ersierung für die Kosten die vom RaytracingStrahl geschnittenen Kindknoten zu ermitteln. Während beim Knotenmedian- und Objektmedianalgorithmus ein übliches Abbruchkriterium für die Unterteilung eine obere Grenze für die Anzahl der Unterteilungen auf dem Weg von der kd-tree-Wurzel zum Blatt oder das Unterschreiten einer maximalen Anzahl von Objekten pro Knoten sind, enthält der SAHAlgorithmus ein anderes Abbruchkriterium. Ein Knoten wird dann nicht mehr unterteilt, wenn die minimalen Kosten eines Unterteilungskandidaten größer sind, als die Kosten für die Schnitttests der im Knoten enthaltenen Objekte. Als Unterteilungskandidaten kommen alle Positionen entlang einer der Knotenachsen in Frage, an denen sich die Kosten ändern. Diese Positionen sind die Kanten der ObjektAABBs im Knoten. Hieraus folgt, dass es sehr teuer ist einen Knoten mit dem SAHAlgorithmus zu unterteilen, da für alle Objekte mehrere Unterteilungskandidaten und deren Kosten berechnet werden müssen. Selbst mit dem in [50] beschriebenen Algorithmus ist die Konstruktion des kd-tree mit SAH so teuer, dass sie nicht für dynamische Szenen auf einer CPU geeignet ist. Ein Algorithmus, der dynamische Szenen mit interaktiven Bildraten ermöglicht und für Knoten nahe den Blättern SAH einsetzt, wird in [53] vorgestellt. Dieser Algorithmus macht 18 starken Gebrauch von lokalem Speicher zur Kommunikation der Work-Items auf der GPU. Da auf der hier verwendeten Hardware der lokale Speicher nicht effizient verwendet werden kann, wird der Algorithmus in dieser Arbeit nicht auf der GPU umgesetzt. Die Struktur des Algorithmus wird hingegen für die Erstellung des kd-trees übernommen, um bei einer Änderung der Hardware den Algorithmus auf der GPU umsetzen zu können. Alle Berechnungen die im originalen Algorithmus auf der Grafikkarte ausgelagert sind, werden in dieser Arbeit auf der CPU berechnet. Des Weiteren wird nur die Large-Node-Stage ohne Berechnung der SAH-Unterteilungsebenen umgesetzt. 19 3 Geometrische Algebra Grundlage dieser Arbeit ist die Konforme Geometrische Algebra. In den folgenden Abschnitten wird eine kurze Einleitung in diese spezielle Ausprägung der Geometrischen Algebren gegeben. Es werden nur so weit Details genannt, wie sie für die in der Arbeit beschriebenen Algorithmen notwendig erscheinen. Einen vertieften Einblick in die Konforme Geometrische Algebra kann der geneigte Leser in [46] finden. 3.1 Grundlagen Die Geometrische Algebra auf einem n-dimensionalen Raum operiert auf Unterräumen dieses Raums. Die Unterräume werden Blades genannt. Der Grad eines Blades gibt die Anzahl der Basisvektoren des n-dimensionalen Raums an, die den Unterraum aufspannen. Daraus folgt, dass es Blades beginnend mit Grad 0 bis zum maximalen Grad n gibt. Die Basisvektoren werden mittels des Operators ∧ zu einem Unterraum verknüpft. Der Operator ∧ stellt hierbei das äußere Produkt der Geometrischen Algebra dar. Für eine ndimensionalen Raum können insgesamt 2n Blades aufgespannt werden. In Tabelle 3.1 sind die Blades des dreidimensionalen euklidischen Raums zur Veranschaulichung aufgelistet. Während eindimensionale Blades Vektoren genannt werden, zweidimensionale Blades Bivektoren heißen und höherdimensionale Blades entsprechende Namen haben, nennt man Linearkombinationen von Blades verschiedenen Grades Multivektoren. Grad 0 1 2 3 Bezeichnung Blades Skalar 1 Vektor e1 , e2 , e3 Bivektor e1 ∧ e2 , e1 ∧ e3 , e2 ∧ e3 Pseudoskalar e1 ∧ e2 ∧ e3 Tabelle 3.1: Die Blades des dreidimensionalen euklidischen Raums. 3.2 Produkte In der Geometrischen Algebra gibt es drei wichtige Produkte mit denen alle Operationen der Algebra ausgedrückt werden können. Diese Produkte sind das äußere, das innere und das geometrische Produkt. • Das äußere Produkt funktioniert wie ein Vereinigungsoperator für Unterräume. Es hilft somit Unterräume aufzuspannen, d.h. der Unterraum des äußeren Produkts zweier Multivektoren hat einen höheren Grad als die Räume der Multivektoren wenn die Multivektoren nicht linear abhängig sind. Die Eigenschaft, die lineare Abhän21 gigkeit zwischen Multivektoren zu zeigen, beruht auf der Asymmetrie des äußeren Produkts. Es gilt für zwei Vektoren a und b. a ∧ b = −(b ∧ a) Das äußere Produkt eines Vektors mit sich selbst muss somit Null sein, damit die Gleichung erfüllt ist. • Das innere Produkt verringert im Gegensatz zum äußeren Produkt den Grad des Ergebnisvektors verglichen mit den Eingabevektoren. Man kann sich die Operation so vorstellen, dass der Unterraum des ersten Operanden des Produkts vom Unterraum des zweiten Operanden abgezogen wird. Im Ergebnis bleibt dann der Unterraum vom zweiten Operanden übrig, der nicht im ersten Operanden enthalten war. Der Ergebnisvektor muss also senkrecht zum ersten Operanden stehen. Folglich ergibt das innere Produkt zwischen erstem Operanden und Ergebnisvektor Null, da beide Unterräume „nichts“ gemeinsam haben. Das innere Produkt kann also zum Messen von Rechtwinkligkeit genutzt werden. • Das geometrische Produkt ist das algebraische Produkt der Geometrischen Algebra. Über die definierende Gleichung der Geometrischen Algebra werden ihre besonderen Eigenschaften festgelegt. Es gilt: a∗a=a◦a∈F Hierbei sei ∗ das geometrische Produkt, der Vektor a ∈ Vn und Vn ein n-dimensionaler Vektorraum über dem Feld F. Das skalare Produkt ◦ über Vn sei so definiert, dass für zwei Vektoren a, b ∈ Vn gelte a◦b= b◦a∈F Das geometrische Produkt eines Vektors der Geometrischen Algebra, also eines Blades mit Grad 1, auf dem Vektorraum Vn mit sich selbst bildet daher auf ein Element des Feldes F ab. In dieser Arbeit ist F gleichzusetzen mit den reelen Zahlen R. Innerhalb von Formeln werden das äußere Produkt, das innere Produkt und das geometrische Produkt mit den Symbolen ∧, · und ∗ dargestellt. Das Symbol ∗ des geometrischen Produkts wird in Formeln manchmal auch weggelassen, wenn klar ist, welche Multivektoren in der Formel durch das geometrische Produkt verknüpft werden. 3.3 Konformer Raum Die in dieser Arbeit verwendete Konforme Geometrische Algebra ist eine Geometrische Algebra, die auf einer speziellen Einbettung des dreidimensionalen euklid’schen Raumes basiert. Zuerst wird der 3D euklid’sche Raum mittels einer stereoskopischen Projektion auf eine Hyperkugel mit dem Radius 1 und dem Mittelpunkt im Ursprung projeziert. Am Beispiel des eindimensionalen euklid’schen Raumes soll dies hier verdeutlicht werden. Es wird von der positiven y-Achse eine Gerade g zu einem Punkt x auf der x-Achse gezogen. Die x-Achse repräsentiert hierbei den eindimensionalen euklid’schen Raum. Dort wo die Gerade g den Kreis mit Radius 1 zentriert um den Ursprung schneidet, befindet sich die 22 Abbildung 3.1: Stereoskopische Projektion des eindimensionalen Raums. Als Beispiel werden Projektionen der Werte −2, 0, 12 und 4 auf die zweidimensionale Hyperkugel (Kreis) mit dem Radius 1 gezeigt. stereoskopische Projektion des Punktes x auf der Hyperkugel, die im zweidimensionalen Raum ein Kreis ist. Aus dieser Projektionsanweisung folgt, dass −∞ und ∞ auf die y-Achse bei eins projeziert werden. Null wird auf die y-Achse bei -1 projeziert. Der stereoskopischen Projektion folgt eine Homogenisierung des Raumes. Es wird also noch eine weitere Koordinate hinzugefügt. Alle Punkte aus dem Raum, der die stereoskopische Projektion des euklid’schen Raumes enthält, werden mit einer 1 in dieser Koordinate versehen. Für das Beispiel des eindimensionalen euklid’schen Raumes bedeutet das, dass die auf den Kreis projezierten Punkte des 2D-Raumes nun im 3D Raum auf einem Kreis liegen dessen Ebene parallel zur x-y-Ebene mit Abstand 1 zum Ursprung liegt. Bei der Rücktransformation aus dem Projektiven Raum werden die x und die y-Koordinate durch den Wert der z-Koordinate geteilt. Es werden also alle Punkte, die auf dem Kegel durch den homogenisierten Kreis liegen, wobei die Kegelspitze im 3D-Ursprung liegt, zurückprojeziert. Der 3D-Ursprung selbst liegt nicht im Projektivenraum und somit gibt es auch kein Problem bei der Rückprojektion mit einer Division durch Null. 3.4 Objekte Objekte, die direkt in der Konformen Geometrischen Algebra enthalten sind, wie Kugeln, Ebenen und Geraden sind mathematisch Unterräume des konformen Raums. Erst durch die richtige Interpretation im euklid’schen Raum erscheinen diese Unterräume auch als die geometrischen Gebilde, deren Bezeichnung sie tragen. Der Interpretationsansatz wird im Kapitel 3.4.1 beschrieben während die in dieser Arbeit verwendeten geometrischen Objekte in Kapitel 3.4.2 aufgelistet werden. 23 3.4.1 Produktnullräume Es gibt zwei Interpretationsvorschriften, die Unterräume des konformen Raums auf geometrische Objekte im dreidimensionalen euklid’schen Raum abbilden. Die zwei Vorschriften sind der Inner Product Null Space (IPNS) und der Outer Product Null Space (OPNS). Die „Vorschriften“ sind also selbst auch spezielle Unterräume. Der IPNS zu einem Multivektor ist der Raum von Vektoren, die unter dem inneren Produkt mit dem Multivektor Null ergeben. Für die Geometrische Algebra auf dem dreidimensionalen euklid’schen Raum und den Vektor e1 , der parrallel zur x-Achse liegt, ist das die y-z-Ebene. Gleichzeitig ist der IPNS des Bi-Vektors e1 ∧ e2 die z-Achse. Analog ist der OPNS eines Multivektors der Raum von Vektoren, die unter dem äußeren Produkt mit dem Multivektor Null ergeben. Es sind die vom Multivektor linear abhängigen Vektoren. In unserem Beispiel ist der OPNS des Vektors e3 also gerade die z-Achse. Weiterhin ist der OPNS des Bi-Vektors e1 ∧ e2 gerade die von den Vektoren e2 und e3 aufgespannte y-z-Ebene. Sowohl die z-Achse als auch die y-z-Ebene können in der Geometrischen Algebra auf E3 je nach Interpretationsansatz auf zwei verschiedene Arten dargestellt werden. IPNS und OPNS verhalten sich dual zueinander. Es ist natürlich auch möglich die Repräsentation eines geometrische Objekts vom einem zum anderen „Interpretationsraum“ zu wechseln. Dieser Wechsel wird in Formeln durch ein hochgestelltes ∗ illustriert. 3.4.2 Objektarten In der Konformen Geometrischen Algebra gibt es sechs verschiedene geometrische Objekte, die direkt in der Algebra repräsentiert werden können. Diese Objekte sind der Punkt, die Kugel, die Ebene, der Kreis, die Gerade und das Punktpaar. Der Aufbau der Objekte wird im Folgenden sowohl im IPNS als auch im OPNS erläutert. Je nach Anwendungsfall ist es manchmal vorteilhaft ein Objekt in einem Interpretationsraum zu erstellen und dann mittels des Dualitätsoperators in den anderen Interpretationsraum zu überführen, um dort zu rechnen. Im Folgenden stehen fettgedruckte kleine Buchstaben für Summen der Vektoren e1 , e2 und e3 mit ihren Vorfaktoren. Der Vektor p steht also für p1 ∗ e1 + p2 ∗ e2 + p3 ∗ e3 . Punkt Der Punkt hat sowohl im IPNS als auch im OPNS die gleiche Formel. Die Formel leitet sich aus der Einbettung des euklid’schen Raumes in den konformen Raum ab. Die Herleitung kann in [46] nachgelesen werden. Die Formel für den Punkt P lautet: 1 P = p + p2 e∞ + e0 2 24 Kugel Im IPNS wird eine Kugel über ihren Mittelpunkt M und ihren Radius r definiert. Der Mittelpunkt ist hierbei ein Punkt der Geometrischen Algebra wie in Kapitel 3.4.2. Die Formel für die Kugel S im IPNS lautet: 1 S = M − r 2 e∞ 2 Wenn der Radius gegen Null geht verschwindet der Term 12 r 2 e∞ und es bleibt die Formel für einen Punkt übrig. Der Punkt kann also als besondere Ausprägung der Kugel gesehen werden. Im OPNS wird die Kugel einfach über vier Punkte auf ihrer Oberfläche definiert, wobei die Punkte nicht in einer Ebene liegen dürfen. S ∗ = P1 ∧ P2 ∧ P3 ∧ P4 Ebene Wie im projektiven Raum der Linearen Algebra wird im IPNS die Ebene π über ihre Normale n und den Abstand zum Ursprung d dargestellt. π = n + de∞ Die OPNS Repräsentation der Ebene erfolgt über drei Punkte, die in der Ebene liegen und den Punkt im Unendlichen e∞ . π∗ = P1 ∧ P2 ∧ P3 ∧ e∞ Diese Formel ähnelt sehr stark der Formel einer Kugel im OPNS. Tatsächlich kann gezeigt werden, dass die Ebene eine Kugel mit unendlichem Radius ist. Nähere Einzelheiten können in [29] im Kapitel „Planes as a limit of spheres“ nachgelesen werden. Kreis Die IPNS Repräsentation eines Kreises ergibt sich aus dem Schnitt zweier Kugeln. Der Schnitt wird über das Äußere Produkt berechnet. Z = S1 ∧ S2 Für die OPNS Repräsentation des Kreises werden drei Punkte über das äußere Produkt verbunden. Z ∗ = P1 ∧ P2 ∧ P3 25 Gerade Analog zum Kreis wird die Gerade im IPNS über den Schnitt zweier Ebenen (also Kugeln mit unendlichem Radius) repräsentiert. L = π1 ∧ π2 Die OPNS Repräsentation der Gerade verhält sich zum Kreis wie die Repräsentation der Ebene zur Kugel. Es wird einer der beteiligten Punkte durch den Punkt im Unendlichen ersetzt. Die Geraden Repräsentation ist also das äußere Produkt von zwei Punkten auf der Geraden und dem Punkt im Unendlichen. L ∗ = P1 ∧ P2 ∧ e∞ Punktpaar Der Schnitt dreier Kugeln ist die IPNS Repräsentation des Punktpaares. P p = S1 ∧ S2 ∧ S3 Im OPNS kann das Punktpaar einfach über die beteiligten Punkte verbunden über das äußere Produkt dargestellt werden. P p∗ = P1 ∧ P2 26 4 Oberflächenmodell In den nachfolgenden Kapiteln werden Details des Oberflächenmodells erläutert. Zuerst wird der Aufbau des Modells aufgezeigt. In den folgenden Kapiteln werden zwei Algorithmen zur Berechnung des Oberflächenmodells erklärt. 4.1 Aufbau des Oberflächenmodells Das Ziel dieser Arbeit setzt voraus, dass die Oberflächenrepresentation von Modellen auf den Objekten der Geometrischen Algebra basiert. Zwei dieser Objekte, die direkt eine Oberfläche darstellen können, sind die Kugel und die Ebene. Jedes dieser beiden Objekte kann einzeln betrachtet nur in begrenztem Maße Objekte aus der Realität wiedergeben. Objekte aus der echten Welt haben meistens viele verschiedene Details, glatte Oberflächenstücke und gekrümmte Bereiche. Für diese einzelnen Bereiche eines Objektes reicht eine angenäherte Kugel oder Ebene durchaus aus, um sie zu repräsentieren. Das Oberflächenmodell eines Objekts soll daher aus vielen verschiedenen Kugeln und Ebenen bestehen, die jeweils ein bestimmtes Detail des Objekts repräsentieren. Die gesamte Oberfläche setzt sich dann aus vielen Oberflächenelementen genannt Surfel (engl. SURFace ELement) zusammen. Es reicht aber nicht aus einfach ein Objekt mit mehreren Kugeln und Ebenen lokal anzunähern und dieses dann rendern zu wollen. Eine Ebene ist ein Objekt mit unendlicher Ausdehnung und auch eine Kugel mit geringer Krümmung und folglich großem Radius würde im gerenderten Bild wesentlich mehr Pixel abdecken als das geometrische Detail, das durch die Kugel reräsentiert werden soll. Der gültige Bereich der Repräsentation durch eine Ebene oder Kugel muss lokal eingeschränkt werden um dieses Problem zu lösen. Ein geometrisches Detail eines Objekts befindet sich an einer bestimmten Stelle im 3DRaum und je weiter man sich von dieser Stelle entfernt umso eher wird das Surfel, das das Detail repräsentiert, die aktuelle Umgebung nicht mehr gut annähern. Aus dieser Beschreibung ergibt sich, dass das Surfel nur in einem bestimmten Radius um einen Punkt herum gültig ist. Ein Objekt der geometrischen Algebra, dass genau diesen gültigen Bereich beschreiben kann, ist die Kugel, die im IPNS (siehe Kapitel 3.4.2) direkt über den Mittelpunkt und einen Radius definiert werden kann. Diese begrenzende Kugel soll im Folgenden Bounding-Sphere genannt werden. In Abbildung 4.1 wird der Zusammenhang aus Punktwolke, Surfel und Bounding-Sphere verdeutlicht. Für den roten Punkt und seine Punktnachbarschaft (grüne Punkte) wurde ein Surfel berechnet. Das Surfel wird durch den schwarzen Kreisbogen repräsentiert. In Wahrheit gehört der Kreisbogen zu einem Kreis, der hier aus Platzgründen nicht komplett dargestellt wird. Der Kreisbogen approximiert nur die Punkte in der direkten Nachbarschaft des roten Punkts gut genug. Die grauen Punkte werden hingegen schlecht durch den Kreisbogen approximiert. Da die Krümmung der Oberfläche in der Nähe des roten Punkts gering ist, hat das berechnete Surfel einen großen Radius und erstreckt sich somit über die grauen Punkte. Das Surfel wird daher von dem gestrichelten Kreis, der die Bounding-Sphere 27 Abbildung 4.1: Darstellung der Grundelemente des Modells. Für den schwarzen Punkt und seine Nachbarschaft wurde ein Surfel berechnet (schwarzer Kreisbogen). Das Surfel wird von der Bounding-Sphere (gestrichelter Kreis) auf den Bereich des 2D-Raumes eingeschränkt, in dem gut genug angenäherte Punkte (graue Punkte) liegen. repräsentiert, auf den Bereich des 2D-Raumes eingeschränkt in dem Punkte gut genug angenähert werden. Zusammenfassend besteht das gesamte Oberflächenmodell aus Surfeln und zugehörigen Bounding-Spheres. Die Surfel sind Ebenen oder Kugeln, die lokale Details des Objekts annähern. Die Bounding-Spheres begrenzen den Gültigkeitsbereich des zugehörigen Surfels. 4.2 Algorithmus 1 Das Ziel bei der Entwicklung dieses Algorithmus war es alle Punkte der Punktwolke bis zu einer bestimmten Schranke ε genau genug durch mindestens ein Surfel anzunähern. Die Schranke ε kann somit dazu genutzt werden die Genauigkeit des resultierenden Modells festzulegen. Weiterhin beeinflusst ε indirekt auch die Anzahl der erzeugten Surfel. Wenn ein Punkt nicht direkt auf der Oberfläche eines Surfels liegt, entscheidet die Schranke darüber, ob für diesen Punkt ein zusätzliches Surfel berechnet werden muss. Je weniger Surfel verwendet werden, desto schneller kann der spätere Renderingprozess ablaufen. Es müssen dann weniger Surfel zur Erstellung des Bildes beachtet werden. Die Oberfläche eines Objekts ändert sich meistens nicht sehr stark an den Stellen benachbarter Abtastpunkte. Für diese Punkte reicht es aus, sie durch ein gemeinsames Surfel zu repräsentieren. Es müssen nur für eine Untermenge der Punkte einer Punktwolke Surfel erstellt werden. Die Erstellung eines Surfels erfordert eine Gruppe von im dreidimensionalen Raum nahe beieinander liegenden Punkten, die eine Punktnachbarschaft bilden. Die Stelle, an der ein Surfel erstellt wird, soll zufällig gewählt werden. Es wird einfach ein noch nicht durch ein Surfel repräsentierter Punkt genannt P1 gewählt. Mit diesem Punkt und 28 seinen Nachbarn wird dann das Surfel berechnet. Damit der Algorithmus terminiert, auch wenn das berechnete Surfel den Punkt P1 nicht genau genug annähert, wird ein Punkt P1 immer als repräsentiert geführt sobald das zugehörige Surfel berechnet wurde. Ein weiterer Parameter des Algorithmus ist die Größe k der Punktnachbarschaft. Sie legt fest wie viele Nachbarn eines Punktes P1 verwendet werden, um ein Surfel zu berechnen. Der Parameter k ist für den kompletten Durchlauf des Algorithmus 1 unveränderbar. Da für die Repräsentationsberechnung nur die Punkte verwendet werden, die auch an der Berechnung des Surfels beteiligt waren, legt der Parameter k auch die untere Schranke für die Anzahl der Surfel fest. Die Schranke hat den Wert Anzahl al lker Punkt e . 4.2.1 Ablauf von Algorithmus 1 Algorithmus 1 arbeitet iterativ. Es wird ein Surfel nach dem anderen erstellt. Der Ablauf ist wie folgt: 1. Suche einen Punkt P1, der noch nicht repräsentiert wird 2. Finde die Nachbarn des Punktes 3. Berechne das Surfel an Hand der Punktnachbarschaft 4. Stufe den Punkt P1 als repräsentiert ein 5. Berechne die Bounding-Sphere zum Surfel 6. Prüfe ob die Nachbarn von P1 durch das Surfel repräsentiert werden 7. Wenn es noch nicht repräsentierte Punkte gibt, fahre mit 1. fort Auf die Punkte 2, 3 und 5 wird in den Kapiteln 4.2.2, 4.2.3 und 4.2.4 noch näher eingegangen. Die Prüfung in Punkt 6 wird anhand zweier Kriterien durchgeführt. Zum einen wird der Abstand zum Surfel berechnet und mit der oben erwähnten Genauigkeitsschranke ε verglichen. Zum anderen wird überprüft, ob der Punkt innerhalb der Bounding-Sphere liegt. Nur wenn der Abstand unter der Schranke liegt und der Punkt nicht außerhalb der Bounding-Sphere ist, wird er als repräsentiert eingestuft. 4.2.2 Suche der Punktnachbarschaft Die Suche der Nachbarn eines Punktes wurde mit der ANN-Bibliothek [42][41] implementiert. Der Name ANN leitet sich von approximate nearest neighbors ab, also der Suche der ungefähren nächsten Nachbarn. Die ANN-Suche kann auf eine exakte Suche umgestellt werden, welche im Programmcode von Algorithmus 1 verwendet wird. ANN verwendet zur Suche einen kd-tree, der vor der Schleife von Algorithmus 1 mit den Punkten der Punktwolke initialisiert wird. Mit ANN kann nach den Nachbarn beliebiger Punkte im 3D-Raum und nicht nur nach den Nachbarn von Punkten, die bei der Initialisierung übergeben wurden, gesucht werden. Ein Resultat davon ist, dass bei der Suche von Nachbarn der Punkte aus der Punktwolke der Punkt aus der Punktwolke selbst als sein nächster Nachbar als Resultat der Suche ausgegeben wird. Um die tatsächlichen k nächsten Nachbarn zu finden muss ANN also nach den k + 1 nächsten Nachbarn suchen. 29 4.2.3 Berechnung des Surfels Die Berechnung eines Surfels geschieht nach dem Algorithmus aus [29] Kapitel Approximation of points with the help of planes or spheres. Der Algorithmus nutzt das innere Produkt zwischen Punkt P und Kugel S beziehungsweise Ebene π um ein Distanzmaß zwischen beiden Objekten zu erhalten. Das innere Produk zwischen P und S 1 1 P · S = (p + p2 e∞ + e0 ) · (M − r 2 e∞ ) 2 2 kann mittels der Produkteigenschaften umgestellt werden zu 2(P · S) = r 2 − (m − p)2 Das doppelte innere Produkt zwischen P und S gibt also die Differenz zwischen dem quadrierten Radius der Kugel und dem quadrierten Abstand zwischen P und dem Mittelpunkt von S an. Dieser Wert ist gleich Null, wenn der Punkt auf der Kugel liegt, kleiner Null, wenn der Punkt außerhalb der Kugel liegt und größer als Null, wenn P innerhalb der Kugel liegt. An Hand dieser Beobachtung kann ein Ansatz nach der Methode der Kleinsten Quadrate erstellt werden. Einzelheiten sind [29] zu entnehmen. Für die Implementierung bedeutet dies, dass ausgehend von der Punktnachbarschaft folgende Matrix B berechnet werden muss: P n i=1 pi,1 pi,1 Pn i=1 pi,2 pi,1 P B = ni=1 pi,3 pi,1 P n i=1 −pi,1 P n 1 2 i=1 − 2 pi,1 ∗ pi Pn Pn Pn Pn Pn Pn i=1 pi,1 pi,2 i=1 pi,2 pi,2 Pn i=1 pi,3 pi,2 Pn i=1 −pi,2 Pn 1 2 i=1 − 2 pi,2 ∗ pi i=1 pi,1 pi,3 i=1 pi,2 pi,3 Pn i=1 pi,3 pi,3 Pn i=1 −pi,3 Pn 1 2 i=1 − 2 pi,2 ∗ pi i=1 −pi,1 i=1 −pi,2 Pn i=1 −pi,3 Pn i=1 1 Pn i=1 1 1 2 − p ∗ p i,1 i=1 i 2 Pn 1 2 i=1 − 2 pi,2 ∗ pi Pn 1 2 i=1 − 2 pi,3 ∗ pi Pn 1 2 i=1 2 pi Pn 1 4 p i=1 4 i Pn Da B symmetrisch ist reduziert sich die Berechnung auf das obere Dreieck und die Diagonale. Der Eigenvektor zum kleinsten Eigenwert von B enthält die Koeffizienten der Vektoren e1 , e2 , e3 , e∞ und e0 . Zur Berechnung der Eigenwerte und Eigenvektoren wird die Bibliothek newmat10 [14] verwendet. Die Bibliothek bietet zwei Algorithmen zur Lösung des Eigenwertproblems an. Algorithmus 1 verwendet die Methode EigenValues(), die auf einer Householder-Tridiagonalisierung [38] der Matrix B und folgenden Iterationen von QL-Zerlegungen [9] basiert. Die Methode EigenValues() wird vom Autor von newmat selbst empfohlen und war in der Mehrzahl eigener Tests drei- bis vierfach schneller als die ebenfalls vorhandene Jacobi()-Methode, welche das gleichnamige Verfahren verwendet. 4.2.4 Berechnung der Bounding-Sphere Zur Berechnung der Boundingsphere wird der IPNS Repräsentation folgend der Mittelpunkt und der Radius der Bounding-Sphere benötigt. Als Mittelpunkt wird der Punkt P1 aus der Konstruktion des zugehörigen Surfels gewählt. Die Annahme ist hier, dass die Nachbarn gleichmäßig im Raum verteilt um P1 liegen. Als maximaler Radius wird der Abstand von P1 zu dem am weitesten entfernten Nachbarn der k gesuchten Nachbarn gewählt. 30 Abbildung 4.2: Artefakte am Bunny Modell ohne Anpassung des Bounding-Sphere-Radius. In den Rechtecken sind deutlich Artefakte aus Kugeln und Kugelschnitten zu sehen, die nicht die Daten der Punktwolke wiedergeben. Abbildung 4.3: Deutliche Reduktion der Artefakte. Der Radius der Bounding-Spheres wird abhängig vom Radius des Surfels reduziert. 31 Da diese Wahl bei Surfeln mit geringem Radius und einigen Modellen Artefakte wie in Bild 4.2 beim Rendern erzeugt, wird der Radius abhängig vom berechneten Surfel verändert. Wenn der geviertelte Radius des Surfels kleiner als der Abstand von P1 zum am weitesten entfernten Nachbarn ist, wird als Radius für die Bounding-Sphere der geviertelte Radius des Surfels verwendet. Abbildung 4.3 zeigt das Ergebnis dieser Anpassung des Bounding-Sphere-Radius. Die Artefakte sind in der Abbildung deutlich reduziert. Der Wert des Divisors von vier wurde empirisch ermittelt und zeigte bei den verwendeten Modellen eine gute Verringerung der Artefakte. Nur bei der Chameleonpunktwolke, die in Bild 4.4 visualisiert ist, zeigt sich, dass auf Grund der Einschränkung des Radius die Zunge nicht komplett dargestellt wird. In Bild 4.5 wird die Zunge weitgehend vollständig dargestellt. In diesem Bild wird die Einschränkung durch den Radius des Surfels nicht vorgenommen. Abbildung 4.4: Fehler in der Darstellung der Zunge der Chameleon-Punktwolke. Die Reduktion des Radius der Bounding-Spheres schränkt die Surfel der Zunge zu stark ein. Löcher entstehen. 4.3 Algorithmus 2 Algorithmus 2 wurde entwickelt um zwei negative Eigenschaften von Algorithmus 1 zu verbessern. Algorithmus 1 kann nicht direkt von der Entwicklung in der Mikroprozessorarchitektur ausgehend von großen, monolithischen, hochgetackteten Kernen hin zu vielen parallelen Recheneinheiten profitieren. Der iterative Ansatz von Algorithmus 1 fordert die Fertigstellung eines Surfels nach dem anderen, damit zu jedem Zeitpunkt klar ist, welche Punkte noch nicht repräsentiert werden. Somit wird immer ein nicht repräsentierter Punkt für die Erstellung des nächsten Surfels gewählt, so dass möglichst wenige Surfel erstellt werden. Eine Möglichkeit mit Algorithmus 1 dennoch parallele Rechenwer32 Abbildung 4.5: Darstellung der Zunge der Chameleon-Punktwolke ohne Bounding-SphereEinschränkung. Die Surfel der Zunge werden vollständig dargestellt. ke zu verwenden besteht darin Algorithmus 1 parallel auf Untermengen von Punkten der gesamten Punktwolke eines Modells arbeiten zu lassen. Hierzu kann der Raum, den die Punktwolke einnimmt in mehrere Zellen eingeteilt werden. Auf jeder Zelle wird dann eine Instanz von Algorithmus 1 ausgeführt. Dieser Ansatz ruft aber neue Probleme hervor. Die Einteilung des Raums in Zellen sollte möglichst so geschehen, dass die Bearbeitung jeder Zelle nahezu den gleichen Rechenaufwand erzeugt. Es ist aber a priori nicht vorherzusehen, wie viele Surfel gebraucht werden um die Punkte innerhalb der Zelle anzunähern. Wenn die Punktnachbarschaft bei der Berechnung eines Surfels durch das Surfel selbst gut angenähert wird, werden bedeutend weniger Surfelberechnungen erforderlich sein, als wenn die Nachbarschaften durch die berechneten Surfel schlecht angenähert werden. Weiterhin bedeutet die Berechnung einer guten Zelleneinteilung einen erhöhten Rechenaufwand und wirkt somit dem Zeitvorteil durch die Parallelisierung entgegen. Ein zweites Problem bei der Einteilung des Raumes in Zellen ist die Behandlung der Grenzen zwischen den Zellen. Hier stellen sich die Fragen, zu welcher Zelle Surfel gehören, die die Zellengrenzen schneiden und wie verhindert werden kann, dass an den Grenzen zu viele Surfel berechnet werden. In der Zellennachbarschaft können auch Punkte aus einer Nachbarzelle enthalten sein, wobei die zellenbearbeitenden Threads aus Performanzgründen möglichst nicht miteinander kommunizieren sollten. Daraus folgt, dass Punkte nahe der Zellengrenze durch mehrere Surfel repräsentiert werden, obwohl schon ein einziges ausreichen würde. Im Grenzbereich werden dann zu viele Surfel berechnet. Ein zweiter Nachteil von Algorithmus 1 ist, dass die Anzahl der berechneten Surfel nur indirekt über den Genauigkeitsparameter und die Größe k der Punktnachbarschaft gesteuert werden kann. Auch dieser Nachteil soll mit Algorithmus 2 beseitigt werden. 33 Algorithmus 2 muss, um beide Nachteile zu beseitigen, eine feste Menge X von Position finden, an denen Surfel berechnet werden. Jedes Surfel sollte möglichst gleich viele Punkte der Punktwolke abdecken. So ist zum einen der Aufwand der Berechnungen pro Surfel nahezu konstant und die Berechnungen sind damit besser parallelisierbar. Zum anderen werden Objektregionen mit vielen Details besser abgebildet, als bei einer uniformen Verteilung der Surfel über die Oberfläche der Punktwolke. Voraussetzung hierfür ist, dass die Punktwolke für diese Regionen mehr Punkte beinhaltet als für weniger detaillierte Regionen. Wenn alle Positionen ermittelt wurden kann Algorithmus 2 die Surfel parallel berechnen. 4.3.1 Berechnung der Surfel-Positionen mit LOP Für die Bestimmung der Surfel Positionen reicht eine zufällige Wahl der Positionen aus den Punkten der Punktwolke nicht aus. Es kann dann weder sicher gestellt werden, dass Regionen mit vielen Details auch mit mehr Surfeln abgedeckt werden, noch kann erzwungen werden, dass das gesamte Modell mit Surfeln abgedeckt wird. Bild 4.6 zeigt eine Beispielpunktwolke, bei der die zufällige Wahl der Surfel Positionen zu einer schlechten Abdeckung der Modelloberfläche geführt hat. Abbildung 4.7 zeigt das zugehörige gerenderte Bild. Zufällig aus der Punktwolke gewählten Punkte haben aber auch einen Vorteil. Sie liegen direkt auf der Oberfläche der Punktwolke. Die Punkte müssen nur noch besser mit einem zusätzlichen Algorithmus auf der Oberfläche verteilt werden. Gemäß dem Gesetz von Amdahl [7] hängt der maximale Geschwindigkeitsgewinn durch die Parallelisierung eines Algorithmus sowohl vom seriellen als auch vom parallelisierbaren Programmanteil des Algorithmus ab. Amdahls Gesetz gibt als grobe obere Schranke S für den maximalen Geschwindigkeitsgewinn bei einem seriellen Programmanteil von γ folgende Formel vor: 1 S= (4.1) γ Der Algorithmus zum Festlegen der Surfelpositionen sollte folglich möglichst auch von parallelen Recheneinheiten profitieren, damit die Parallelisierung der Surfelberechnung nicht durch einen zuvor arbeitenden seriellen Algorithmus beeinträchtigt wird. Ein Algorithmus, der den genannten Forderungen gerecht wird, ist der LOP-Algorithmus n o (0) (0) ⊂ (Locally Optimal Projection) [34]. LOP projeziert eine gegebene Menge X = x i i∈I ¦ © R3 von Punkten auf eine andere gegebene Menge von Punkten P = p j j∈J ⊂ R3 , wobei I und J die Mengen der Indizes bezeichnen. In unserem Fall ist P die gesamte Punktwolke und X (0) ist die zufällig gewählte Untermenge X von Punkten der Punktwolke. LOP verwendet zwei Kräfte, um die Punkte X auf die Punktwolke P zu projezieren. Zwei Punkte (0) x i und x n(0) mit i 6= j stoßen sich ab. Diese abstoßende Kraft sorgt dafür, dass sich die Punkte gleichmäßig im Raum verteilen und nicht an einigen Stellen Anhäufungen bilden. Die zweite Kraft geht von den Punkten p j der Punktwolke aus. Die Punkte p j ziehen die (0) (0) Punkte x i an. Die zweite Kraft sorgt somit dafür, dass die projezierten Punkte x i auf der Oberfläche der Punktwolke bleiben und sich auf Grund der ersten Kraft nur auf der Oberfläche und nicht im restlichen dreidimensionalen Raum verteilen. Die Anziehungskraft der (0) Punkte aus P sorgt weiterhin dafür, dass die Punkte x i stärker dorthin gezogen werden, wo mehrere Punkte aus P und somit mehrere Details liegen. 34 Abbildung 4.6: Auswahl der Surfelpositionen für Algorithmus 2. Die Punktwolke stellt 2048 zufällig gewählte Surfelpositionen dar. Die Surfelpositionen wurden aus der Punktwolke des Chameleon-Modells (4594 Punkte) gewählt. Deutlich sichtbar ist die schlechte Wahl der Punkte, die im Bereich des Rückens zu einer lückenhaften Abdeckung des Modells führt. Abbildung 4.7: Visualisierung der Surfelpositionen aus Abbildung 4.6. Ausgehend von den Surfelpositionen wurden Surfel berechnet und mit Hilfe eines Raytracers visualisiert. Der Rücken des Chameleon-Modells zeigt ein großes Loch im Modell, welches aus der schlechten Wahl der Surfelpositionen resultiert. 35 Abbildung 4.8: Verteilung der Surfelpositionen nach zehn Iterationen von LOP. Als Parameter wurden h = 0.3 und mu = 0.25 gewählt. Auf Grund der abstoßenden Kräfte wird der Rücken des Chamäleons von einigen der projezierten Punkte abgedeckt. Abbildung 4.9: Visualisierung der Verteilung der Surfelpositionen nach zehn Iterationen von LOP. Das Loch im Rücken des Chameleon-Modells ist deutlich kleiner geworden. 36 Abbildung 4.10: Verteilung der Surfelpositionen nach 20 Iterationen von LOP. Als Parameter wurden h = 0.3 und mu = 0.25 gewählt. Der Rücken des Chamäleons wird von deutlich mehr projezierten Punkten abgedeckt. Abbildung 4.11: Visualisierung der Verteilung der Surfelpositionen nach 20 Iterationen von LOP. Das Loch im Rücken des Chameleon-Modells ist fast vollständig verschwunden. Es sind noch kleinere Artefakte im Rückenbereich sichtbar, da die dort liegenden Surfel einen größeren Bereich der Oberfläche als beim Rest des Modells abdecken. 37 Analog zur Natur, in der sich von Punktquellen ausgehende Kräfte wie zum Beispiel elektromagnetische Wellen mit zunehmender Entfernung abschwächen, arbeitet LOP. Die Kräfte zwischen den Punkten wirken nur in einem bestimmten Radius um die Punkte. In größerer Entfernung werden die Kräfte vernachlässigt, da ihr Beitrag nicht mehr groß genug ist. Aus dieser Arbeitsweise resultiert auch das „local“ im Namen des Algorithmus. Der Vorteil in der Einschränkung des Wirkungsradius der Kräfte zwischen Punkten liegt in einem stark verringerten Arbeitsaufwand bei der Berechnung der neuen Positionen der (0) Punkte x i auf Grund der Krafteinwirkungen. Die Einführung des Radius erlaubt auch erst eine effektive Bearbeitung von LOP auf parallelen Einheiten, da die Speicherzugriffe stark eingeschränkt werden. Mittels einer räumlichen Datenstruktur wie einem kd-tree können (0) dann die Nachbarn eines Punktes x i , die diesen beeinflussen, schnell gefunden werden. LOP arbeitet in mehreren Iterationen. Daraus folgt, dass der kd-tree für die Punkte aus (0) X für jede Iteration neu berechnet werden muss. In den Abbildung 4.8 und 4.9 ist das Ergebnis von zehn LOP-Iterationen auf die am Anfang des Kapitels gegebene schlechte Verteilung von Surfelpositionen zu sehen. Die Verteilung der projezierten Punkte im Bereich des Rückens ist schon wesentlich besser als ohne LOP-Iterationen jedoch wird der Bereich des Modells immer noch nicht komplett dargestellt. Die Abbildungen 4.10 und 4.11 zeigen das Ergebnis nach 20 LOP-Iterationen. Der Rücken wird nun fast komplett dargestellt. Es treten nur noch kleinere Artefakte bei der Surfelberechnung auf. Ausgehend von einer besseren Anfangsverteilung der Surfelpositionen kann man sagen, dass zehn bis zwanzig LOP-Iterationen ausreichen, um eine gute Verteilung der Surfelpositionen zu erhalten. Das Beispiel dieses Kapitels hat die Arbeit von LOP an einem Extremfall verdeutlicht. Implementierungsdetails Die Berechnung des kd-tree für die LOP-Iterationen findet aus den im Kapitel 2.2 genannten Gründen auf der CPU statt. Als Nachteil erscheint dabei, dass zwischen jeder Iteration die projezierten Punkte aus dem Speicherbereich der OpenCL-Umgebung ausgelesen werden müssen, und dann auch zusätzlich wieder in den Speicherbereich der OpenCL-Umgebung kopiert werden müssen, da der kd-tree-Algorithmus die Reihenfolge der projezierten Punkte im linearen Speicher verändert. Für die Ergebnisse der Arbeit ist diese Vorgehensweise nicht nachteilig, da die Geschwindigkeit der OpenCL-kernel trotzdem gemessen werden kann. Weiterhin ist mittels einer Abschätzung der Performanz eines OpenCL-basierten kd-tree Algorithmus auf Basis der Ergebnisse von Kun-Zhou [53] ein Vergleich der Laufzeit zwischen Algorithmus 1 und Algorithmus 2 möglich. 4.3.2 Paralleles Fitten Das parallele Fitten beinhaltet die gleichen Schritte wie in Algorithmus 1. Es müssen die Nachbarn eines Punktes gesucht, Matrizen berechnet, Eigenvektoren gefunden und Bounding-Spheres festgelegt werden. Diese Schritte sollen an Hand der Ergebnisse des LOP-Algorithmus ausgeführt werden und sich für die Bearbeitung auf parallelen Einheiten eignen. Daraus resultiert, dass die Reihenfolge der Schritte umgestellt werden muss. Algorithmus 2 berechnet zuerst den Radius r der Boundingspheres. Der initiale Radius wird als der Abstand eines projezierten Punktes zu dem k-ten nächsten anderen proje38 zierten Punkt festgelegt. Der Standardwert von k ist in dieser Arbeit drei. Kleinere Werte von k rufen Löcher im Modell hervor, weil sich dann nicht genug Surfel überlappen. Mit der Erhöhung von k werden mehr Punkte der Originalpunktwolke in die Berechnung mit einbezogen. Als Folge dessen können Details verschwinden, die von wenigen Punkten der Wolke dargestellt werden. Nach der Berechnung des Surfels kann der Radius der Bounding-Sphere wie in Algorithmus 1 angepasst werden, um Artefakte zu verringern. Zur Berechnung des nächsten Nachbarn wird wie in Algorithmus 1 in Kapitel 4.2.2 der knn-Algorithmus aus der ANN-Bibliothek verwendet. Mit Hilfe des Radius r werden zu einem projezierten Punkt Ppr o j alle die Punkte der ursprünglichen Punktwolke festgelegt, die zum Surfel des Punktes Ppr o j beitragen sollen. Algorithmus 2 verwendet abweichend von Algorithmus 1 keine festgelegte Anzahl von Nachbarn. Eine feste Anzahl von Nachbarn erfordert eine knn-Suche, welche wiederum als Datenstruktur eine Priority-Queue benötigt. Diese Datenstruktur wird im knn-Algorithmus während der Suche genutzt, um die bisher gefundenen nächsten Nachbarn zu speichern und am Ende des Algorithmus die k nächsten Nachbarn zu beinhalten. Das Problem bei der Priority-Queue und ähnlichen Datenstrukturen besteht im stark datenabhängigen Zugriffsmuster. Dieses Zugriffsmuster beeinträchtigt die parallele Ausführung des knn-Algorithmus in der OpenCL-Umgebung. Kun-Zhou [53] schlägt einen knn-Algorithmus für GPUs vor, der in mehreren Iterationen läuft und keine priority-Queue verwendet. Dieser Algorithmus führt häufige Zugriffe auf lokalen Speicher auf der GPU aus, welcher auf der in der Arbeit verwendeten Hardware nur eingeschränkt verwendbar ist (siehe Kapitel 2.1.3). Die Verteilung der projezierten Punkte durch den LOP-Algorithmus wirkt einer ungleichmäßigen Anzahl von Nachbarn verschiedener projezierter Punkte und somit einer ungleichmäßigen Rechenlast zwischen verschiedenen Surfeln entgegen. Da sich projezierte Punkte vermehrt dort sammeln, wo sich Punkte der Punktwolke häufen, sind in diesen Regionen auch die Bounding-Spheres kleiner, die die Punkte zur Berechnung des Surfels festlegen. Die Größe der Bounding-Sphere passt sich somit der lokalen Punktdichte der Punktwolke an. Als Resultat ist die Anzahl der zur Surfel-Berechnung herangezogenen Punkte nahezu konstant. Algorithmus 2 berechnet die Matrix B parallel zur Nachbarschaftssuche. Die Suche verwendet einen kd-tree über die Punkte der Punktwolke zur Beschleunigung. Der kd-tree wird mit einer Kugel mit dem Mittelpunkt Ppr o j und dem zugehörigen Radius r traversiert. Wenn ein Knoten des kd-tree die Kugel schneidet und Punkte enthält, werden alle Punkte des Knotens gegen die Kugel getestet. Punkte, die innerhalb der Kugel liegen, werden auf die Einträge der Matrix B aufaddiert. Zur Lösung des Eigenvektorproblems wurden verschiedene Algorithmen in der Literatur veröffentlicht. Diese Arbeit untersucht die Householder Tridiagonalisierung mit anschließender QL-Zerlegung, das Jacobi-Verfahren und die inverse Vektoriteration auf ihre Tauglichkeit zur Implementierung auf parallelen Recheneinheiten. Dabei wurde vor allem darauf geachtet, dass sich vorhandene Schleifen entrollen lassen und der Programmfluss eine geringe Datenabhängigkeit aufweist. Das Entrollen der Schleifen führt nicht nur zum Einsparen der Schleifenanweisungen sondern sorgt auch dafür, dass Matrizen, deren Elemente über Schleifenindizes angesprochen werden, in Registern gespeichert werden können. Durch das Entrollen sind die Indizes vor der Ausführung bekannt. Daraus folgt, dass die indizierten Arrayeinträge mit festgelegten Variablennamen in Registern gespeichert werden können. Ist hingegen der Zugriff durch einen Index nötig, weil die Schleife auf Grund von Datenabhängigkeiten nicht entrollbar ist, so muss die Matrix in einem indizierbaren Speicherbereich stehen. In der OpenCL-Umgebung kann für ein indizierbares Array 39 entweder ein Bereich im Grafikkartenspeicher oder OpenCLs Shared-Memory verwendet werden. Da die in der Arbeit verwendete Hardware das Shared-Memory im Grafikkartenspeicher abbildet, bedeuten beide Möglichkeiten einen signifikanten Performanceeinbruch auf Grund der hohen Zugriffslatenzen zum Grafikkartenspeicher. Das Householder-QLVerfahren und das Jacobi-Verfahren werden erfolgreich in newmat verwendet und sollen aus diesem Grund hier untersucht werden. Beiden Verfahren ist gemein, dass sie alle Eigenwerte und zugehörige Eigenvektoren berechnen. Die inverse Vektoriteration verspricht verglichen mit der Berechnung aller Eigenwerte einen Vorteil, da sie direkt den kleinsten Eigenwert und den zugehörigen Eigenvektor berechnen kann. Der Eigenvektor zum kleinsten Eigenwert ist dabei der Vektor, der die Koeffizienten des berechneten Surfels beinhaltet. Householder Tridiagonalisierung und QL-Zerlegung Die Householder-Tridiagonalisierung [51] [38] reduziert eine symmetrische n×n Matrix A auf eine tridiagonale symmetrische Matrix An−1 . Es werden hierzu n − 2 orthogonale Transformationen angewendet. Die Iterationsvorschrift ist wie folgt: Ai+1 = Pi Ai Pi , i = 1, 2, . . . , n − 2, mit 1 Pi = I − ui uiT /H i , H i = uiT ui , 2 1 (i) (i) (i) (i) uiT = al,1 ; al,2 ; . . . ; al,l−2 ; al,l−1 ± σi2 ; 0; . . . ; 0 , l = n − i + 1, σi = l−1 X (i) (al,m )2 m=1 Die Iteration sorgt dafür, dass die Matrix Ai in den letzten i − 1 Zeilen und Spalten tridiagonal ist. Eine tridiagonale Matrix hat nur auf der Diagonalen und den beiden Nebendiagonalen Einträge ungleich Null. Um die Eigenvektoren z1 der Matrix A1 zu berechnen, müssen die Eigenvektoren zn−1 der Matrix An−1 mit dem Produkt der Pi -Matrizen multiplziert werden. z1 = P1 P2 · · · Pn−2 Pn−1 zn−1 Das Produkt der Matrizen Pi muss zwischengespeichert werden bis die Eigenvektoren der Matrix An−1 berechnet wurden. Listing 4.1 zeigt Codeausschnitte der Methode tred2 aus [38], die die HouseholderTridiagonalisierung implementiert, übersetzt in C++-Code. Die Methode enthält eine große Schleife (Zeilen 6 bis 19) in der weitere Schleifen geschachtelt sind. Alle Schleifen können entrollt werden, da die zugehörigen Zählvariablen nicht von den Einträgen der Eingangsmatrix A abhängig sind. Des Weiteren befindet sich in der äußeren Schleife von Zeile 13 bis Zeile 17 ein If-Else-Statement, welches dafür sorgt, dass der Iterationsschritt übersprungen wird, falls die Variable g einen zu kleinen Wert annimmt. Da der If-Zweig nur zwei Anweisungen enthält und im Normalfall nur selten ausgeführt werden sollte, stellt die Verzweigung im Kontrollfluss keinen Nachteil für die parallele Ausführung mit OpenCL dar. 40 Listing 4.1: Ausschnitt der Prozedur tred2 aus [38] zur Berechnung der HouseholderTridiagonalisierung einer symmetrischen Matrix mit den für die Parallelisierung relevanten Stellen void t r e d 2 ( i n t n , f l o a t t o l , f l o a t * a , f l o a t * d , 2 f l o a t * e , f l o a t * z ){ 3 int i , i , k , l ; 4 f l o a t f , g , h , hh ; 5 . . . // K o p i e r e a nach z 6 f o r ( i n t i = n ; i > 1 ; −−i ){ // n−2 T r a n s f o r m a t i o n e n 7 l = i − 2 ; 8 g = 0 ; 9 f o r ( i n t k = 1 ; k <= l ; ++k ){ 10 g = g + z[ i * n + k] * z[ i * n + k ]; 11 } 12 ... 13 i f ( g <= t o l ){ 14 e [ i ] = f ; h = 0; 15 } else { 16 // I t e r a t i o n s s c h r i t t 17 } 18 ... 19 } 20 . . . // Zusammenfassung d e r T r a n s f o r m a t i o n s m a t r i z e n 21 } 1 Die Eigenvektoren und Eigenwerte der Matrix An−1 können mit Hilfe des QL-Verfahrens [9] berechnet werden. Der Algorithmus beruht auf der Beobachtung, dass die Matrix B unitär ähnlich zu der Matrix A ist wenn gilt: A = QL und B = LQ wobei L eine untere Dreiecksmatrix und Q eine unitäre Matrix ist. Daraus folgt, dass die Spalten der möglicherweise komplexen Matrix Q orthonormal zueinander sind, alT so U ∗ U = I und U ∗ = U gilt. Sind die Bedingungen gegeben kann man B auch mit A ausdrücken: B = LQ = Q H AQ Wenn die obere Gleichung mehrmals angewendet wird, entsteht eine Folge von Matrizen, die unitär ähnlich zu der Matrix A sind. Es gilt: AS = Q S L S AS+1 = LS Q S = Q HS AS Q S Mit steigender Anzahl von Iterationen tendiert die Matrix AS zu einer untereren Dreiecksmatrix. Bei der Matrix A∞ , die eine untere Dreiecksmatrix ist, stehen dann auf der Diagonalen die Eigenwerte die gleichzeitig die Eigenwerte von A1 und somit der Matrix A sind. Wenn die Matrix AS symmetrisch und tridiagonal ist vereinfachen sich die Berechnungen der Matrizen Q S . Aus diesem Grund sollte auf eine symmetrische Matrix, wie sie 41 bei der Berechnung des Surfels entsteht, die Householder-Tridiagonalisierung angewendet werden. Die Matrix Q S ist unter der Voraussetzung der Eigenschaften „symmetrisch“ (S) und „tridiagonal“ der Matrix AS ein Produkt von Rotationsmatrizen Pi , i ∈ [n − 1; 1]. (S) Jede Matrix Pi wird so geformt, dass das Element der Matrix AS an der Stelle (i, i + 1) eliminiert wird. Es gilt (S) (S) (S) Q S = P1 P2 · · · Pn−1 Ein weiterer Vorteil der Tridiagonalität der Eingangsmatrix A liegt in der Möglichkeit den QL-Algorithmus, abhängig von den Einträgen von A, während der Iterationen auf die Berechnung von Eigenwerten kleinerer Submatrizen aufzuspalten. Dadurch kann die Berechnung stark vereinfacht und somit beschleunigt werden. Listing 4.2: Ausschnitt der Prozedur tql2 aus [9] zur Berechnung der Eigenwerte und Eigenvektoren einer symmetrischen Tridiagonalmatrix mit den für die Parallelisierung relevanten Stellen bool t q l 2 ( i n t n , f l o a t macheps , f l o a t * d , 2 f l o a t * e , f l o a t * z ){ 3 i n t i , i , k , l , m; 4 float b , c , f , g , h, p , r , s ; 5 . . . // V e r s c h i e b e Werte von e e i n s nach vorn 6 e [n] = b = f = 0; 7 f o r ( i n t l = 1 ; l <= n ; ++l ){ // n E i g e n w e r t e 8 h = macheps * ( abs ( d [ l ] ) + abs ( e [ l ] ) ) ; 9 b = max( b , h ) ; 10 f o r (m = l ; m <= n ; ++m){ 11 i f ( abs ( e [m] ) <= b ) break ; 12 } 13 i f (m != l { 14 f o r ( j = 0 ; j < 30; ++j ){ 15 . . . // B e r e c h n e t S h i f t 16 p = d [m] ; 17 c = 1; s = 0; 18 f o r ( i = m−1; i >= l ; −−i ){ 19 . . . //QL−T r a n s f o r m a t i o n 20 } 21 ... 22 i f ( abs ( e [ l ] ) <= b ) break ; 23 } 24 } 25 ... 26 } 27 . . . // S o r t i e r u n g d e r E i g e n w e r t e und E i g e n v e k t o r e n 28 } 1 Die Auflistung der wichtigsten Code-Zeilen des QL-Algorithmus aus [9] in Listing 4.2 zeigt, dass der QL-Algorithmus für die Implementierung auf der OpenCL-Umgebung mit der verwendeten Hardware schlecht geeignet ist. Der Algorithmus führt in einer äußeren Schleife 42 (Zeile 7 bis 26) die Berechnung der n Eigenwerte und Eigenvektoren aus. Für jeden dieser n Durchläufe werden maximal 30 Iterationen des QL-Algorithmus durchgeführt (Zeile 14 bis 23). Auch wenn die Iterationsschleife mit ihren 30 Durchläufen nicht komplett entrollt werden kann, da die Anzahl der Durchläufe zu hoch ist, stellt der verwendete Index j kein Problem für die Parallelisierung dar. Der Index j wird weder für einen lesenden noch einen schreibenden Zugriff in ein Array verwendet. Problematisch ist die Verwendung der Variablen m. In der Schleife von Zeile 10 bis 12 wird der Wert für die Variable m ermittelt. Der irreguläre Schleifenabbruch durch die break-Anweisung in Zeile 11 ist nicht problematisch, da der Wert von n bei den bearbeiteten fünfzeiligen Matrizen fünf ist und die Schleife somit komplett entrollt werden kann. Es muss nach dem Entrollen nur sichergestellt werden, dass m den kleinsten Wert annimmt, für den die Bedingung in Zeile 11 wahr wird. Das eigentliche Problem für die Parallelisierung besteht in der weiteren Verwendung der Variablen m, deren Wert von den Einträgen im Array e abhängt. In Zeile 16 wird abhängig vom Wert von m und somit abhänging von den Eingabewerten der Prozedur aus dem Array d gelesen. Daraus resultiert, dass das Array d in einer parallelen Umsetzung als echtes Array vorhanden sein muss, da der Zugriff auf die einzelnen Speicherbereiche nicht a priori feststeht. Weiterhin ist die Anzahl der Durchläufe der Schleife in Zeile 18 abhängig von m. Diese Schleife kann somit nicht entrollt werden. Da innerhalb der Schleife die QL Transformation abhängig von der Schleifenzählvariable zusätzlich zum Array e auf die Arrays d und z zugreift, können auch diese Arrays nicht über Register abgebildet werden und müssen als echte indexierbare Speicherbereiche vorliegen. Die QL-Transformation verursacht somit einen signifikanten Zugriff auf den Grafikkartenspeicher und daraus folgend einen starken Einfluss der Latenzen des Zugriffs auf die Performanz des Algorithmus. Jacobi-Verfahren Das Jacobi-Verfahren zur Berechnung der Eigenwerte und Eigenvektoren einer symmetrischen n × n Matrix A wurde 1845/46 von C.G.J. Jacobi [31] entwickelt. Die Idee des Verfahrens besteht darin, durch orthogonale Transformationen T die Matrix A auf Diagonalform zu bringen. Auf der Diagonalen der so transformierten Matrix A D stehen dann die Eigenwerte der Matrix A. Bei den Transformationen handelt es sich um Rotationsmatrizen, die eine Drehung in der ei e j -Ebene durchführen, um das Nicht-Diagonalelement an der Stelle (i, j) der Matrix A zu eliminieren. .. 1 . . . . . . . s . . . .. T = . . . . −s . . . c . . . .. .. . . 1 .. . c .. . (4.2) Formel 4.2 zeigt die Form dieser Rotationsmatrizen, die sich nur an den Stellen (i, i), (i, j), ( j, i) und ( j, j) von der Einheitsmatrix unterscheiden. Die Einträge c und s in der Matrix stehen entsprechend für Cosinus und Sinus über einen Winkel θ , der dafür sorgt, dass das 43 Element an der Stelle (i, j) der Matrix A verschwindet. Die Berechnung von c und s aus den Einträgen der Matrix A wird wie folgt durchgeführt: θ= a j j − aii 2ai j 1 t= p |θ | 1 + θ 2 1 c=p t2 + 1 s = ct (4.3) (4.4) (4.5) (4.6) Werden die Transformationen gleichzeitig auf die n × n Einheitsmatrix I angewendet, so enthält die zur Diagonalmatrix A D gehörende transformierte Einheitsmatrix I D in ihren Zeilen die Eigenvektoren v iT zum Eigenwert aii der Matrix A D in der i-ten Zeile. Das klassische Jacobi-Verfahren sucht sukzessive den betragsmäßig größten Nicht-Diagonaleintrag und eliminiert diesen zuerst, bis alle Nicht-Diagonaleinträge nahezu Null sind. Da die Suche nach dem betragsmäßig größten Nicht-Diagonalelement einen irregulären Kontrollfluss für verschiedene Threads der OpenCL-Umgebung hervorrufen kann, wird in dieser Arbeit das zyklische Jacobi-Verfahren verwendet. Das zyklische Jacobi-Verfahren iteriert nacheinander über alle Nicht-Diagonalelemente. Für die Elemente, die ungleich Null sind, werden während des Zyklus die Transformationen ausgeführt. Die aufeinander folgenden Transformationen für verschiedene Diagonalelemente der Matrix A können dafür sorgen, dass Nicht-Diagonalelemente, die schon eliminiert wurden, wieder einen Wert ungleich Null annehmen. Um diesem Verhalten entgegenzuwirken, wird das Jacobi-Verfahren in mehreren Iterationen durchgeführt. Die Betrachtung der Implementierung des Jacobi-Verfahrens in Listing 4.3 zeigt, dass der Algorithmus gut für die Implementierung in der OpenCL-Umgebung geeignet ist. Die Schleifen in Zeile 6 und Zeile 7 bilden den Zyklus über alle Nicht-Diagonalelemente der Matrix A ab. Da in dieser Arbeit nur 5 × 5 Matrizen verwendet werden, wiederholt sich der Inhalt der beiden Schleifen beim kompletten Entrollen nur zehn mal. Listing 4.3: Ausschnitt der Implementierung des Jacobi-Verfahrens aus [39] zur Berechnung der Eigenwerte und Eigenvektoren einer symmetrischen Matrix mit den für die Parallelisierung relevanten Stellen void J a c o b i ( f l o a t * i n M a t r i x , i n t n , f l o a t * e v e c t o r s ){ 2 f l o a t s = 1.0 f , r , a , b , c , d , e ; 3 i n t m = n−1; 4 while ( s != 0 . 0 f ){ // I t e r a t i o n s s c h l e i f e 5 s = 0.0 f ; 6 f o r ( i n t p = 0 ; p < m; ++p ){ 7 f o r ( i n t q = p+1 ; q < n ; ++q ){ 8 a = i n M a t r i x [ p*n + p ] ; 9 r = i n M a t r i x [ p*n + q ] ; 10 b = i n M a t r i x [ q*n + q ] ; 11 i f ( f a b s ( r)+ f a b s ( a ) <= f a b s ( a ) && 12 f a b s ( r)+ f a b s ( b ) <= f a b s ( b ) ) { 1 44 r = 0.0 f ; } else { . . . // B e r e c h n e t , s und c wie i n G l e i c h u n g 4.2 i n M a t r i x [ p*n + q ] = 0 . 0 f ; i n M a t r i x [ p*n + p ] = a − t * r ; i n M a t r i x [ q*n + q ] = b + t * r ; f o r ( i n t i = 0 ; i < p ; ++i ){ r o t a t e ( inMatrix , n , i , q , i , p , c , s ) ; } f o r ( i n t k = p+1; k < q ; ++k ){ r o t a t e ( inMatrix , n , k , q , p , k , c , s ) ; } f o r ( i n t j = q+1; j < n ; ++j ){ r o t a t e ( inMatrix , n , q , j , p , j , c , s ) ; } f o r ( i n t j = 0 ; j < n ; ++j ){ rotate ( evectors , n , q , j , p , j , c , s ); } } s = s + fabs ( r ); 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 } 33 } 34 35 } } 37 void r o t a t e ( f l o a t * i n M a t r i x , int n , int u , int v , 38 i n t w, i n t x , f l o a t c , f l o a t s ){ 39 f l o a t arot , brot ; 40 brot = inMatrix [u * n + v ] ; 41 a r o t = i n M a t r i x [w * n + x ] ; 42 i n M a t r i x [w * n + x ] = c * a r o t − s * b r o t ; 43 inMatrix [u * n + v ] = s * arot + c * brot ; 44 } 36 Der Algorithmus arbeitet nur auf dem oberen Dreieck einschließlich der Diagonale der Matrix A. Das Entrollen der beiden Schleifen ist sinnvoll durchführbar, da der enthaltene Programmcode kurz ist und die durchlaufenen Indizes der Schleifen nicht von den Daten der Matrix A abhängen. Auch die Schleifen in den Zeilen 19, 22 und 25 zur Transformation der Matrix A (inMatrix) und die Schleife in Zeile 28, die die Transformation auf die Eigenvektormatrix anwendet, können entrollt werden, da sie nur von den Zählvariablen des Zyklus abhängen (p und q in den Zeilen 6 und 7). Da alle Schleifen, die Indizes in die Arrays inM at r i x und ev ec t ors erzeugen, entrollbar sind, können die Arrays über schnell zugreifbare Register abgebildet werden. Zur endgültigen Aussage müssen noch die If-Else-Anweisung (Zeilen 12 und 14) und die Iterationsschleife (Zeile 4) betrachtet werden. Der If-Zweig ist sehr kurz. Im schlimmsten Fall führt die If-Else-Anweisung hauptsächlich dazu, dass einige Threads, die parallel an der jeweiligen Lösung des JacobiVerfahrens arbeiten, auf andere Threads warten, bei denen ein zuvor eliminiertes Element durch spätere Transformationen wieder Null wird. Die wartenden Threads führen dabei 45 nur den kurzen If-Zweig aus. Diese Situation tritt frühestens im zweiten Zyklus des JacobiVerfahrens ein. Der Einfluss auf die Performanz hängt hauptsächlich von der Anzahl der Zyklen und der Häufigkeit des Auftretens der Wertänderungen der Nicht-Diagonalelemente ab. Die Iterationsschleife ist nur dann problematisch, wenn einige parallel bearbeitete Matrizen wesentlich mehr Iterationen zur Berechnung der Eigenwerte benötigen als andere Matrizen. Sowohl der Einfluss der If-Else-Anweisung als auch der Einfluss der Iterationsschleife hängen also vom Konvergenzverhalten des zyklischen Jacobi-Verfahrens ab. In [48] wird gezeigt, dass das Jacobi-Verfahren meistens quadratische und mindestens lineare Konvergenz aufweist und somit der Einfluss beider Anweisungen nicht zu stark in Erscheinung tritt. Es werden nur wenige Iterationsschritte benötigt, damit das zyklische Jacobi-Verfahren konvergiert. Inverse Vektor Iteration Die inverse Vektoriteration verspricht verglichen mit dem QL-Verfahren inklusive Householder-Tridiagonalisierung oder dem Jacobi-Verfahren weniger Berechnungsaufwand, da die inverse Vektoriteration nur den benötigten Eigenvektor und den zugehörigen Eigenwert berechnet. Die inverse Vektoriteration basiert auf der Vektoriteration von Mises [40]. Die Iterationsvorschrift berechnet auf Grundlage eines beliebigen Vektors iterativ den betragsmäßig größten Eigenwert λ1 und den zugehörigen Eigenvektor e1 . Der Startvektor y (0) wird auf folgende Weise definiert x (0) = n X α j x j ; α1 6= 0 (4.7) j=1 x (0) y (0) = x (0) (4.8) 2 Die Iterationsvorschrift ist x (m) = A(m−1) y y (m) (4.9) σm x (m) = x (m) (4.10) 2 wobei σm so gewählt wird, dass gilt 0 ≤ y (m)T y (m−1) (4.11) Wenn die Matrix A diagonalisierbar ist und λ1 dominierend, also der betragsmäßig größte Eigenwert ist, dann verhält sich die Iteration für eine steigende Anzahl m von Schritten für m → ∞ wie folgt x (m) → λ 1 2 y (m) → e1 σm → si gn(λ1 ) 46 (4.12) (4.13) (4.14) Aus dem Betrag von x (m) und dem Vorzeichen von σm kann der Eigenwert λ1 berechnet werden. Der zugehörige Eigenvektor e1 ist y (m) . Algorithmus 2 benötigt zur Berechnung des Surfels aber nicht den Eigenvektor zum betragsmäßig größten Eigenwert, sondern den Vektor zum betragsmäßig kleinsten Eigenwert. Die inverse Vektoriteration kann genau diesen Eigenwert berechnen. Mit Hilfe der Eigenwerte λ1 der Matrix A−1 können die Eigenwerte λ der Matrix A berechnet werden. Wenn λmin der betragsmäßig kleinste Eigenwert zur Matrix A ist, dann ist λ 1 der betragsmäßig größte Eigenwert von A−1 . Wenn die min Iteration von Mises auf A−1 angewendet wird, berechnet sie den Kehrwert vom betragsmäßig kleinsten Eigenwert von A. Die Iterationsvorschrift ändert sich dann zu x (m) = A−1 y (m−1) (4.15) oder Ax (m) = y (m−1) (4.16) Zum Lösen des Iterationsschritts muss entweder die Matrix A invertiert werden oder ein lineares Gleichungssystem gelöst werden. Das Invertieren einer Matrix zeigt oft Stabilitätsprobleme in der Berechnung auf einem Computer. Das Lösen des Gleichungssystems ist hier vorzuziehen. Die Lösung des Gleichungssystem kann mit einer LR-Zerlegung der Matrix A mit Zeilenpivotisierung und anschließendem Vorwärtseinsetzen und Rückwärtseinsetzen erfolgen. Es gilt PA = LR (4.17) mit einer unteren Dreiecksmatrix L mit lauter Einsen auf der Diagonalen, einer oberen Dreiecksmatrix R und einer Permutationsmatrix P. Das Vorwärtseinsetzen erfolgt dann durch Ly = Pb (4.18) (4.19) und das Rückwärtseinsetzen durch Rx = y (4.20) Die Berechnung der LR-Zerlegung hat einen Aufwand von O(n3 ), sie muss aber nur einmal zu Anfang des Algorithmus berechnet werden. Der Aufwand des Vorwärts- und Rückwärtseinsetzens ist O(n2 ). Die inverse Vektoriteration ist also aufwändiger als das Berechnungsergebnis zuerst vermuten lässt. Zusätzlich stellen die Zeilenvertauschungen auf Grund der Spaltenpivotisierung bei der LR-Zerlegung ein Problem dar. In einem Array können die Zeilen mit Hilfe von Indizes relativ einfach ausgetauscht werden. Weiterhin ist die Permutation der Einträge des Vektors b beim Vorwärtseinsetzen 4.19 einfach, wenn die Einträge von b indizierbar sind. Der Nachteil ist dann, dass die Arrays in der OpenCL-Umgebung in den langsamen Grafikspeicher ausgelagert werden müssen und die Daten nicht über schnelle Register zur Verfügung stehen. Die Zeilen der Matrix A könnten auch in Registern gespeichert werden. Dies ist bei einer 5 × 5 Matrix gerade noch vertretbar. Dann muss die Zeilenvertauschung aber über Kontrollstrukturen umgesetzt werden, welche einen irregulären, datenabhängigen Programmfluss erzeugen und somit der Performanz entgegen wirken. Das Vorwärts- und Rückwärtseinsetzen kann bis auf die Multiplikation von b mit der Permutationsmatrix P gut in der OpenCL-Umgebung umgesetzt werden. 47 Auswahl eines Verfahrens zur Lösung des Eigenwertproblems Zusammemfassend ist die Householder-Tridiagonalisierung mit anschließendem QLVerfahren schlecht geeignet um in der OpenCL-Umgebung auf parallelen Recheneinheiten umgesetzt zu werden. Die Vorteile, die das QL-Verfahren auf einer CPU aus der Reduktion der Berechnungen auf kleinere Matrizen abhängig von den Daten der Matrix A zieht, werden auf einer Grafikkarte nicht vorhanden sein. Wahrscheinlich wird der irreguläre Kontrollfluss die Performanz sogar negativ beeinflussen. Die inverse Vektoriteration ist nicht so einfach umsetzbar, wie es die Betrachtung der Iteration für den betragsmäßig größten Eigenwert vermuten lässt. Insbesondere scheint die obere Schranke von O(n3 ) für die Erstellung der LR-Zerlegung die anfangs angenommenen Effizienzvorteile zu widerlegen. Sowohl der Algorithmus zur inversen Vektoriteration als auch das QL-Verfahren hängen weiterhin zu stark von der Verfügbarkeit von indizierbaren Speicherbereichen ab, als dass sie für eine Implementierung auf der vorhandenen Hardware als geeignet erscheinen. Das Jacobi-Verfahren zeigt hingegen keine Abhängigkeit von Arrays auf. Aus diesem Grund und der Einfachheit seiner Implementierung soll es in dieser Arbeit zur Lösung des Eigenwertproblems bei der Berechnung von Surfeln verwendet werden. 48 5 Raytracing Dieses Kapitel erläutert alle Vorgänge, die zur Berechnung eines Bildes ausgehend von einem Surfelmodell durchgeführt werden. Es beginnt mit einer Zusammenfassung des gesamten Raytracing-Algorithmus. In den nachfolgenden Teilkapiteln werden dann einzelne Aspekte des Algorithmus näher behandelt. Beim Raytracing soll der nächste Schnittpunkt eines Strahls von der Kamera durch einen Bildpunkt mit einer Oberfläche im Raum ermittelt werden. Während die Kamerapositionierung und die Berechnung der Eckpunkte des Viewfrustums auf dem Hauptprozessor erfolgt, werden die restlichen Berechnungen in der OpenCL-Umgebung durchgeführt. Der Raytracing-Algorithmus für einen Pixel läuft wie folgt ab. 1. Berechne den Strahl durch den Pixel 2. Iteriere über die Surfel der Szene a) Schneide die Bounding-Sphere des Surfels b) Teste das Surfel auf einen Schnitt mit dem Strahl c) Berechne die Schnittpunkte zwischen Strahl und Surfel d) Teste die Schnittpunkte gegen die Bounding-Sphere e) Teste die Schnittpunkte gegen einen vorher gefundenen nächsten Schnittpunkt 3. Interpoliere die Oberflächenposition 4. Berechne die Beleuchtung für einen gefundenen Schnittpunkt Die Schritte 2a und 2b sind so gestaltet, dass sie die restlichen Schritte in der Iteration für das aktuell betrachtete Surfel überspringen. Die Tests in den Schritten 2d und 2e werden für beide Schnittpunkte ausgeführt. Die Testergebnisse eines Schnittpunktes ergeben erst zusammen betrachtet ein Entscheidungskriterium, ob der aktuelle zum Strahlursprung nächste Punkt durch den getesteten neuen Schnittpunkt ersetzt wird. Da die Tests nacheinander für beide Schnittpunkte durchgeführt werden, kann es passieren, dass in einem Iterationsschritt, der nächste Schnittpunkt zweimal vergeben wird und der erste durch den zweiten Schnittpunkt ersetzt wird. Die restlichen Einzelheiten zu den Schritten des Algorithmus befinden sich in den jeweiligen Kapiteln. 5.1 Raytracing mit Geometrischer Algebra Die nachfolgenden Kapitel beschreiben die Umsetzung des Raytracing-Algorithmus mit Geometrischer Algebra. 49 5.1.1 Berechnung der Strahlen Als Basis zur Berechnung der Strahlen dient das sogenannte Viewingfrustum, welches auf dem Hauptprozessor berechnet wird. Es ist in dieser Anwendung eine Pyramide mit rechteckiger Grundfläche. Die Ecken der Grundfläche werden von den Ecken des zu berechnenden Bildes auf der Bildebene festgelegt. Die Spitze der Pyramide liegt in der Kamera. Die Berechnung der Eckpunkte des Frustums wird mit Hilfe der OpenGL-Methode gluUnProject(x,y,z,modelview,projection,viewport,&a,&b,&c) durchgeführt. Die Variablen modelview, projection und viewport stellen dabei die entsprechenden OpenGL Matrizen zur Manipulation der OpenGL-Kamera dar. Die Variablen x, y und z sind die Standardpositionen eines Frustumeckpunktes, wenn die OpenGL-Kamera nicht bewegt wurde. Die durch die Kamerabewegung veränderten Koordinaten des Frustums befinden sich nach dem Methodenaufruf in den Variablen a, b und c. Die Methode muss für jeden Frustumeckpunkt einzeln aufgerufen werden. Die neuberechneten Koordinaten werden an die OpenCL-Umgebung weiter gegeben. Eine Berechnung auf dem Hauptprozessor ist sinnvoll, da das Frustum pro Bild für alle Pixel gleich ist und somit eine Berechnung pro Pixel einen erhöhten Rechenaufwand bedeutet der keine Vorteile bringt. Der Strahl im Raytracing-Algorithmus soll die Kamera und das aktuelle Pixel schneiden. Die Kameraposition wird durch die Spitze des Viewingfrustums festgelegt. Die Position des Pixels im Raum kann aus den Eckpunkten des Bildes, die die restlichen vier Ecken des Frustums darstellen, interpoliert werden. Es wird hier eine bilineare Interpolation verwendet. Ausgehend von den oberen beiden Eckpunkten des Bildes OL und OR, wobei OL für „oben links“ steht, und den unteren beiden Eckpunkten UL und UR werden zwei neue Punkte auf dem oberen und unteren Rand des Bildes berechnet. α= x pos Br ei t e P1 = (1 − α) ∗ OL + α ∗ OR P2 = (1 − α) ∗ OL + α ∗ OR (5.1) (5.2) (5.3) Die Variable xpos gibt hierbei den Index in x-Richtung auf dem Bild des aktuellen Pixels an und die Variable Breite gibt die Breite des Bildes in Pixeln an. Entsprechend gibt es eine Variable ypos für den Index des aktuellen Bildpunkts in y-Richtung und eine Variable Hoehe für die Höhe des Bildes in Pixeln. Im zweiten Schritt der bilinearen Interpolation wird aus den Punkten P1 und P2 der gesuchte Punkt PP i x el berechnet. β= PP i x el y pos H oehe = (1 − β) ∗ P1 + β ∗ P2 (5.4) (5.5) Die Interpolation wird sowohl im GA-Algorithmus als auch im LA-Algorithmus mit dreidimensionalen euklidischen Punkten durchgeführt. Eine Interpolation von konformen Punkten 3.4.2 der Geometrischen Algebra würde zum falschen Ergebnis führen. Dies ist insbesondere am Koeffizienten von ei n f t y zu erkennen. Der erste Schritt der bilinearen Interpolation der konformen Punkte führt auf p∞ = 50 1 (OL 2x + OL 2y + OLz2 ) ∗ (1 − α) + (OR2x + OR2y + OR2z ) ∗ α 2 (5.6) während der Koeffizient der konformen Projektion des interpolierten euklidischen Punktes wie folgt berechnet wird p∞ = 1 2 (1 − α) ∗ OL x + α ∗ OR x 2 2 1 2 + (1 − α) ∗ OL y + α ∗ OR y + (1 − α) ∗ OLz + α ∗ ORz 2 (5.7) Formel 5.7 zeigt deutlich, dass die konforme Projektion des interpolierten Punktes andere Summanden hervorbringt als sie in Formel 5.6 vorkommen. Insbesondere ist der Parameter α in Formel 5.7 in quadrierter Form enthalten. Aus der Position der Kamera und der konformen Projektion des interpolierten Punktes wird dann die Gerade LS t r ahl gebildet die den Strahl darstellt. LS t r ahl = ∗ (PKamer a ∧ PP i x el ∧ e∞ ) Als Strahlursprung wird im weiteren Algorithmenverlauf die Kameraposition verwendet. 5.1.2 Iteration über die Surfel der Szene Es wurden zwei verschiedene Versionen der Iteration im Raytracing-Algorithmus implementiert. Beide Versionen sind für die GA-Version und LA-Version des RaytracingAlgorithmus gleich. Für den reinen Geschwindigkeitsvergleich beim Raytracing zwischen Geometrischer Algebra und Linearer Algebra wurde ein Brute-Force-Algorithmus implementiert. Es wird mit diesem Algorithmus für jedes Pixel einfach über alle Surfel einer Szene iteriert. Somit trägt der Iterationsalgorithmus am wenigsten zur Ausführungszeit des Raytracing-Algorithmus bei. Da eine Iteration über alle Surfel für große Szenen eine sehr schlechte Performance zeigt und die Geschwindigkeit des in dieser Arbeit neu vorgestellten Algorithmus auch unter wirklichkeitsgetreuen Bedingungen getestet werden soll, wurde zusätzlich die Traversierung eines kd-tree implementiert. Räumliche Datenstrukturen wie ein kd-tree (siehe Kapitel 2.2) werden häufig im Raytracing eingesetzt und erlauben erst interaktive Bildberechnungen. Anpassung des kd-trees Der spezielle Aufbau der Szene aus Surfeln erfordert eine Modifikation des kd-trees aus Kapitel 2.2. Abbildung 5.1 zeigt in 2D ein Beispiel bei dem es mit dem gewöhnlichen kdtree zu Problemen mit dem Surfelmodell kommt. Die Surfel werden durch einen Kreis mit umzogenen Quadrat dargestellt. Der Kreis steht für die Bounding-Sphere des Surfels und das Quadrat stellt die zugehörige Axis-Aligned-Bounding-Box (AABB), den an den Achsen des Koordinatensystems ausgerichteten kleinsten umschließenden Quader, dar. Der kdtree-Knoten soll quer zu seiner Längsachse in zwei Kindknoten geteilt werden. Zwei der im Vaterknoten enthaltenen Surfel liegen mit ihrer AABB auf der Trennlinie (Trennebene in 3D) der beiden Kindknoten. Im Fall eines kd-tree für Dreiecke könnten die Dreiecke in kleinere Dreiecke so aufgeteilt werden, dass kein Dreieck mehr über die Trennlinie ragt. Die Trennung eines Surfels ist nicht möglich. Aus diesem Grund muss das Surfel entweder 51 Abbildung 5.1: Aufteilung des Vaterknotens eines kd-trees in zwei Kindknoten. Die zwei Objekte auf der Trennlinie müssen entweder im Vaterknoten verankert oder in den Kindknoten dupliziert werden Abbildung 5.2: Lösung des Problems der Überlappung von Trennlinie und Objekten beim Teilen eines kd-tree Knotens. Die Kindknoten werden so erweitert, dass alle Objekte genau einem Kindknoten zugewiesen werden können. 52 dem Vaterknoten oder jedem Kindknoten zugewiesen werden, damit es in jedem Fall unabhängig von der Traversierung des kd-trees beachtet wird. Das Verankern im Vaterknoten hat den Nachteil, dass nahe der Wurzel schon viele Objekte auftauchen können. Daraus folgt ein erhöhter Rechenaufwand, denn diese Objekte nahe der Wurzel müssen für alle Traversierungen des kd-trees zu einem Blattknoten beachtet werden. Wenn die Objekte hingegen dupliziert werden, um sie beiden Kindknoten zuzuweisen, kann ein erhöhter Rechenaufwand entstehen. Immer dann, wenn beide Kindknoten traversiert werden, wird das duplizierte Objekt zweimal in die Berechnungen über die Objekte der Knoten mit einbezogen. Da das duplizierte Surfel bei der Teilung der Kindknoten wieder eine Trennlinie schneiden kann und dann wieder dupliziert werden muß, kann der Rechneaufwand auf Grund von mehrfachen Duplizierungen ausgehend von der Wurzel des kd-trees stark anwachsen. Eine Modifikation des kd-trees, die das Problem der Überlappung von Trennlinie und Objekten angeht, zeigt Abbildung 5.2. Die Boxen der Kindknoten werden so erweitert, dass alle Objekte genau einem Kindknoten zugewiesen werden können. Immer dann, wenn ein Objekt mit dem größeren Anteil seiner Fläche auf einer Seite der Trennlinie liegt, wird das Objekt dem Kindknoten auf dieser Seite der Trennlinie zugewiesen. Tritt der Fall auf, dass die Objektmitte genau auf der Trennlinie liegt, wird es immer einem vorher bestimmten Knoten, also immer dem linken oder immer dem rechten Kindknoten, zugewiesen. Nachdem alle Objekte den beiden Kindknoten zugewiesen wurden, wird die Box der Kindknoten so angepasst, dass alle Objekte vom jeweiligen Kindknoten umschlossen werden. Da sich die Kindknoten dadurch überschneiden können, ist der resultierende Baum nach Definition kein kd-tree mehr. Die Überlappung der Kindknoten muss im Traversierungsalgorithmus des Baums beachtet werden. So funktioniert der stapel-freie Algorithmus aus [16] nach dieser Modifikation des kd-trees in manchen Fällen nicht mehr. Auf Grund der Modifikation wächst auch der Speicherbedarf des Vaterknotens. Reicht es im gewöhnlichen kd-tree eine Koordinate für die Trennlinie zu speichern, so müssen jetzt für jeden der Kindknoten die Koordinaten der „Trennlinien“ gespeichert werden. In dieser Arbeit wird noch eine weitere Modifikation am kd-tree vorgenommen. Gerade nahe der Wurzel treten häufig Kindknoten auf, die auf Grund der Häufung der enthaltenen Surfel an wenigen Raumpositionen sehr große Freiräume haben, in denen sich keine Surfel befinden. Trifft ein Strahl einen solchen Knoten im Freiraum, so müssen alle Schnitttests für diesen Knoten und seine Kindknoten durchlaufen werden, nur um dann in einem Blattknoten zu bemerken, dass gar kein Surfel getroffen wurde. Der kd-tree wird deshalb so modifiziert, dass die AABB der Kindknoten auch die AABB der enthaltenen Objekte darstellt. Abbildung 5.3 zeigt die Auswirkungen auf die Kindknoten aus Abbildung 5.2. Das Entfernen des „Freiraums“ erfordert eine weitere Speichererhöhung für die Knoten, da jetzt die Information für die vollständige AABB der Knoten gespeichert werden muss. Es reicht hierzu die gegenüberliegenden Ecken der AABB zu speichern. Die erhöhte Speicherbandbreite zum Laden der Knotendaten wird durch das Entfernen des „Freiraums“ teilweise kompensiert, da für die Traversierung des kd-trees an diesen Stellen nicht mehr alle Kindknoten bis zu einem Blatt verfolgt werden müssen. 53 Abbildung 5.3: Entfernung des „Freiraums“ bei der Aufteilung eines kd-tree-Knotens in Kindknoten. Die AABB der Kindknoten ist die AABB der enthaltenen Objekte. Traversierungsdetails Der Traversierungsalgorithmus für den beim Raytracing verwendeten kd-tree wurde mit einem Stack implementiert. Die OpenCL-Umgebung ermöglicht es den Stack im SharedMemory abzubilden. Dieser Speicherbereich ist indizierbar und somit gut für die Verwendung mit einem Stackpointer geeignet. Neben der Verwendung des Shared-Memory wurde auch ein Stack basierend auf int4-Registern implementiert. Dieser Schritt wurde bereits gemacht, als die OpenCL-Umgebung vom Hardwarehersteller noch nicht verfügbar war. Da der Shared-Memory-Stack mit den aktuellen Treibern anscheinend nicht immer richtig funktioniert und auf Grund der Hardwareeinschränkungen bzgl. des Shared-Memory auch nicht schneller war, als der Register-Stack, wird für die Ergebnisse der Arbeit der RegisterStack verwendet. Da es in der OpenCL-Umgebung keine Pointer auf Register gibt und die Register nicht über einen Index ansprechbar sind, erfordert die Implementierung des Stacks mit Registern Kontrollstrukturen. Der Stack-Pointer ist eigentlich nur eine int-Variable die den Index angibt, der in einem echten Array zum richtigen Speicherbereich führen würde. Mit Hilfe der Kontrollstrukturen kann aber auch abhängig vom Stack-Pointer aus dem richtigen Register gelesen werden. Kontrollstrukturen kosten aber Rechenzeit und führen bei der Verwendung von If-Else-Kaskaden auf der GPU zu starken Performance-Verlusten. Die erste Version des Register-Stacks wurde mit einer If-Else-Kaskade implementiert. Wenn die Größe des Stacks a priori auf eine Zweierpotenz festgelegt ist, kommt der Algorithmus bei einer Stackgröße von 2n mit n Vergleichen aus, um aus dem richtigen Register zu le54 Abbildung 5.4: Entscheidungsbaum für das Lesen aus einem Registerstack. Der Stack hat eine maximale Größe von acht Einträgen. Mittels des Stack index i wird aus dem Stack gelesen. Die Abbildung zeigt den durchlaufenen Zweig des Baumes für eine Belegung von i mit dem Wert 3. sen. Die If-Else-Kaskade ist dann wie bei der binären Suche aufgebaut. Der Nachteil ist, dass es im resultierenden Suchbaum 2n verschiedene Zweige gibt, und somit die Wahrscheinlichkeit gegeben ist, dass Threads in der OpenCL-Umgebung, die gleichzeitig an der Berechnung benachbarter Pixel arbeiten, auf Grund unterschiedlicher Tiefen ihrer Stacks unterschiedliche Zweige abarbeiten. Auf der verwendeten Hardware bedeutet dies, dass die Abarbeitung jedes Bedingungszweiges serialisiert wird. Um die Anzahl der Zweige zu reduzieren, verwendet der Traversierungsalgorithmus int4-Register. Ein int4-Register wird dabei als aktueller Stack deklariert und enthält immer vier verschiedene Werte des tatsächlichen Stacks. Da das Register insgesamt 22 = 4 Einträge hat, reichen zwei bedingte Anweisungen aus, um aus dem aktuellen Stack den indizierten Wert auszulesen. Der Index in das Register des aktuellen Stack wird aus dem Vierermodulus des Stackpointers berechnet. Die restlichen Werte des gesamten Stacks stehen in anderen int4-Registern. Immer wenn der Stackpointer auf einen Stackeintrag zeigt, der nicht im aktuellen Stack steht, wird rechtzeitig der aktuelle Stack mit dem Inhalt der anderen int4-Register geladen. Der Ladevorgang muss natürlich wieder einen Bedingungszweig durchlaufen, um das passende int4-Register auszuwählen. Der Aufwand für den Ladevorgang aus den anderen int4-Registern des Stacks tritt aber nur dann in jedem Traversierungsschritt auf, wenn der Wert des Stackpointers ständig zwischen den Werten 4n und 4n + 1 wechselt. Dieses Verhalten bedeutet, dass ständig in einem Schritt m ein Kindknotenindex auf den Stack geschoben wird und im Schritt m + 2 dieser Index wieder gelesen wird. Das ist aber nur möglich, wenn im Zwischenschritt m + 1 der Nachbarknoten zum Kind aus Schritt m ein Endknoten im Traversierungsbaum ist. Der Knoten ist dann ein Endknoten, wenn der Raytracingstrahl durch freien Raum im Nachbarknoten verläuft und kein Kind des Nachbarknotens trifft oder wenn der Nachbarknoten ein Blattknoten ist. Freier Raum ist hauptsächlich nahe der Wurzel des kd-trees vorhanden. Der kd-tree wird im Erstellungs55 Abbildung 5.5: Entscheidungsbäume für das Lesen aus einem Vektorregisterstack. Der Stack besteht aus zwei int4-Vektorregistern. Die linke Abbildung zeigt den Entscheidungsbaum für das Laden eines der Stackregister in den aktuellen Stackausschnitt. Der Wert des Stackindex i ist 3. Nach dem Lesen des Stackregisters wird ein zweiter Entscheidungsbaum durchlaufen, um den Stackeintrag auszulesen. Die rechte Abbildung zeigt ein Beispiel des Durchlaufs mit einem Wert des Stackindex i von 3. algorithmus gerade so aufgebaut, dass freier Raum in Kindknoten minimiert wird. Der freie Raum kann also nicht während der ganzen Traversierung für eine Oszillation des Stackpointers sorgen. Der zweite Fall, dass der Nachbarknoten ein Blatt ist, ist auch sehr unwahrscheinlich, da der kd-tree so aufgebaut wird, dass alle Zweige eine möglichst gleiche Länge haben. Aus diesen Beobachtungen lässt sich schließen, dass der Ladevorgang aus den int4-Registern, die den Stack enthalten, in das Register des aktuellen Stacks wesentlich seltener als in jedem zweiten Schritt auftritt. Da immer in einem Ladevorgang vier Werte aus den Stack-Registern in den aktuellen Stack gelesen werden, ist auch die If-ElseKaskade zum Laden aus den Stackregistern um ld4 = 2 Schritte kürzer. Im schlechtesten Fall muss der Algorithmus bei einem Stack mit sechzehn Einträgen ld16 − ld4 = 2 Vergleiche durchführen, um den aktuellen Stack aus den Stackregistern zu laden. Desweiteren fallen zwei Vergleiche an, um aus dem aktuellen Stack den richtigen Eintrag zu lesen. Der Algorithmus mit int4-Registern benötigt damit im schlechtesten Fall genau so viele Vergleiche, wie der Algorithmus der 16 int-Register verwendet. In den wesentlich häufiger auftretenden Fällen, dass nur aus dem aktuellen Stack gelesen werden muss, benötigt der Algorithmus mit int4-Registern aber nur zwei Vergleiche, während der ursprüngliche Algorithmus in jedem Fall vier Vergleiche braucht. Die Wahl von Vektorregistern mit vier Einträgen resultiert teilweise aus der Tatsache, dass der Raytracer zuerst in der Brook+-Umgebung entwickelt wurde, welche nur Vektorregister mit zwei, drei oder vier Einträgen unterstützt. Wenn die Vektorregister zu klein sind, wie dies bei zwei oder drei Registern der Fall ist, muss der aktuelle Stackausschnitt zu oft aus den Stackregistern erneuert werden. Der schlechteste Fall mit den meisten Vergleichen tritt somit zu häufig auf. In der OpenCL Umgebung sind zusätzlich Vektorregister mit acht oder sechzehn Einträgen verfügbar. Bei diesen Registern ist die Anzahl der Vergleiche, um aus dem aktuellen Stackausschnitt zu lesen, zu groß. Eine gesamte Stackgröße 56 von sechzehn reicht für die in dieser Arbeit verwendeten Modelle vollkommen aus. Ein balancierter kd-tree, bei dem alle Zweige die gleiche Länge haben, enthält 216 = 65536 Blätter. Der Erzeugungsalgorithmus des kd-tree verwendet Blätter, die maximal 32 Objekte enthalten. Ein kd-tree mit einer Höhe von sechzehn reicht damit für Modelle mit ca. zwei Millionen Surfeln aus. In einem zweiten Optimierungsschritt wurde sowohl der Entscheidungsbaum für das Laden aus den Stackregistern als auch das Lesen aus dem aktuellen Stack durch bedingte Zuweisungen ersetzt. In der Entwicklung des Raytracers hat sich gezeigt, dass das Nacheinanderausführen von mehreren bedingten Zuweisungen, auch wenn sie mehr Vergleiche als eine If-Else-Kaskade erfordern, auf der verwendeten Hardware selbst bei Kaskaden mit nur zwei Vergleichen schneller ist. Der gesamte Lesealgorithmus zum Lesen des Stackeintrags, abhängig vom Stackindex, wird in Listing 5.1 mit OpenCL-Code wiedergegeben. Listing 5.1: OpenCL-Code zum Lesen des Stackeintrags aus einem mit int4-Registern implementierten Stack. Der Stack hat eine maximale Höhe von sechzehn Einträgen. Der Stackindex stack_ptr zeigt immer auf den nächsten freien Platz auf dem Stack. Es wird also von der Position stack_ptr der oberste Wert des Stacks gelesen. 1 2 3 4 5 6 7 8 stack_mod4 = ( s t a c k _ p t r % 4 ) ; l o c a l _ s t a c k = s t a c k _ p t r == 4 ? l o c a l _ s t a c k _ 0 t o 3 : l o c a l _ s t a c k ; l o c a l _ s t a c k = s t a c k _ p t r == 8 ? l o c a l _ s t a c k _ 4 t o 7 : l o c a l _ s t a c k ; l o c a l _ s t a c k = s t a c k _ p t r == 12 ? l o c a l _ s t a c k _ 8 t o 1 1 : l o c a l _ s t a c k ; nodeindex = l o c a l _ s t a c k .w; nodeindex = stack_mod4 == 1 ? l o c a l _ s t a c k . x : nodeindex ; nodeindex = stack_mod4 == 2 ? l o c a l _ s t a c k . y : nodeindex ; nodeindex = stack_mod4 == 3 ? l o c a l _ s t a c k . z : nodeindex ; In den Zeilen 2 bis 4 wird der aktuelle Stack abhängig vom Wert des Stackindex aktualisiert. Die drei Vergleiche mit dem Stackindex in diesen Zeilen können auf den parallelen ALUs der Hardware zusammen mit der Modulusberechnung des Stackindex berechnet werden. Diese vier Operationen belegen somit nur eine Anweisung auf der Hardware. Da jede Anweisung der Zeilen 2 bis 4 auf den aktuellen Stack im Register local_stack zugreift, werden diese Operationen seriell auf der Hardware verarbeitet. In den Zeilen 5 bis 8 wird der indizierte Wert aus dem aktuellen Stack in das Register nodeindex gelesen. Die Zuweisung aus Zeile 5 kann parallel zu den Vergleichen mit dem Vierermodulus aus dem Stackindex abgearbeitet werden. Die weiteren drei Zuweisungen auf das Register nodeindex müssen hingegen seriell bearbeitet werden. Insgesamt muss die GPUHardware so nur acht Anweisungen abarbeiten, um den indizierten Stackwert auszulesen. Der große Vorteil dieser Implementierungsmethode ist, dass sie keine Divergenz in den Anweisungspfaden von parallelen Threads hervorruft und die GPU das Lesen aus dem Stack mit maximaler Effizient durchführen kann. Die Schnitttest zwischen dem Strahl des Raytracingalgorithmus und den AABBs der kdtree Knoten wurde mit dem von Geimer et. al. [22] beschriebenen Algorithmus umgesetzt. Dieser Algorithmus ist für die Ausführung auf SIMD-Einheiten, zu denen man auch die ALUs der verwendeten Hardware zählen kann, optimiert. Er verzichtet auf frühe Abbruchtests, die auf einem CPU-Raytracer effizient die Anzahl der ausgeführten Anweisungen reduzieren. Eine positive Folge daraus ist, dass es nur einen Pfad im Programmfluss gibt. Des Weiteren werden Teilergebnisse des Schnitttests parallel berechnet. Der Algorithmus 57 von Geimer ist aus den genannten Gründen sehr gut für die Ausführung auf der verwendeten Hardware geeignet. 5.1.3 Schnitttests Die Schnitttests wurden eingeführt, um nachfolgende teure Operationen zu verhindern, wenn deren Berechnung unnötig ist. Da die Abbildung der Bounding-Sphere auf der Bildebene meist wesentlich kleiner ist als die Abbildung des flächenrepräsentierenden Objekts der geometrischen Algebra, ist der Schnitttest zwischen Bounding-Sphere und Sichtstrahl möglichst früh durchzuführen. So belegt eine Ebene, die die Fläche des Surfels darstellt, aus allen nicht zur Ebene parallelen Blickrichtungen auf die Bounding-Sphere alle Pixel der Bildebene, während die Bounding-Sphere bei genügendem Abstand zum Strahlursprung nur einen Teil der Pixel belegt. Der Schnitttest wird in der Geometrischen Algebra über zwei Schritte umgesetzt. Zuerst wird über das äußere Produkt der Schnitt zwischen der Strahlgeraden und der Bounding-Sphere berechnet. P p = SBound ing ∧ LS t r ahl (5.8) Das Ergebnis der Schnittberechnung ist ein Punktpaar. Der Schnittindikator wird über das innere Produkt des Punktpaares mit sich selbst umgesetzt. Schni t t = (P p · P p < 0) (5.9) Immer wenn ein tatsächlicher Schnitt vorliegt und somit das Punktpaar ein reales Punktpaar ist, dann ist das innere Produkt des Punktpaares mit sich selbst kleiner als Null. Ein zusätzlicher Schnitttest wird nachfolgend mit dem Geometrischen Objekt durchgeführt, welches die Oberfläche des Surfels darstellt. Dieser Test bedeutet keinen erhöhten Aufwand, da das Punktpaar selbst sowie das innere Produkt des Punktpaares aus dem Schnitt zwischen Strahl und Oberfläche auch für spätere Rechnungen benötigt werden. Für Surfel nahe der Silhouette des Modells kann dieser zusätzliche Test den Rechenaufwand verringern, da dann der Strahl annähernd parallel zur Oberfläche des Modells verläuft. 5.1.4 Berechnung der Schnittpunkte Das Ergebnis des Schnittes zwischen einer Geraden und einer Kugel der Geometrischen Algebra (siehe Formel 5.8) ist ein Punktpaar, welches die beiden Schnittpunkte enthält. Zur weiteren Verarbeitung muss das Punktpaar P pSchni t t in die beiden Schnittpunkte PS1 und PS2 zerlegt werden. Dies kann mit folgender Formel aus [18] bewerkstelligt werden. p PS x = ∗ ∗ ∗ P pSchni · P pSchni + P pSchni tt tt tt ∗ e∞ · P pSchni tt (5.10) Eine Erklärung zur Arbeitsweise dieser Formel in leicht abgewandelter Form kann in [25] Kapitel 9.2 gefunden werden. Wie am Zähler der Formel 5.10 zu sehen ist, kann das Teilergebnis der Berechnung aus den Schnitttests aus Formel 5.9 direkt weiterverwendet werden. Leider ist Formel 5.10 nur anwendbar, um die Schnittpunkte eines Schnittes zwischen 58 Gerade und Kugel zu trennen. Wird der Schnitt zwischen einer Ebene und einem Strahl gebildet, so besteht das resultierende Punktpaar aus dem Schnittpunkt auf der Ebene und dem Punkt im Unendlichen. ∗ ∗ P pSchni t t = (π ∧ L) = PS ∧ e∞ (5.11) Dieser Ausdruck eines äußeren Produkts zwischen einem konformen Punkt und e∞ wird auch Flatpoint genannt. In Formel 5.10 besteht das Problem in der Berechnung des Terms 1 . Der Flatpoint, der aus dem Schnitt zwischen Strahl und Ebene (siehe Formel e ·P p∗ ∞ Schni t t 5.11 hervorgeht, enthält nur Summanden mit einem ∧e∞ -Term. 1 P ∧ e∞ = (p + p2 e∞ + e0 ) ∧ e∞ 2 1 = p ∧ e∞ + p2 e∞ ∧ e∞ +e0 ∧ e∞ |2 {z } (5.12) (5.13) e∞ ∧e∞ =0 Wenn der Flatpoint aus Gleichung 5.13 das innere Produkt mit e∞ wie im Nenner von Gleichung 5.10 bildet, bleibt nur noch ein (e0 ∧ e∞ ) · e∞ -Summand übrig. Das innere Produkt e∞ · e∞ ist nach Definition Null. Aus diesem Grund verschwinden die anderen Summanden. Der übrigbleibende Summand, ist nach weiteren Umformungen gerade e∞ mit einem Vorfaktor aus R. Der Term e ·P p1∗ kann nach [46] umgeformt werden in ∞ Schni t t 1 ∗ e∞ · P pSchni tt ∗ (e∞ · P pSchni t t )˜ = 2 e · P p∗ ∞ Schni t t ∗ e∞ · P pSchni tt = ∗ ∗ (e∞ · P pSchni ) · (e · ∞ P pSchni t t ) tt (5.14) (5.15) Der Nenner aus Gleichung 5.15 ist nach den vorigen Überlegungen aber e∞ · e∞ und somit gleich Null. Damit ist auch der Nenner von Gleichung 5.10 gleich Null wenn das Punktpaar aus dem Schnitt zwischen einer Ebene und einer Geraden resultiert. Als Lösung hat sich nach empirischen Tests mit der GA-Entwicklungsumgebung CLUCalc ∗ [45] folgende kleine Änderung an Formel 5.10 erwiesen. Der Nenner e∞ · P pSchni t t muss ∗ durch e0 ·P pSchni t t ersetzt werden. Da e0 ·e∞ = −1 gilt, verschwinden in Formel 5.13 andere Summanden und der Nenner in Formel 5.15 wird im Falle eines Schnitts zwischen einer Geraden und einer Ebene nicht Null. Die in dieser Arbeit verwendete Formel zum Trennen des Punktpaares lautet somit p ∗ ∗ ∗ P pSchni · P pSchni + P pSchni tt tt tt PS x = (5.16) ∗ e0 · P pSchni t t Der Vorteil an Formel 5.16 ist, dass sie sowohl für den Schnitt zwischen Ebene und Gerade als auch für den Schnitt zwischen Kugel und Gerade die Schnittpunkte berechnen kann. Auch wenn Formel 5.16 in dieser Arbeit nicht mathematisch bewiesen wurde, hat sie im implementierten Raytracer ihre Anwendbarkeit bewiesen. Alternativ hätte auch Formel 5.10 für den Schnitt von Kugeln und eine andere Methode wie in [18] für den Schnitt von Ebenen verwendet werden können. Dies würde aber zwei von den Modelldaten abhängige bedingte Anweisungszweige einführen, deren Auftreten in der OpenCL-Umgebung gerade verhindert werden sollte. 59 Abbildung 5.6: Verschiedene Konstellationen des Schnitts zwischen Strahl und Surfel. Der Strahl trifft jeweils das durch einen Kreisbogen dargestellte Surfel und die zugehörige gepunktete Bounding-Sphere. Die Schnitte unterscheiden sich in der Akzeptanz der Schnittpunkte. Nur die durch Sterne dargestellten Schnittpunkte werden akzeptiert. Die Schnittpunkte außerhalb der Bounding-Sphere, dargestellt durch Kreisscheiben, werden verworfen. 5.1.5 Überprüfung der Schnittpunkte Die gefundenen Schnittpunkte eines Surfels müssen einigen Tests unterzogen werden, bevor sie als nächster Schnittpunkt zur Kamera auf dem aktuellen Strahl akzeptiert werden können. Abbildung 5.6 zeigt verschiedene Konstellationen von Surfeln, Bounding-Sphere und Strahl, die zu unterschiedlichen Behandlungen der Schnittpunkte führen. Die Kamera in der Szene wird durch ein schwarz gefülltes Rechteck dargestellt, während die BoundingSphere durch einen gepunkteten Kreis und das zugehörige Surfel durch einen schwarzen Kreisbogen illustriert werden. Akzeptierte Schnittpunkte liegen im Mittelpunkt der grünen Sterne. Weiterhin zeigen die roten Kreisscheiben nicht akzeptierte Schnittpunkte. Die Geraden mit Pfeilende stellen die Strahlen dar. Alle drei von der Kamera aus in die Szene geschossenen Strahlen schneiden sowohl die Bounding-Sphere als auch das Surfel. Gemäß der Modellbeschreibung aus Kapitel 4.1 ist die Oberfläche des Surfels nur innerhalb der Bounding-Sphere gültig. Demnach sind auch nur Schnittpunkte mit dem Surfel, die innerhalb der Bounding-Sphere liegen, gültige Schnittpunkte mit der Oberfläche des Modells. Es müssen also zusätzlich zu den Schnitttests des Strahls gegen die Bounding-Sphere und gegen das Surfel auch die berechneten Schnittpunkte gegen die Bounding-Sphere getestet werden, um sicher aussagen zu können, ob ein Schnitt mit dem Modell vorliegt. Der obere Strahl in Abbildung 5.6 zeigt den gewöhnlicher Weise erwarteten Fall, bei dem beide Schnittpunkte innerhalb der Boundingsphere liegen und somit gültig sind. Einen weiteren 60 Fall, der für die Gültigkeit der Schnittpunkte auftreten kann, zeigt der mittlere Strahl in Abbildung 5.6. Hier zeigt sich, dass es nicht ausreicht, immer den nächsten der beiden Schnittpunkte zu wählen. Der zur Kamera nähere Schnittpunkt ist in diesem Fall ungültig. Er liegt außerhalb der Bounding-Sphere. Der zweite Schnittpunkt ist hingegen gültig. Es kann auch vorkommen, dass beide Schnittpunkte außerhalb der Bounding-Sphere liegen und somit verworfen werden müssen. Diesen Fall zeigt der untere Strahl in Abbildung 5.6. Für alle gültigen Schnittpunkte wird der Abstand der Schnittpunkte zur Kamera berechnet. Daraufhin wird der Abstand der aktuellen Schnittpunkte mit dem Abstand des bisher zur Kamera nächsten Schnittpunkts aus vorangegangenen Schnitten des Strahls mit anderen Surfeln verglichen. Wenn der aktuelle Schnittpunkt näher ist, als der bisherige nächste Punkt wird dieser Schnittpunkt ersetzt. Es ist überflüssig die Abstände beider Schnittpunkte des aktuellen Surfels miteinander zu vergleichen. Wenn die beiden Schnittpunkte nacheinander gegen den bisherigen Schnittpunkt getestet werden, so ist der erste Schnittpunkt, wenn er den alten Schnittpunkt ersetzt, beim Test des zweiten Schnittpunkts selbst der ältere Schnittpunkt. Er wird dann durch den zweiten Schnittpunkt ersetzt, wenn dieser näher zur Kamera liegt als der erste Schnittpunkt. 5.1.6 Interpolation Punktwolken, die von natürlichen Gegenständen abgelesen werden, weisen oft glatte Flächen ohne scharfe Übergänge auf. Auch Punktwolken von industriellen Gütern zeigen zwischen Kanten und Ecken glatte Flächen. Aufgrund des Aufbaus der Modelle aus vielen Kugeln kommt es an den Schnittkanten der Kugeln zu sichtbaren scharfen Kanten, obwohl die abgetasteten Objekte an dieser Stelle keine Kanten aufweisen. Dies tritt insbesondere auf, wenn sich die Krümmung der Oberfläche in verschiedenen Richtungen verschieden stark ändert. Die Krümmung der zur Repräsentation gewählten Kugel ändert sich hingegen in jede Richtung gleichmäßig. Damit eine lochfreie Darstellung der Oberflächen möglich ist, müssen sich die Surfel überlappen. Diese Überlappungsbereiche sind die Bereiche, an denen die unerwünschten Kanten am deutlichsten hervortreten. Da in diesen Regionen die Oberfläche des Modells aber durch mehrere Repräsentationen dargestellt wird, ist es sinnvoll auch alle Repräsentationen zu einem gewissen Anteil zur Darstellung zu verwenden. Dies wird durch eine Interpolationsstufe nach dem Finden des nächsten Schnittpunkts zur Kamera durchgeführt. Wenn die Interpolation passend gewählt ist, werden die optischen sichtbaren Übergänge zwischen verschiedenen Surfeln reduziert. Abbildung 5.7 verdeutlicht das Problem der scharfen Kanten. Ohne Interpolation schneiden die Strahlen von der Kamera im oberen Bereich des Bildes das rechte Surfel. Wandern die Strahlen jedoch weiter nach unten als die Oberkante des linken Surfels, so tritt ein Sprung vom rechten Surfel auf die Oberfläche des linken Surfels auf, der im berechneten Bild auch sichtbar wird. Da die Surfel jedoch nahe beieinander liegen, ist anzunehmen, dass die Surfel die gleiche Oberfläche annähern. Im Überlappungsbereich werden deshalb die beiden roten Schnittpunkte des mittleren Strahls und die zugehörigen Normalen zum grünen Punkt interpoliert. Wird die Interpolation auf alle Strahlen im Überlappungsbereich angewendet, so ergibt sich ein weicher Übergang von der Oberfläche des rechten Surfels zur Oberfläche des linken Surfels. Um die Interpolation durchführen zu können, müssen die Surfel bekannt sein, die das in der Schnittberechnung gefundene Surfel überlappen. Zusätzlich zu dem gefundenen 61 Abbildung 5.7: Interpolation an sich überlappenden Surfeln. Der obere und untere Strahl weisen jeweils nur einen einzigen Schnittpunkt mit einem Surfel auf. Der mittlere Strahl hat hingegen in der Nähe des zur Kamera nächsten Schnitts einen weiteren Repräsentanten für die Oberfläche des Modells. Da beide dunkelgrauen Schnittpunkte die tatsächliche Oberfläche in der Nähe des Schnitts repräsentieren, muss die tatsächliche Oberfläche an einem Punkt zwischen den beiden Punkten liegen. Dieser hellgraue Punkt wird durch die Interpolation berechnet. Zusätzlich wird auch die Normale interpoliert um nach der Beleuchtungsrechnung eine glatte Oberfläche zu erhalten. Schnittpunkt ist auch die Bounding-Sphere des am Schnitt beteiligten Surfels bekannt. Mit Hilfe dieser Bounding-Sphere wird der Surfel Raum nach Nachbarsurfeln des geschnittenen Surfels abgesucht. Da eine exzessive Suche des gesamten Surfelraums zu aufwändig ist, wird der kd-tree aus der Schnittberechnung wiederverwendet. Anstelle des Schnitts zwischen Strahl und Knoten des kd-trees werden in der Interpolationsstufe die Knoten traversiert, die die Bounding-Sphere des Schnittsurfels beinhalten oder schneiden. Für den Schnitttest wird der SIMD-freundliche Algorithmus aus [5] verwendet. Eine weitere Beschleunigung der Traversierung ist möglich, indem nicht der gesamte kd-tree traversiert wird. Beim Erzeugen des Baums werden nahe beieinander liegende Objekte meist dem selben Knoten zugewiesen. Da die Interpolation auch nahe beieinander liegende Surfel betrachten soll, reicht es aus, in dem Teil des kd-trees zu starten, der das geschnittene Surfel enthält. Die ID des Knotens mit dem geschnittenen Surfel ist aus der Traversierung während der Schnittsuche bekannt. Durch die Reduktion auf den Knoten mit dem geschnittenen Surfel können einige Surfel aus der Interpolationsberechnung ausgeschlossen werden, obwohl sie die Bounding-Sphere des geschnittenen Surfels schneiden. Dies tritt auf Grund der sich überlappenden Knoten des kd-tree auf, wobei die ausgeschlossenen Surfel sich in benachbarten Knoten befinden. Die Interpolation liefert trotzdem zufriedenstellende Ergebnisse. Der Interpolationsalgorithmus berechnet mit allen Surfeln, deren Bounding-Spheres die Bounding-Sphere des Schnittsurfels schneiden, Schnittpunkte. Der Schnitt zwischen den 62 Bounding-Spheres wird mit folgenden Berechnungen umgesetzt. Zuerst wird der Schnittkreis zwischen beiden Kugeln berechnet. CSchni t t = S1 ∧ S2 (5.17) Dann wird das innere Produkt des Schnittkreises mit sich selbst gebildet. Wenn der resultierende skalare Wert kleiner als Null ist, liegt ein Schnitt vor. I ndikat or = CSchni t t · CSchni t t (5.18) Wenn sich die Bounding-Spheres schneiden, werden für das zugehörige Surfel die Schnittpunkte mit dem Strahl des Raytracing-Verfahrens berechnet. Für die Schnittpunkte gelten die gleichen Anforderungen an die Gültigkeit wie in Kapitel 5.1.5. Eine Ausnahme besteht jedoch darin, dass beide Schnittpunkte für die Interpolation verwendet werden, wenn sie gültig sind. Die nahe Lage der beiden Schnittpunkte zum Interpolationsbereich lässt diese Änderung als sinnvoll erscheinen. Alle Schnittpunkte Pi werden nach der Formel P i Pi ∗ G(Pi , Si ) (5.19) P= P i G(Pi , Si ) interpoliert, wobei G(Pi , Si ) eine Gewichtungsfunktion ist, deren Wert mit steigendem Abstand des Punktes Pi zum Mittelpunkt seiner Bounding-Sphere Si stark abnimmt. Als Gewichtungsfunktion wird eine e-Funktion verwendet. Sie hat den folgenden Aufbau: G(Pi , Si ) = e e x p P − M 2 i i 2 ex p = 2 ri ∗ µ (5.20) (5.21) wobei Mi der Mittelpunkt und ri der Radius der Kugel Si sind. Der Parameter µ dient dazu die Stärke der Dämpfung durch die e-Funktion abhängig vom Radius ri zu beeinflussen. Der Standardwert für µ ist in dieser Arbeit 0.25. Neben den Schnittpunkten werden die Normalen an den Schnittpunkten auf die gleiche Weise interpoliert. 5.1.7 Beleuchtungsrechnung Unter der Beleuchtungsrechnung oder Shading versteht man die Berechnung des Farbwerts eines Pixels enlang des Sichtstrahls v . Der Farbwert wird auf Grund von Materialeigenschaften der Oberfläche und den Eigenschaften von einer oder mehreren Lichtquellen bestimmt. Die Berechnung selbst wird über die sogenannte Shading-Equation, in etwa Schattierungsgleichung, bestimmt. Mit Hilfe dieser Gleichung wird versucht verschiedene Phänomene der Interaktion von Licht mit Oberflächen in der realen Welt nachzubilden. Die einfacheren Gleichungen bestehen meist aus drei Teilen. Sie setzen sich aus dem ambienten, diffusen und spekulären Term zusammen. Der spekuläre Term berechnet die Ausbreitung von reflektiertem Licht. Licht, welches in eine Oberfläche eindringt und nach mehreren Reflektionen innerhalb der Oberfläche wieder in den freien Raum austritt sowie von rauen Oberflächen reflektiertes Licht wird mit dem diffusen Term berechnet. In einfacheren Beleuchtungsmodellen 63 wird angenommen, dass das Licht in alle Richtungen gleichmäßig reflektiert wird. Komplexere Verfahren berechnen die Lichtstreuung innnerhalb der Oberfläche. So simulieren D’On et al. [15] die Ausbreitung von Licht in menschlicher Haut, um die Darstellung von Personen in Echtzeit-Anwendungen realistischer zu gestalten. Der ambiente Term ist meist ein konstanter Wert. Er soll Licht immitieren, welches mehrfach in der Szene reflektiert und teilweise absorbiert wurde. Vereinfacht ausgedrückt gibt der ambiente Term das Licht der Umgebung wieder. In komplexeren Shading-Equations wird auch das tatsächlich von der Umgebung reflektierte Licht für den ambienten Term berechnet. Dies ist wichtig, um eine realitätsnahe Wiedergabe der virtuellen Realität zu erhalten, wie sie z.B. in Anwendungen zum Training von Operationen am Menschen [11] erforderlich ist. Da die realistische Beleuchtungssimulation nicht Ziel dieser Arbeit ist, wird hier nur ein einfaches Verfahren gesucht, welches sich auch möglichst mit den Mitteln der geometrischen Algebra berechnen lässt. In den einfacheren Beleuchtungsmodellen wie auch in dieser Arbeit wird der diffuse Term nach dem Lambert’schen Gesetz berechnet. Der resultierende Wert L di f f hängt nur von der Oberflächennormalen n und der Lichtposition über den Lichtvektor l ab. L di f f = cd i f f ∗ cos θd (5.22) Weiterhin muss der Winkel θd = n · l zwischen der Oberflächennormalen und dem Vektor zur Lichtquelle für jeden Pixel berechnet werden. Die resultierende Farbe der Interaktion zwischen Licht und Oberfläche wird über die Konstante cd i f f festgelegt. Da die Oberfläche nicht beleuchtet wird, wenn die Lichtquelle hinter der Oberfläche ist und der Cosinus dann negative Werte liefert, sind nur positive Werte oder Null für den Cosinus erlaubt. Dies wird in Formel 5.22 mit dem überstrichenen cos ausgedrückt. Das spekuläre Licht ist im Gegensatz zum diffusen Licht auch vom Betrachtungswinkel zur Oberfläche abhängig. Das weit verbreitete Blinn-Phong-Modell [8] verwendet den Sichtstrahl v indirekt über den Halbvektor h, um den Betrachtungswinkel in die Berechnung mit einzubeziehen. Der Halbvektor bezieht seinen Namen aus der Tatsache, dass er auf halbem Weg zwischen Lichtvektor l und Sichtvektor v liegt. Der normalisierte Halbvektor wird wie folgt berechnet: h= l +v (5.23) kl + v k Der Wert des spekulären Lichts wird dann an Hand des Winkels zwischen Halbvektor h und der Oberflächennormalen n bestimmt. Lspec = cspec ∗ cos θs θs = n · h m (5.24) (5.25) Die Formel ähnelt ansonsten der Formel des diffusen Terms. Sie beinhaltet auch einen konstanten Wert cspec für die resultierende Farbe. Ein Unterschied besteht jedoch darin, dass der Cosinus mit dem Parameter m potenziert wird. Der Parameter m steuert im Beleuchtungsmodell, wie schnell sich der Wert des spekulären Terms verringert, wenn der Sichtstrahl vom nach dem Reflexionsgesetz reflektierten Lichtstrahl l r abweicht. Somit repräsentiert m die Rauheit der Oberfläche im spekulären Term. Ein kleines m bedeutet ein 64 Abbildung 5.8: Der spekuläre Term. Diese Abbildung zeigt die Auswirkung des Parameters m auf den spekulären Term. Für kleine Werte von m ist das Glanzlicht groß und an den Rändern unscharf. Das linke Bild der Rückseite des Max Planck Modells verdeutlicht diesen Zusammenhang mit einem Wert von m = 5. Mit steigendem m wird das Glanzlicht kleiner und schärfer. Im rechten Bild wird die Beleuchtung des Max Planck Modells mit m = 20 berechnet. 65 großes schwaches Bild der reflektierten Lichtquelle auf der Oberfläche, während eine Vergrößerung von m zu einem kleineren helleren Abbild der Lichtquelle führt. Abbildung 5.8 zeigt ein Beispiel zur Wirkungsweise des Parameters m. Das Phong-Modell [47] nutzt zur Berechnung der spekulären Lichtintensität den an der Oberfläche reflektierten Sichtvektor v r oder den reflektierten Lichtvektor l r . Der Winkel θs wird zwischen dem Lichtvektor l und v r oder dem Sichtstrahl v und l r berechnet. Beide Ergebnisse sind identisch. Die Verwendung des reflektierten Sichtstrahls v r hat jedoch den Vorteil, dass er für die Berechnung von Spiegelungen weiter verwendet werden kann. Es müssen dann Schnitte von Objekten mit v r in der Szene gesucht, deren Beleuchtung bestimmt und der Beleuchtungswert mit dem Wert an der reflektierenden Oberfläche verrechnet werden. Gleichung 5.24 wird ebenfalls vom Phong-Modell verwendet, wobei alle Operationen bis auf die Berechnung von θs gleich ablaufen. Abbildung 5.9: Vektoren und Winkel der spekulären Beleuchtungsrechnung. Das linke Bild zeigt die Vektoren des Blinn-Phong-Modells. Der Halbvektor h liegt genau zwischen Sichtvektor v und Lichtvektor l. Das mittlere und rechte Bild zeigen die Vektoren des Phong-Beleuchtungsmodells. Die Vektoren v r respektive l r sind die an der Normalen n reflektierten Vektoren v bzw. l. Der Winkel θs wird bei Blinn und Phong zwischen dem Halbvektor und der Normalen gemessen, während er bei Phong zwischen Sichtstrahl und reflektiertem Lichtstrahl oder Lichtstrahl und reflektiertem Sichtstrahl gemessen wird. Abbildung 5.9 fasst die geometrischen Objekte zusammen, die beide Verfahren zur spekulären Beleuchtungsrechnung verwenden. Der Sichtstrahl l ist in der Beleuchtungsrechnung durch die Schnittberechnungen mit dem Modell gegeben. Die weiteren Objekte müssen hingegen berechnet werden. Am einfachsten gestaltet sich dies bei der Normalen n. Wenn das geschnittene Surfel eine Ebene ist, kann die Normale in Linearer Algebra direkt ausgelesen werden. Im Fall einer Kugel wird die Normale im Schnittpunkt mit Hilfe des Mittelpunkts der Kugel berechnet. Die Normale ist parallel zur Geraden durch diese beiden Punkte. Die Berechnung der Normalen ist damit zum ersten Mal ein Punkt im Programmfluss an dem zwischen Ebene und Kugel unterschieden werden muss. Da die Normalenberechnung aber nur wenige Male pro Sichtstrahl erfolgt und in beiden Fällen wenig Berechnungsaufwand bedeutet, ist die Verlangsamung auf Grund der Unterscheidung nur minimal. Im Programmcode zur Arbeit wird einfach immer die Normale einer Kugel berechnet und dann abhängig vom Typ des Surfels die berechnete Normale der angenommenen Kugel verwendet oder die Normale der Ebene ausgelesen. In Geometrischer Algebra kann die Normale über eine Gerade repräsentiert werden. Die Berechnung dieser Geraden ist jedoch überflüssig. Den Cosinus für die diffuse Beleuchtung würde man in Geometrischer Algebra über das innere Produkt zwischen der Geraden parallel zur Norma66 len und der Geraden durch den Schnittpunkt und die Lichtquelle berechnen. Wenn man sich von einem Tool wie Gaalop [28] eine symbolische Vereinfachung der zu Grunde liegenden mathematischen Operationen ausgeben lässt, erkennt man, dass der Cosinus nur aus den Koeffizienten der beiden Geraden berechnet wird, die den Richtungsvektoren der Linearen Algebra parallel zu den beiden Geraden entsprechen. Es ist ausreichend nur die Normale der Linearen Algebra zu kennen. Auch für die Berechnung des reflektierten Lichtvektors l r oder des reflektierten Sichtvektors v r muss die Gerade parallel zur Normalen nicht ermittelt werden. Die Reflektion wird in Geometrischer Algebra über ein sogenanntes Sandwichprodukt des zu reflektierenden Objekts mit dem Reflektor umgesetzt. Das zu reflektierende Objekt ist hier eine Gerade parallel zum Lichtvektor oder zum Sichtvektor. Der Reflektor ist die Ebene, die den Schnittpunkt enthält und eine Normale parallel zur Normalen im Schnittpunkt hat. Kapitel 3.4.2 zeigt, dass zur Berechnung dieser Reflektionsebene π r nur die Koeffizienten der Normalen aus der linearen Algebra benötigt werden. Die Reflektion geschieht dann über folgende Formeln: v r = −π r ∗ v ∗ π r (5.26) l r = −π r ∗ l ∗ π r (5.27) Der Winkel θs wird dann zwischen der reflektierten Sichtgeraden v r und der Lichtgeraden l über das innere Produkt der beiden Objekte berechnet. 5.2 Raytracing mit Linearer Algebra Ein Ziel dieser Arbeit ist die Untersuchung, ob die Geometrische Algebra besser von paralleler Hardware profitiert als die Lineare Algebra. Diese Untersuchung soll durch Benchmarks auf der verwendeten Hardware ermöglicht werden. Zu diesem Zweck wurde der Raytracer auch in Linearer Algebra umgesetzt. Der Algorithmus zur Traversierung des kd-trees ist in beiden Implementierungen der gleiche. Weiterhin wurde die Interpolationsstufe nicht in Linearer Algebra implementiert. Beim Vergleich mit dem Algorithmus der Geometrischen Algebra wird die Interpolation im GA-Code entfernt. Die beiden Algebren unterscheiden sich in der Schnittberechnung und in der Beleuchtungsrechnung. Der Ablauf der einzelnen Schritte des Raytracing-Algorithmus ist dabei der gleiche. 5.2.1 Schnittberechnung In Linearer Algebra wird der Strahl ähnlich zur Geometrischen Algebra berechnet. Der Ursprung o des Strahls ist der Ortsvekor zur Kamera. Anstelle der Geraden, die den Strahl in GA repräsentiert, wird ein Richtungsvektor d der LA verwendet. Mit Hilfe der Differenz von Strahlursprung und dem Punkt auf der Bildebene, der das vom Strahl geschnittene Pixel repräsentiert, wird der Richtungsvektor bestimmt. Der Punkt in der Bildebene wird genauso wie in Kapitel 5.1.1 berechnet. Anschließend wird der Richtungsvektor normalisiert. Da die Lineare Algebra keine Formel enthält um sowohl den Schnitt zwischen Strahl und Ebene als auch den Schnitt zwischen Strahl und Kugel zu berechnen, muss im LAAlgorithmus bei den Schnitttests und Schnittberechnungen zwischen diesen beiden geometrischen Objekten unterschieden werden. Der Schnitt zwischen Strahl und Kugel wird 67 berechnet, indem die Strahlgleichung in die Gleichung der Kugel eingesetzt wird. Die entstehende quadratische Gleichung wird dann mit Hilfe der Mitternachtsformel gelöst. Für die Schnitttests, die keine Berechnung der Schnittpunkte erfordern, wird nur die Diskriminante der quadratischen Gleichung untersucht. Eine ausführlichere Betrachtung der Rechenschritte kann in [44] gefunden werden. Die Berechnung des Schnittpunkts zwischen Strahl v und Ebene π wird ähnlich zum Schnitt mit der Kugel gelöst. Auch hier wird die Strahlgleichung in die Ebenengleichung eingesetzt. Es ergibt sich mit der Ebenennormalen n und dem Abstand dπ der Ebene zum Ursprung folgende Gleichung: v = o+t∗d (5.28) π = n · x + dπ (5.29) 0 = n · (o + t ∗ d) + dπ −dπ − n · o t= n·d (5.30) (5.31) Der Nenner n · d von Gleichung 5.31 ist gleichzeitig ein Indikator für das Vorliegen des Schnitts. Wenn der Strahl parallel zur Ebene verläuft und somit kein Schnitt vorliegt wird der Nenner gleich Null. Sowohl die Schnittberechnung zwischen Strahl und Kugel als auch die Berechnung zwischen Strahl und Ebene geben den Parameter t der Strahlgleichung zurück, für den ein Schnitt vorliegt. Der Schnittpunkt wird durch Einsetzen des Parameters t in die Strahlgleichung berechnet. Der Schnittpunkt muss noch den in Kapitel 5.1.5 beschriebenen Tests unterzogen werden. Im Test gegen die Bounding-Sphere wird der quadrierte Abstand zwischen Schnittpunkt und Bounding-Sphere-Mittelpunkt mit dem quadrierten Radius verglichen. Auf diese Weise muss keine Wurzel gelöst werden, um den Abstand zwischen Schnittpunkt und Mittelpunkt zu berechnen. Ein quadrierter Abstand wird auch für den Vergleich des Abstands zwischen altem nächsten Punkt zum Strahlursprung und dem Abstand vom Schnittpunkt zum Strahlursprung verwendet. 5.2.2 Beleuchtungsrechnung Die Beleuchtungsrechnung nutzt zur Vergleichbarkeit zwischen LA und GA auch in Linearer Algebra das Phong Modell. Der diffuse Anteil wird genau wie in GA über den Cosinus aus dem Skalarprodukt des Normalenvektors n und des Lichtvektors l berechnet. Für den spekulären Anteil wird der reflektierte Sichtvektor v r und der Lichtvektor verwendet. Die Reflektion des Sichtstrahlrichtungsvektors wird über die bekannte Formel v r = d −2∗ d · n∗ n durchgeführt. Diese Formel sieht schon einfacher aus, als die Reflektion in Geometrischer Algebra mit Hilfe des Sandwichprodukts. Es muss hier aber festgehalten werden, dass der Richtungsvektor in Linearer Algebra an einer Ebene durch den Ursprung reflektiert wird, während die Reflektion in GA praktisch beliebig im Raum platziert stattfindet. Diese Beliebigkeit muss dazu beitragen, dass die Reflektion in GA aufwändiger ist. 68 6 Ergebnisse Eine Auswertung der Modellberechnung sowie des Raytracers steht im Mittelpunkt dieses Kapitels. Es werden die beiden Modellerzeugungsalgorithmen sowohl in ihrer Geschwindigkeit als auch in der Qualität der erzeugten Modelle verglichen. Weiterhin wird ergründet inwieweit die Raytracing-Algorithmen in Linearer Algebra und Geometrischer Algebra von den parallelen Recheneinheiten der GPU profitieren. Um das hier aufgestellte Visualisierungsmodell von Punktwolken allgemein einordnen zu können, wird es einem bekannten Modell sowohl in der visuellen Qualität der Bilder als auch in der Darstellungsgeschwindigkeit gegenübergestellt. 6.1 Modellerzeugung Dieses Kapitel vergleicht die beiden Algorithmen Algorithmus 1 und Algorithmus 2 zur Erzeugung eines Oberflächenmodells mit den Objekten der Geometrischen Algebra. Es wird sowohl die Optik der berechneten Modelle verglichen als auch die Performance während der Berechnung dieser Modelle gemessen. Weiterhin werden die erhofften Vorteile von Algorithmus 2 gegenüber Algorithmus 1 beurteilt. 6.1.1 Bunny Das Bunny-Modell aus dem Stanford 3D Scanning Repository [2] ist eines der am häufigsten genutzten Modelle zur Visualisierung in der Computergraphik. Das Dreiecksmodell des Bunnys wurde mit Hilfe eines Entfernungsscanners und verschiedener Algorithmen auf Basis einer Terracottafigur erstellt. Damit stellt das Bunny ein typisches Modell für eine aus der realen Welt ausgelesene Punktwolke dar. Abbildung 6.1: Das Dreiecksnetz des Stanford Bunny und die projezierten Punkte von LOP. 69 Abbildung 6.2: Ergebnisse der verschiedenen Algorithmen. Die linke Abbildung zeigt das Modell von Algorithmus 1 mit 18966 Surfeln. Die rechte Abbildung zeigt das Surfelmodell aus 17417 Surfeln, welches mit Algorithmus 2 berechnet wurde. 6.1.2 Variable Surfelanzahl Eines der Hauptargumente für die Entwicklung von Algorithmus 2 war die Möglichkeit eine feste Anzahl von Surfeln für die Oberflächenrepräsentation vorgeben zu können. Dieser Abschnitt zeigt Beispiele für Algorithmus 2 mit fest vorgegebener Surfel-Anzahl als auch Modelle von Algorithmus 1 mit variierender Genauigkeit. Abbildung 6.3: Das Bunny mit 8192 und 16384 Surfeln berechnet mit Algorithmus 2 70 Abbildung 6.4: Das Bunny mit 2048 und 4096 Surfeln. Die Modelle wurden mit Algorithmus 2 berechnet. Abbildung 6.5: Modelle von Algorithmus 1 mit 4094 und 8194 Surfeln. Die Parameter der Berechnung waren k = 75 und ε = 0, 003 sowie k = 24 und ε = 0, 001. 71 6.1.3 Dilo Das Dilo-Modell eines Parasaurolophus aus dem Aim@Shape-Repository [1] ist eine eher untypische Punktwolke. Das Modell zeigt starke Unterschiede in der Punktdichte verteilt über die Oberfläche. Insbesondere nahe der Ebene parallel zur Hoch- und Längsachse des Dinosauriers befinden sich sehr viele Punkte zur Darstellung des Modells. Weiterhin werden die Füße des Dinosauriers mit vielen Punkten modelliert. Die restlichen Bereiche des Modells zeigen hingegen eine weit geringere Punktdichte. Damit weicht das Modell stark von Modellen ab, die aus der realen Welt eingescannt wurden. Das Dilo-Modell soll trotzdem hier verwendet werden, um zu zeigen, wie sich die beiden Algorithmen in Extremfällen von stark wechselnder Punktdichte verhalten. Abbildung 6.6: Das Dilo-Dreiecksnetz und die mit LOP projezierten Punkte. Abbildung 6.7: Die berechneten Modelle der Dilo-Punktwolke. Links ist das Ergebnis von Algorithmus 1. Die rechte Abbildung zeigt das Ergebnis von Algorithmus 2. 6.1.4 Performance-Vergleich Tabelle 6.1 zeigt die Performance von Algorithmus 1 und Algorithmus 2. Für Algorithmus 2 wird zusätzlich die erwartete Performance angegeben, wenn die Berechnung des kd-tree und des Radius der Bounding-Spheres in Algorithmus 2 zusätzlich mit OpenCL parallelisiert werden. Für die Berechnung des Radius der Bounding-Sphere muss der kd-tree mit den projezierten Punkten traversiert werden und der zum Bounding-Sphere-Mittelpunkt zweitnächste andere projezierte Punkt gefunden werden. Während der Surfelberechnung wird der kd-tree mit den Punkten der Punktwolke mit der Bounding-Sphere traversiert. Dabei werden alle Knoten besucht, die von der Bounding-Sphere geschnitten werden. Daher kann angenommen werden, dass die Berechnung des Radius maximal so lange dauert, wie die Berechnung der Surfel selbst. Der kd-tree während der Surfel-Berechnung enthält mehr 72 Punkte und die Surfelberechnung beinhaltet mehr mathematische Operationen als der Vergleich der Abstände zwischen Punkten während der Radiusberechnung. Als Schätzwert für die Radiusberechnung wird daher die Zeit verwendet, die die Surfelberechnung benötigt. Der Schätzwert für die Berechnung des kd-tree wird von den Ergebnissen von Kun-Zhou et al. abgeleitet [53]. Der GPU-kd-tree-Algorithmus benötigt auf einer ähnlich leistungsstarken Grafikkarte, wie sie in dieser Arbeit verwendet wird, rund 100 Millisekunden, um einen kd-tree über 250000 Objekte zu erstellen. Die meisten Beispielpunktwolken in dieser Arbeit bestehen aus weniger als 250000 Punkten, so dass die Dauer von 100 Millisekunden für kd-tree-Erzeugung eine brauchbare obere Schranke für den Schätzwert darstellt. Modell Anzahl Surfel (Algo. 1/2) Bunny EG07 Phlegmatic Dragon 18966/17417 166162 Gesamtzeit Algorithmus 1 4,50 40,27 Algorithmus 2 9,45 236,14 Algorithmus 2 (erwartet) 3,50 76,96 Surfel und Bounding Sphere Algorithmus 1 1,800 17,00 Algorithmus 2 0,024 0,19 Tabelle 6.1: Performance von Algorithmus 1 und Algorithmus 2 (Dauer in Sekunden) 6.1.5 Auswertung Die Ergebnisse der vorangegangenen Abschnitte zeigen, dass Algorithmus 2 ein ernstzunehmender Ersatz für Algorithmus 1 bei der Modellberechnung ist. Die Modelle von Algorithmus 2 haben nicht nur annähernd die gleiche visuelle Qualität wie die Modelle von Algorithmus 1 bei gleicher Surfelanzahl, sondern bei der Verwendung von sehr wenigen Surfeln sind die Modelle von Algorithmus 2 sogar detaillierter (siehe Abbildung 6.4 und Abbildung 6.5). Des Weiteren zeigt Algorithmus 2 das Potenzial für eine größere Performance als Algorithmus 1. Insbesondere die Berechnung der Surfel und der zugehörigen Bounding-Spheres ist gut parallelisierbar und zeigt einen hohen Speedup. Die Performance von Algorithmus 2 wird hauptsächlich von LOP-Algorithmus und den damit ausgeführten Iterationen bestimmt. Wenn die kd-tree-Berechnung des LOP-Algorithmus parallelisiert wird, erreicht Algorithmus 2 je nach Modell eine sehr viel bessere oder leicht schlechtere Performance als Algorithmus 1. Ein großer Nachteil von Algorithmus 2 zeigt sich bei Modellen, die eine stark variierende Punktdichte aufweisen. Bei diesen Modellen hat Algorithmus 2 Probleme eine geschlossene Oberfläche zu berechnen. Als Lösung ist eine nachgeschaltete Ausführung von Algorithmus 1 für all die Punkte der Punktwolke denkbar, die nicht an der Berechnung eines Surfels beteiligt waren. Diese nachgeschaltete Stufe muss dann wesentlich weniger Surfel berechnen, als die Ausführung von Algorithmus 1 auf der gesamten Punktwolke. Somit sollte die Performance nicht zu stark beeinflusst werden. Mit Algorithmus 2 ist somit eine gute Grundlage für einen Algorithmus zur Berechnung von Oberflächenmodellen gegeben der auch in Zukunft von parallelen Einheiten in seiner Performance profitiert. 73 6.2 Vorteil durch Parallelisierung Die Arbeit von Fontijne [19] zeigt, dass das Raytracen mit Geometrischer Algebra selbst bei geschickter Implementierung mehr mathematische Operationen erfordert als das Raytracen mit Linearer Algebra. In diesem Kapitel wird untersucht, ob die Geometrische Algebra von den parallelen ALUs der verwendeten Hardware profitieren kann und somit eine ähnliche oder sogar bessere Performance als die Lineare Algebra zeigt. 6.2.1 Zeitmessungen mit Brook+ und OpenCL Es werden Zeitmessungsläufe zwischen dem Raytracing-Algorithmus in GA und LA mit verschiedenen Modellen durchgeführt und die Zeiten verglichen. Dabei werden die Messungen sowohl in der anfangs verwendeten Brook+-Umgebung als auch in der neueren OpenCL-Umgebung durchgeführt, um möglicherweise auftretende Unregelmäßigkeiten bei einem der zugehörigen Compiler zu erkennen. Tabelle 6.2 zeigt die Zeitmessungen mit einer Brute-Force-Traversierung über die Surfel der Szene. Die Ergebnisse mit der Beschleunigung durch einen kd-tree werden in Tabelle 6.3 präsentiert. OpenCL Brook+ LA GA LA GA Chameleon Bunny Moai Laurana (2899) (18966) (6816) (14616) 0,194 1,287 0,455 0,973 0,332 2,203 0,780 1,666 0,465 3,200 0,943 2,565 0,326 3,000 0,769 2,318 Tabelle 6.2: Performancevergleich zwischen Linearer- und Geometrischer Algebra in der OpenCL- und Brook+-Umgebung mit einer Brute-Force-Traversierung über die Surfel (Angaben in Sekunden) OpenCL (kd-tree) Brook+ (kd-tree) LA GA LA GA Chameleon Bunny Max Planck EG07 Phlegmatic (2899) (18966) (96208) Dragon (166162) 0,013 0,044 0,050 0,076 0,012 0,040 0,044 0,068 0,030 0,082 0,092 0,122 0,022 0,066 0,071 0,096 Tabelle 6.3: Performancevergleich zwischen Linearer und Geometrischer Algebra in der OpenCL- und Brook+-Umgebung mit der Beschleunigung des Algorithmus durch einen kd-tree (Angaben in Sekunden). In Klammern steht die Anzahl der Surfel jedes Modells. Die Ergebnisse zeigen, dass der Algorithmus in Geometrischer Algebra mit der BruteForce-Traversierung in OpenCL ungefähr 1,7 mal so langsam ist, wie der Algorithmus in Linearer Algebra. Diese Ergebnisse bewegen sich im Rahmen der Ergebnisse aus [19]. Die Algorithmen in Geometrischer Algebra scheinen hier keinen Vorteil aus den parallelen Recheneinheiten zu ziehen. Mit der kd-tree Beschleunigung ist das Raytracen mit GA 74 hingegen leicht performanter als das Raytracen mit LA. Dieser deutliche Wandel in der Performance durch Hinzunahme der kd-tree-Traversierung, die für die beiden Algebren gleich implementiert ist, wird in einem späteren Abschnitt in der Analyse der disassemblierten OpenCL-Kernel betrachtet. Die Ergebnisse mit Brook+ unterscheiden sich teilweise deutlich von denen mit OpenCL. Die Ausführung der Berechnungen ist nicht nur insgesamt bei allen Kerneln etwas langsamer als mit OpenCL, sondern der LA-Algorithmus mit Brute-Force-Traversierung ist sogar deutlich langsamer als der Algorithmus in Geometrischer Algebra. Bei den kleineren Modellen „Chameleon“ und „Moai“ zeigen Brook+ und OpenCL nahezu die gleiche Performance mit dem GA-Brute-Force-Algorithmus. Bei den größeren Modellen verliert Brook+ an Performance. Dieser Nachteil könnte an den bei größeren Modellen notwendigen Adressberechnungen beim Speicherzugriff liegen, da in Brook+ eindimensionale Speicherbereiche nur 8192 Elemente fassen können. Sowohl das Laurana- als auch das Bunny-Modell bestehen aus deutlich mehr als 8192 Surfeln. Mit der kd-tree-Traversierung verhalten sich sowohl die OpenCL als auch die Brook+-Algorithmen bei steigender Modellgröße weitgehend gleich. Der winzige Unterschied liegt im größeren Abstand der Performance zwischen GA und LA in Brook+. 6.2.2 Statische Analyse der Kompilate Da die Ergebnisse der Zeitmessungen recht unterschiedlich ausfallen und nicht ausreichen um eine endgültige Aussage zu treffen, wird im Folgenden der von den Compilern erzeugte Maschinencode untersucht. Mit dem Stream KernelAnalyzer [4] von AMD können sowohl Kernel von Brook+ als auch von OpenCL kompiliert und daraufhin in menschenlesbare Versionen des erzeugten Maschinencodes umgewandelt werden. Anhand des so erzeugten Maschinencodes wird nachfolgend untersucht, wie viele Berechnungsbefehle erzeugt werden. Weiterhin wird ermittelt, wie gut der Compiler die arithmetischen Operationen des Raytracing-Algorithmus auf die parallelen Recheneinheiten der ALUs aufteilt. Zusätzlich wird der Verbrauch der einzelnen Kernel an GPRs (General Purpose Register; Vektorregister mit vier 32bit Werten) ermittelt. Die Anzahl der GPRs entscheidet darüber, wie viele sogenannte Wavefronts (Gruppen aus 64 gemeinsam ausgeführten Threads) auf einer der SIMD-Einheiten der Hardware gleichzeitig zur Ausführung vorgehalten werden können. Die GPU kann mit steigender Anzahl von Wavefronts die Latenzen beim Speicherzugriff besser verdecken, indem die arithmetischen Befehle von anderen Wavefronts ausgeführt werden, während eine Wavefront auf die Ergebnisse des Speicherzugriffs wartet. Die Anzahl der verwendeten GPRs eines Kernels beeinflusst über die Anzahl der Wavefronts pro SIMD-Einheit somit die Performance. Die beiden Tabellen 6.4 und 6.5 zeigen, dass OpenCL mehr Anweisungen für den gleichen Algorithmus erzeugt als Brook+. Da bis auf den Algorithmus in Linearer Algebra mit Brute-Force-Traversierung die Packungseffizienz der OpenCL-Kompilate in der Nähe der Brook+-Kompilate liegt, erzeugt der OpenCL-Compiler nicht nur mehr Anweisungen sondern auch mehr mathematische Operationen. Diese zusätzlichen Operationen resultieren vermutlich daraus, dass OpenCL-Code von Unterfunktionen in den aufrufenden Programmcode kopiert während Brook+ den Unterfunktionscode über Funktionsaufrufe ausführt. Weiterhin ist festzustellen, dass OpenCL für das Kompilat eines bestimmten Kernels, bis auf den Sonderfall der Linearen Algebra mit Brute-Force-Traversierung, mehr Register 75 Traversierung Algebra LA Brute Force GA LA kd-tree GA Anweisungen Packungseffizienz GPR 1364 33,09 16 1007 53,92 36 2088 54,54 44 1657 63,87 60 Tabelle 6.4: Auswertung der OpenCL Kompilate. Die Spalte Anweisungen gibt die Anzahl der arithmetischen Befehle an. Eine Anweisung kann bis zu fünf mathematische Operationen beinhalten. Die Spalte Packungseffizienz gibt wieder, zu welchem Prozentsatz der Compiler die fünf möglichen Operationen innerhalb einer Anweisung mit arithmetischen Operationen des Kernels belegen konnte. Die Anzahl der verwendeten Register pro Thread wird in der Spalte GPR angegeben. Traversierung Algebra LA GA Brute Force LA(!Addr) GA(!Addr) LA kd-tree GA Anweisungen Packungseffizienz GPR 205 50,05 19 255 57,10 26 111 47,39 19 130 63,85 26 602 46,64 36 561 58,57 40 Tabelle 6.5: Auswertung der Brook+ Kompilate. Die Spalte Anweisungen gibt die Anzahl der arithmetischen Befehle an. Eine Anweisung kann bis zu fünf mathematische Operationen beinhalten. Die Spalte Packungseffizienz gibt wieder, zu welchem Prozentsatz der Compiler die fünf möglichen Operationen innerhalb einer Anweisung mit arithmetischen Operationen des Kernels belegen konnte. Die Anzahl der verwendeten Register pro Thread wird in der Spalte GPR angegeben. Die Zeilen mit (!Addr) zeigen die Ergebnisse ohne Anweisungen zur Adressenberechnung der Speicherzugriffe. verwendet als Brook+. Daraus resultiert, dass die Hardware mit dem OpenCL-Kompilat weniger aktive Wavefronts (Gruppen von Threads) zur Überbrückung von Speicherlatenzen vorhalten kann. Brook+ kann aus den zusätzlichen Wavefronts aber anscheinend keinen Nutzen ziehen, wie die Zeitmessungen zeigen. Die geringe Anzahl an Registern für den OpenCL-LA-Kernel mit Brute-Force-Traversierung und somit eine deutlich höhere Anzahl von Wavefronts während der Ausführung erklärt den Performance-Unterschied dieses Kernels gegenüber dem GA-Kernel mit gleicher Traversierung. Der Hardware stehen hier doppelt so viele Wavefronts für den LA-Kernel zur Verfügung. Somit kann die Speicherzugriffslatenz deutlich überdeckt werden. Bei allen anderen Vergleichen zwischen LA- und GA-Kernel unterscheidet sich die Anzahl der aktiven Wavefronts auf Grund des Registerverbrauchs höchstens um eine Wavefront. Diese eine Wavefront reicht nicht aus, um die Performance der LA-Kernel deutlich in Richtung der Performance der GA-Kernel zu verbessern. 76 6.2.3 Dynamische Analyse Die statische Analyse kann nur Anhaltspunkte über den Rechenaufwand der Kernel in Linearer- und Geometrischer Algebra geben. Da die Kernel in jeder Algebra sowohl sequentiellen Programmcode als auch Programmcode in Schleifen enthalten, bedeuten zusätzliche Anweisungen, wie sie in den meistens Kompilaten der LA verglichen zur GA vorkommen, nicht, dass auch im Programmablauf mehr Anweisungen durchgeführt werden. Die zusätzlichen Anweisungen können bei der LA in sequentielle Programmteile vorkommen, während die Schleifen ähnlich viele Anweisungen wie Kerneln der GA enthalten. Aufschluss über den tatsächlichen Rechenaufwand kann daher nur eine Analyse zur Laufzeit der Kernel ergeben, welche die ausgeführten Anweisungen zählt. Diese Analyse ist nur für die OpenCL-Kernel möglich, da für Brook+ ein entsprechendes Tool fehlt. In Tabelle 6.6 werden die Ergebnisse der dynamischen Analyse für das Bunny Modell präsentiert. Traversierung Algebra LA Brute Force GA LA kd-tree GA # ALU 388193 446234 6419 6771 # Speicher % ALU 19355 87,08 19373 58,47 215 42,19 230 49,32 % Speicher Dauer (ms) 69,44 1287 40,57 2203 17,00 44 19,06 40 Tabelle 6.6: Ergebnisse der Laufzeitanalyse. Die Ergebnisse wurden mit dem Bunny-Modell erzielt. Die Spalten # ALU und # Speicher geben die durchschnittlich pro Pixel durchgeführten Berechnungs- und Speicherzugriffsanweisungen wieder. Der Anteil dieser Anweisungen an der gesamten Berechnungszeit eines Pixels wird in den Spalten % ALU und % Speicher angegeben. Da beide Arten von Anweisungen gleichzeitig ausgeführt werden können, sind in der Summe der beiden Spalten auch Werte größer als 100% möglich. In der letzten Spalte steht die Dauer der gesamten Kernelberechnung für ein Bild mit der Auflösung 640x480. Mit beiden Traversierungsarten zeigen die Kernel der Geometrischen Algebra eine leicht erhöhte Anzahl von Berechnungs- und Speicherzugriffsanweisungen verglichen mit der Linearen Algebra. Während die zusätzlichen Berechnungsanweisungen im Fall der BruteForce-Traversierung fünfzehn Prozent ausmachen, schrumpft der Mehraufwand mit der Beschleunigung durch den kd-tree auf knapp fünf Prozent. Der Mehraufwand bei der Brute-Force-Traversierung kann also nicht den signifikanten Unterschied in der Ausführungszeit der LA- und GA-Kernel in Tabelle 6.2 Kapitel 6.2.1 ausmachen. Wie auch in Kapitel 6.2.2 angemerkt, scheint der Unterschied hauptsächlich aus dem Registerverbrauch der beiden Kernel zu resultieren. Anhaltspunkte hierfür liefern die Werte für den Anteil der Berechnungs- und Speicheroperationen an der Berechnungszeit eines Bildpunktes mit den jeweiligen Kerneln (Tabelle 6.6). So führt der LA-Kernel mit Brute-Force-Traversierung fast während seiner gesamten Ausführungszeit Berechnungen durch und während zwei Dritteln der Ausführungszeit wird auf den Speicher zugegriffen. Es treten kaum Wartezeiten für die Berechnung eines Bildpunktes auf. Anders ist es beim GA-Brute-Force-Kernel, welcher nur knapp zu zwei Dritteln seiner aktiven Zeit auch Berechnungen ausführt. Während der restlichen Zeit wartet der Kernel auf Latenzzeiten innerhalb des Grafikprozessors und bei Zugriffen auf den Grafikspeicher. Da sich der LA- und der GA-Kernel nicht wesent77 lich in ihrem Ablauf unterscheiden, ist es eher unwahrscheinlich, dass der höhere Anteil an aktiven Wartezeiten aus der Reihenfolge der Berechnungs- und Speicherzugriffsbefehle allein resultiert. Eine plausible Erklärung ergibt sich aus der Funktionsweise des Grafikprozessors, der die Wartezeiten von Wavefronts (Berechnungseinheiten) mit der Berechnung durch andere Wavefronts ersetzt. Kapitel 6.2.2 hat gezeigt, dass im Falle des LA-Kernels dem Grafikprozessor wesentlich mehr Wavefronts zur Verfügung stehen als mit dem GAKernel. Anders ausgedrückt bedeutet dies, dass die GA-Wavefronts aktiv auf den Speicher warten müssen, weil keine weiteren Wavefronts mehr für Berechnungen bereit sind, während die wartenden LA-Wavefronts durch andere Wavefronts ausgetauscht werden und somit passiv warten. Die aktiven Wartezeiten sorgen bei den GA-Brute-Force-Kerneln hauptsächlich für die schlechtere Performance und den geringeren Anteil der Berechnungszeit eines Pixels an der gesamten Kernel-Ausführungszeit für diesen Pixel. Im Fall der Kernel mit kd-tree-Traversierung kann nicht mit den Wavefronts argumentiert werden. Der schnellere Kernel ist hier der Kernel mit weniger Wavefronts. Der Unterschied von LA zu GA mit fünf zu vier Wavefronts ist jedoch nicht sehr groß. Des Weiteren scheint diese geringe Anzahl von aktiven Wavefronts noch genügend Potential für Wartezeiten bei Speicherzugriffen hervorzurufen, wie die geringen Anteile von aktiver Berechnungs- und Speicherzugriffszeit an der Gesamtzeit einer Pixelberechnung in Tabelle 6.6 zeigen. Die Geometrische Algebra zeigt hier einen leicht höheren Wert als die Lineare Algebra. Die zusätzlichen Berechnungsanweisungen müssen daher während der Wartezeiten auf Speicherzugriffe ausgeführt werden. Mit dieser Platzierung der zusätzlichen Berechnungsanweisungen könnte dann aber nur eine ähnliche Performance wie mit der Linearen Algebra erreicht werden. Die Tatsache, dass die Geometrische Algebra aber knapp zehn Prozent schneller ist, muss dann daran liegen, dass der OpenCL-Compiler für den GA-Kernel eine bessere Mischung aus Berechnungs- und Speicherzugriffsanweisungen findet. Dadurch treten dann weniger Wartezeiten als im Ablauf des LA-Algorithmus auf. Dieses Vorgehen des Compilers würde auch den erhöhten Anteil der Speicherzugriffsanweisungen an der Gesamtzeit im Fall des GA-Kernels gegenüber dem LA-Kernel erklären. Die Differenz zwischen den beiden Algebren beim Anteil der Speicherzugriffsanweisungen an der Gesamtzeit ist deutlich höher als der Unterschied in der Anzahl der pro Pixel ausgeführten Speicherzugriffe. 6.2.4 Einfluss der OpenCL-Umgebung Eine Variable wurde bei den Betrachtungen der Vorteile für die Geometrische Algebra noch nicht betrachtet. Dies ist die OpenCL-Umgebung selbst. Bisher wurden alle Ergebnisse mit der gleichen Hardware und der gleichen Software-Umgebung erzielt. Offen bleibt, ob sich die Ergebnisse auf ähnlichen Grafikkarten von AMD/ATI wiederholen lassen und welche Ergebnisse der Einsatz einer komplett anderen Hardware oder einer OpenCLUmgebung eines anderen Hersteller mit sich bringt. Diese Tests sollen in diesem Kapitel nachgeholt werden. Für die weitergehenden Test wurden neben der in Kapitel 2.1.3 beschriebenen Grafikkarte HD4850 weitere Karten von AMD aus der fünften Generation HD5XXX verwendet. Diese Generation von Grafikkarten stellt eine Weiterentwicklung der HD4XXX Generationen dar. Der Grafikchip enhält ebenfalls SIMD-Einheiten zu sechzehn ALUs mit fünf Rechenwerken. Die Rechenwerke der ALUs wurden leicht überarbeitet, so dass in der aktuellen Generation 78 Grafikkarte Leistung Bandbreite HD4850 HD5670 HD5870 GTX 280 100(%) 58(%) 256(%) 170(%) 100(%) 101(%) 242(%) 221(%) Chameleon LA GA 0,193 0,323 0,529 0,486 0,127 0,113 0,133 0,226 Moai LA GA 0,454 0,759 1,246 1,145 0,295 0,266 0,313 0,532 Bunny LA GA 1,284 2,144 3,528 3,251 0,833 0,754 0,885 1,507 Tabelle 6.7: Zeitmessung der Brute-Force-Kernel auf anderen Grafikkarten. Die Referenz für den Grundwert stellen die Ergebnisse der HD4850 Grafikkarten mit dem ATI Stream SDK 2.1 aus Tabelle 6.2 dar. Die hier dargestellten Ergebnisse wurden mit dem ATI Stream SDK 2.2 bzw. dem CUDA Toolkit 3.1.1 erzielt. In den Spalten Leistung und Bandbreite werden die theoretische Rechenleistung der Grafikkarten und die Speicherbandbreite in Relation zur Referenzkarte angegeben. Die weiteren sechs Spalten zeigen die Ausführungszeiten der LA- und GA-Kernel für verschiedene Surfel-Modelle in Sekunden. Grafikkarte Leistung Bandbreite HD4850 HD5670 HD5870 GTX 280 100(%) 58(%) 256(%) 170(%) 100(%) 101(%) 242(%) 221(%) Bunny LA GA 43,9 73,2 75,2 67,9 15,8 14,3 24,1 30,7 Max Planck EG07 Dragon LA GA LA GA 49,2 81,2 75,9 121,0 78,2 70,6 119,3 106,7 17,7 15,9 27,3 24,5 38,1 48,2 42,7 54,6 Tabelle 6.8: Zeitmessung der kd-tree-Kernel auf anderen Grafikkarten. Die Referenz für den Grundwert stellen die Ergebnisse der HD4850 Grafikkarten mit dem ATI Stream SDK 2.1 aus Tabelle 6.3 dar. Die hier dargestellten Ergebnisse wurden mit dem ATI Stream SDK 2.2 bzw. dem CUDA Toolkit 3.1.1 erzielt. Die weiteren sechs Spalten zeigen die Ausführungszeiten der LA- und GA-Kernel für verschiedene Surfel-Modelle in Millisekunden. Grafikkarte Leistung Bandbreite Speedup Brute-Force LA GA HD4850 100(%) 100(%) 1,00 1,03 HD5670 58(%) 101(%) 0,37 0,68 HD5870 256(%) 242(%) 1,54 2,94 GTX 280 170(%) 221(%) 1,45 1,47 Speedup kd-tree LA GA 1,00 0,55 0,62 0,62 2,78 2,78 1,60 1,13 Tabelle 6.9: Speedup der Kernel auf anderen Grafikkarten. Die Referenz für den Grundwert stellen die Ergebnisse der HD4850 Grafikkarten mit dem ATI Stream SDK 2.1 aus Tabelle 6.3 dar. Die hier dargestellten Ergebnisse wurden mit dem ATI Stream SDK 2.2 bzw. dem CUDA Toolkit 3.1.1 erzielt. Der gemittelte relative Speedup über die drei Modelle aus den Tabellen 6.7 und 6.8 gegenüber der Zeitmessung aus Tabelle 6.3 wird in den letzten vier Spalten angegeben. 79 alle Rechenwerke auch bitweise Operationen beherrschen. Die für diese Arbeit wichtigen Float-Fähigkeiten der ALUs unterscheiden sich hingegen nicht sonderlich zwischen den Generationen. Somit ist ein direkter Vergleich der Performance ausgehend von der Anzahl der ALUs, der Taktfrequenz des Grafikprozessors und der Speicherbandbreite möglich. Alle Zeitmessungen, die zu den Ergebnissen in den Tabellen 6.7, 6.8 und 6.9 geführt haben, wurden mit dem ATI Stream SDK 2.2 durchgeführt. Als Referenzwert für die einhundert Prozentmarke gelten die Ergebnisse aus den Tabellen 6.2 und 6.3, die mit dem ATI Stream SDK 2.1 erzielt wurden. Um den Einfluss der Speicherbandbreite auf die Performance für die Algorithmen der Linearen und der Geometrischen Algebra zu ermitteln, wurden Zeitmessungen mit einer HD5670 Grafikkarte durchgeführt. Diese Karte hat annähernd die gleiche Speicherbandbreite wie die HD4850 Karte aber nur knapp 60 Prozent der theoretischen Rechenleistung. Weiterhin wurden Zeitmessungen mit einer HD5870 Karte durchgeführt. Bei dieser Grafikkarte wurde die theoretische Rechenlast stärker als die Speicherbandbreite im Vergleich zur HD4850 Karte erhöht. Somit wird deutlich, wie die Algorithmen mit einer reduzierten Speicherbandbreite arbeiten. Neben zusätzlichen Grafikkarten von AMD wurden auch auf einer Nvidia GTX 280 Grafikkarte Zeitmessungen durchgeführt. Da Nvidia skalare ALUs mit nur einem Rechenwerk verwendet, sollte sich auf der Nvidia Grafikkarte deutlich der Mehraufwand an Berechnungen der Geometrischen Algebra zeigen. Gleichzeitig wird somit belegt, dass die verbesserten Zeiten der GA auf AMD Grafikkarten tatsächlich auf die parallelen ALUs zurückzuführen ist. Auf Grund der unterschiedlichen Architektur der Nvidia und AMD Grafikkarten ist ein direkter Vergleich der theoretischen maximalen Leistung der Grafikkarten nicht sinnvoll durchführbar, da die AMD Karten nicht immer alle fünf Rechenwerke der ALUs auslasten können. Somit erreichen die AMD Karten nur in seltenen Fällen ihre maximale Leistungsfähigkeit, während die maximale Leistungs bei Nvidia weniger durch Abhängigkeiten im Kernelcode beeinflusst wird. Auf Basis der mitteleren statischen Packungsdichte für die verschiedenen Raytracing-Kernel soll dennoch die relative Leistung der Nvidia Grafikkarte zur Referenzkarte HD4850 ermittelt werden. Mit einer mittleren Packungsdichte der Kernel von 51% erreicht die Grafikkarte GTX 280 170% der Leistung der HD4850. Die Zeitmessungen der Nvidia GTX 280 wurden auf Gentoo Linux mit dem CUDA Toolkit 3.1.1 durchgeführt. Den Ergebnissen der Tabellen 6.7, 6.8 und 6.9 kann entnommen werden, dass die Algorithmen direkt mit der theoretischen Rechenleistung der AMD Grafikkarten skalieren. Die Speicherbandbreite ist in jedem Fall ausreichend. Weiterhin zeigt sich, dass der Softwarestack der OpenCL-Umgebung einen deutlichen Einfluss auf die Performance haben kann. Im Fall des GA-Kernels mit kd-tree-Traversierung erzeugt das SDK2.2 für die HD4850 Karte schlechteren Assemblercode als mit dem SDK2.1. Der Code wird zwar in seiner Länge kürzer, verbraucht aber ein Register mehr. Des Weiteren zeigt die dynamische Analyse, dass wesentlich längere Wartezeiten im SDK2.2 Code entstehen und die HD4850 GPU nur noch zu 26 Prozent der Ausführungszeit auch Berechnungen durchführt. Auf Grund dieser Verschlechterung des Assemblercodes ist die HD4850 Karte mit dem SDK 2.2 und dem GA-kd-tree-Kernel langsamer als eine HD5670 Karte, die nur 60 Prozent der Rechenleistung aufweist. Dieser Unterschied zeigt auch, dass der Compiler recht unterschiedlichen Assemblercode für die verschiedenen Grafikkartengenerationen erzeugt. Dies wird auch beim LA-Brute-Force-Kernel deutlich, der auf den HD5XXX Karten langsamer läuft als der entsprechende GA-Kernel. Beim Vergleich des Assemblercodes der HD4850 und HD5XXX Karten zeigt der HD5XXX Assembler wesentlich weniger Anweisungen als der HD4850 80 Assembler und verbraucht zwei Register mehr. In der dynamischen Analyse weist der HD5XXX Assemblercode für den LA-Brute-Force-Kernel einen wesentlich geringeren Anteil der Berechnungen an der Ausführungszeit von 55 Prozent gegenüber den 87 Prozent auf der HD4850 Karte auf. In beiden betrachteten Fällen ist die Anzahl der ausgeführten Operationen nahezu gleich und kann keinen starken Einfluss auf den gemessenen Unterschied in der Ausführungszeit haben. Neben diesen beiden negativen Fällen hat sich die Performance der Kernel mit dem neueren SDK aber leicht verbessert. Der gegenüber der Theorie deutlich höhere Speedup bei einigen der Kernel deutet auf eine bevorzugte Behandlung der HD5XXX Karten seitens AMD hin. Die Unterstützung der HD4850 Karte befindet sich sowohl im SDK 2.1 als auch im SDK 2.2 laut dem AMD Getting Started Guide für das ATI Stream SDK [3] im Beta-Stadium, während die HD5XXX Karten vollständig unterstützt werden. Die Zeitmessungen mit der GTX 280 Grafikkarte zeigen deutlich den Mehraufwand der GA. Mit der kd-tree Traversierung liegt der Mehraufwand bei 30 Prozent des Aufwands für die LA-Berechnungen. Der Einsatz der Brute-Force-Traversierung steigert den Mehraufwand auf 80 Prozent. Da auch bei den Nvidia Grafikkarten der Registerverbrauch der Kernel einen bedeutenden Einfluss auf die Fähigkeit der Grafikkarte zur Verdeckung von Speicherlatenzzeiten hat, kann der höhere Mehraufwand bei den Brute-Force-Kerneln nachfolgend erklärt werden. Unter der Annahme, dass der Kompiler von Nvidia ein ähnliches Verhältnis von verwendeten Registern zwischen den LA- und GA-Kerneln wie der AMD Kompiler erzeugt, ist der Unterschied in der Anzahl verbrauchter Register für die BruteForce-Kernel größer als für die Kernel mit kd-tree-Traversierung. Da Nvidia skalare Register verwendet, wobei die Anzahl der Register auf Nvidia und AMD Grafikkarten pro SIMDEinheit gleich ist, verschärft sich die Lage für den GA-Kernel auf Nvidia-Grafikkarten. Auf dieser Hardware steht nur ein Viertel der Register verglichen mit AMD Grafikkarten zur Verfügung. Die Nvidia Grafikkarte kann während der Ausführung des LA-Brute-ForceKernels wesentlich besser die Speicherlatenzen verdecken als mit dem entsprechenden GA-Kernel. Weiterhin zeigen die Ergebnisse der GTX 280 Karte, dass die Geschwindigkeit der HD 4850 Grafikkarte mit der Brute-Force-Traversierung hauptsächlich vom Registerverbrauch der LA- und GA-Kernel abhängen muss. Da die GTX 280 mit dem gleichen Faktor im Vergleich zur HD4850 mit LA und GA skaliert, kann geschlussfolgert werden, dass die GTX 280 Karte den Mehraufwand der GA-Berechnungen weitgehend in den Zeiten der Speicherzugriffslatenzen verstecken kann. Auf der HD 4850 Karte muss der Kernel hingegen auf Grund des Registerverbrauchs aktiv während der Speicherlatenzen warten, so dass die Karte gegenüber der GTX280 in diesem Fall keinen Vorteil aus den parallelen ALUs ziehen kann. Umgebung Chameleon Bunny LA GA LA GA ATI Stream SDK 2.2 9,9 11,7 67 77 Fixstars FOXC (12/2009) 39,9 47,9 261 320 Tabelle 6.10: Zeitmessung der Brute-Force-Kernel mit OpenCL-Umgebungen für CPUs. Die Hardware Grundlage bildet ein AMD Athlon X2 5600. Während die AMD Ergebnisse auf Windows Vista 32bit erzielt wurden, fand die Zeitnahme der Ergebnisse mit FOXC unter CentOS 5.5 64bit statt. Die Einheit der Werte ist Sekunden. 81 Eine weitere interessante Frage ergibt sich aus der Betrachtung von OpenCLUmsetzungen für die SSE-Vektoreinheiten aktueller CPUs. Kann die Geometrische Algebra auch von diesen vierfach parallelen Vektoreinheiten aktueller CPUs mit den aktuellen OpenCL-SDKs profitieren? Einen Eindruck können die Ergebnisse aus Tabelle 6.10 vermitteln. Es wurden sowohl mit dem ATI Stream SDK unter Windows Vista als auch mit der FOXC OpenCL-Umgebung von Fixstars auf der Linux-Distribution CentOS 5.5 Zeitmessungen vorgenommen. Die Ergebnisse können aber nur als eine Tendenz aufgefasst werden, da beiden OpenCL-Umgebungen ein Autovektorisierer, wie er im ATI Stream SDK für die Berechnung auf Grafikkarten vorhanden ist, fehlt. Die Performance hängt somit maßgeblich von der geschickten Programmierung der Kernel und dem händischen Aufteilen der Berechnungen auf Vektorberechnungen ab. Dieses Vorgehen wurde in dieser Arbeit nicht verfolgt, da die Hauptarchitektur durch die AMD Grafikkarten dargestellt wird. Die Kernel der Geometrischen Algebra sollten jedoch keinen unfairen Vorteil gegenüber der Linearen Algebra haben, da die vom Tool Gaalop [28] für die GA erzeugten Berechnungsfunktionen nur sequentielle Berechnungen enthalten. Die Messungen wurden nur mit Brute-ForceTraversierung durchgeführt, um den Einfluß der Traversierung auf die Berechnungen der beiden Algebren gering zu halten. Da mit der Brute-Force-Traversierung die SchnitttestOperation überwiegt, sollte sich der Unterschied in den gemessenen Zeiten zwischen LA und GA beim Faktor 1,3 für den Mehraufwand der GA gegenüber der LA bewegen. Trotz fehlender Autovektorisierung ist der Performanceverlust durch Verwendung der Geometrischen Algebra mit beiden OpenCL-Umgebungen sogar geringer als 1,3. Es zeigt sich somit zusammen mit den Ergebnissen der Messungen auf Grafikkarten ein deutliches Potenzial für die Autovektorisierung von GA-Berechnungen für die SIMD-Einheiten von CPUs, um den Mehraufwand der GA zu verringern. 6.2.5 Folgerung Die Frage, ob die Geometrische Algebra gegenüber der Linearen Algebra von der parallelen Hardware profitiert, kann nun beantwortet werden. Die Betrachtung der Auswertung der Kompilate zeigt, dass die Kompilate der Geometrischen Algebra immer eine höhere Packungseffizienz als die jeweiligen Kompilate der Linearen Algebra aufweisen. Weiterhin zeigt sich, dass die höhere Packungseffizienz ausreicht, den Unterschied in der Anzahl ausgeführter Anweisungen von den theoretisch angenommenen 30% je nach Traversierungsart der Szene auf 5% bis 10 % zu reduzieren. Abhängig von der Architektur des verwendeten Prozessors und des zugehörigen Compilers kann dieser Mehraufwand an Berechnungen in Wartelatenzzeiten bei Speicherzugriffen versteckt werden. Ein Nachteil der Algorithmen in Geometrischer Algebra ist der höhere Verbrauch an Speicherressourcen gegenüber Algorithmen in Linearer Algebra. Der OpenCL-Compiler kann diesen höheren Ressourcenverbrauch nicht so gut behandeln wie der Brook+-Compiler. Es wird aber auch deutlich, dass sich der OpenCL-Compiler des ATI-Stream-SDK noch in der Entwicklung befindet, was die teils deutlichen Unterschiede in der Laufzeit einiger Kernel beim Wechsel der SDK-Version belegen. Der Hardware stehen für jede der SIMD-Einheiten 248 Register für einen Thread einer Wavefront (Gruppe aus 64 Threads) zur Verfügung. Wenn die Wavefront für ihre Threads jeweils weniger als die 248 Register benötigt, um alle Daten der Algorithmenausführung eines Threads zu speichern, kann ab einem Bedarf von 124 Registern eine zweite Wavefront mit weiteren 64 Threads auf der SIMD-Einheit aktiv gehalten 82 werden. Für den Fall der Brute-Force-Traversierung kann die Hardware sechzehn Wavefronts des LA-Algorithmus aber nur sieben Wavefronts des GA-Algorithmus aktiv halten. Wenn nun eine Wavefront auf die Ergebnisse eines Speicherzugriffs wartet, kann die Hardware mit dem LA-Algorithmus in dieser Zeit wesentlich mehr arithmetische Operationen aus anderen Wavefronts berechnen lassen als mit dem GA-Algorithmus. Dies zeigt auch die dynamische Analyse. In den Fällen, in denen der Kernel mit dem Wechsel der OpenCL-Umgebung weniger Register verbraucht, steigt gleichzeitig auch der Anteil aktiver Berechnungen an der gesamten Berechnungszeit eines Pixels. Im Fall der kd-tree-Traversierung ist das Verhältnis von LA- zu GA-Wavefronts fünf zu vier. Dieser geringere Unterschied aktiver Wavefronts wird auch in den Zeitmessungen deutlich. In Brook+ hat der Unterschied in der Anzahl der Wavefronts einen geringeren Einfluss auf die Ausführungszeit der Kernel, da Brook+ den auf der Hardware vorhandenen Cache für die Speicherzugriffe nutzen kann. Wird aus dem Cache an Stelle des Grafikspeichers gelesen, so verringern sich die Latenzen. Für die verringerten Latenzen müssen zur Überbrückung mit Berechnungen wiederum weniger Wavefronts aktiv gehalten werden. Des Weiteren zeigen die geringen Verbesserungen des Mehraufwands für die GAAlgorithmen gegenüber der LA auf der CPU im Vergleich zu den Ergebnissen auf AMD Grafikkarten, wie wichtig eine Vektorisierung der Berechnungen ist. Ohne die Vektorisierung der Berechnungen per Hand oder durch ein geeignetes Tool ist die Geometrische Algebra mit den verwendeten Algorithmen deutlich langsamer als die Lineare Algebra. 6.3 Vergleich mit anderen Verfahren Zur Darstellung von Punktwolken wurden schon einige Verfahren entwickelt. In diesem Kapitel wird das in der Arbeit vorgestellte Verfahren mit dem SLIM-Modell [43] verglichen. Die SLIM-Modelle basieren auch auf einer Surfel-Repräsentation der Oberfläche. Des Weiteren sind sowohl der Quellcode zur Applikation des Papers als auch die verwendeten Modelle verfügbar, so dass die Implementierung der SLIM-Algorithmen sowie die Algorithmen aus dieser Arbeit auf der gleichen Hardware verglichen werden können. Da die SLIM-Applikation nur die CPU verwendet, um die Visualisierung der Punktwolken zu berechnen, wird die OpenCL-Umgebung so eingestellt, dass auch die Algorithmen aus dieser Arbeit auf der CPU ausgeführt werden. Da OpenCL auf der CPU die Vektorerweiterung SSE verwendet, sind die Ergebnisse aus Kapitel 6.2 auch hier teilweise gültig. Auf der CPU fehlt hingegen eine automatische Einteilung der Berechnungen auf Vektoroperationen. Die Geometrische Algebra kann daher nicht so stark wie auf der GPU von den parallelen Recheneinheiten profitieren. Zur besseren Vergleichbarkeit werden nur SLIM-Modelle mit quadratischen Funktionen verwendet. Auch die in dieser Arbeit verwendeten Kugeln können über quadratische Funktionen dargestellt werden. Der SLIM-Algorithmus kann eine Normaleninterpolation ähnlich zur Interpolation in dieser Arbeit verwenden. Es werden sowohl ohne als auch mit hinzugeschalteter Interpolation Vergleiche durchgeführt. Abbildung 6.8 zeigt ein Modell eines Moai. Das Modell dient als Beispiel für eine relativ kleine Punktwolke mit nur 10002 Punkten. Zum Vergleich wird links ein Dreiecksmodell mit Flat-Shading dargestellt. Im Dreiecksmodell wird deutlich sichtbar, dass die Punkte des Moai von einem leichten Rauschen in den Koordinaten überlagert werden. Die mittelere Abbildung zeigt eine Visualisierung mit den Algorithmen dieser Arbeit. Die Interpolation 83 Abbildung 6.8: Das Moai-Modell. Links wird zum Vergleich das zu Grunde liegende Dreiecksmodell mit Flat-Shading, in der Mitte das GA-Modell und rechts das SLIM-Modell dargestellt. Interpolation wird nicht verwendet. Auf Grund unterschiedlicher Kameraparameter unterscheidet sich die Perspektive der Darstellungen. wird nicht verwendet. Obwohl die Flächen des Moai bis auf das Rauschen relativ glatt sind, zeigen sich an den Armen der abgebildeten Figur Artefakte. Die Algorithmen zur Modellerzeugung berechnen hier Kugeln mit zu kleinem Radius. Die rechte Abbildung zeigt das Ergebnis der SLIM-Visualisierung ohne Interpolation. Das SLIM-Modell ist trotz fehlender Interpolation deutlich glatter. Der Algorithmus zur SLIM-Berechnung scheint wesentlich besser mit dem Rauschen in den Punktpositionen umgehen zu können und insgesamt numerisch stabiler zu sein. Eine Visualisierung mit Interpolation ist in Abbildung 6.9 dargestellt. Während die Artefakte in der mit Geometrischer Algebra berechneten Abbildung reduziert werden, verschwinden sie vollständig auf der rechten Seite in der SLIM-Darstellung. Die Interpolation kann für die GA nicht mehr viel ausrichten, da das zu Grunde liegende Modell schon zu große Artefakte zeigt. SLIM profitiert neben dem artefaktfreien Modell auch von seinem Multiskalenaufbau. Ein SLIM Modell besteht aus einer Hierarchie verschieden großer Surfel. Detailarme Flächen werden von wenigen großen SLIM-Surfeln repräsentiert, während Details von mehreren verschieden großen Surfeln repräsentiert werden. Somit stehen für die SLIM-Visualisierung für detailreiche Flächen mehr Informationen zur Verfügung als im GA-Modell. In Abbildung 6.10 wird der Stanford-Dragon als Beispiel für eine größere Punktwolke von 437645 Punkten gezeigt. Auf der linken Seite ist das GA-Modell ohne Interpolation zu sehen. Da wesentlich mehr Surfel als für den Moai verwendet werden, wirkt die Darstellung wesentlich besser. Es sind aber auch beim Dragon Artefakte von Kugeln zu sehen, die an diesen Teilen der Oberfläche eine geringere Krümmung haben sollten. Auch 84 Abbildung 6.9: Moai mit Interpolation. In der Darstellung mit Geometrischer Algebra im linken Bild werden die Artefakte deutlich reduziert. Im rechten Bild zeigt die Darstellung des SLIM-Modells ein sehr gutes Ergebnis ohne Artefakte. hier verstärkt sich die Vermutung, dass der Algorithmus zur Berechnung der Surfel Stabilitätsprobleme hat. Die SLIM-Darstellung hat auch ohne Interpolation schon eine hohe Qualität. Den Dragon mit Interpolation zeigt Abbildung 6.11. Beim GA-Modell auf der linken Seite werden die Artefakte wieder reduziert, während die Interpolation beim SLIM-Modell zu einer exzellenten Darstellung führt. Moai Moai (Interpolation) Dragon Dragon (Interpolation) GA-Raytracing 0,33 0,65 0,84 1,68 SLIM 0,11 0,22 0,23 0,47 Tabelle 6.11: Vergleich der Rendergeschwindigkeit zwischen der Darstellung mit Geometrischer Algebra und SLIM. Dei Zeitmessung wurde auf einer AMD Athlon X2 CPU mit 2,8 GHz vorgenommen. Es wurden jeweils Zeitmessungen mit der Berechnung des nächsten Schnittpunkts sowie mit der Berechnung des interpolierten Schnittpunkts und der interpolierten Normale vorgenommen. Die Angaben sind in Sekunden. Der Vergleich von Zeitmessungen zum Rendern der GA- und SLIM-Modelle zeigt, dass SLIM sowohl ohne als auch mit Interpolation schneller rendert als der in dieser Arbeit verwendete Raytracingalgorithmus. Zum Rendern verwendet SLIM einen zu dieser Arbeit unterschiedlichen Ansatz um die Oberflächen mit Strahlen von der Kamera durch die Pixel der Bildebene zu schneiden. Während beim klassischen Raytracing für einen Strahl der Raum nach den Strahl schneidenden Objekten untersucht wird, berechnet das SLIM Verfahren in einem ersten Schritt die Pixel des Bildes, die von der Darstellung eines Sur85 Abbildung 6.10: Das Dragon Modell des Stanford 3D Scanning Repository. Die Darstellungen links mit GA und rechts mit SLIM werden ohne Interpolation berechnet. fels potenziell belegt werden. Hierzu wird basierend auf dem Mittelpunkt des Surfels und dessen Radius ein Rechteck in der Bildebene festgelegt, dass das Abbild der das Surfel umschließenden Kugel auf der Bildebene einrahmt. Für alle Pixel innerhalb dieses Rechtecks werden dann Strahlen berechnet und genau mit diesem Surfel geschnitten. Um die Verdeckung zwischen den verschiedenen Surfeln der Szene richtig zu berechnen wird ein Z-Buffer mit Tiefeninformationen für jedes Pixel der Bildebene verwendet. Nur wenn ein Schnitt mit einem Surfel eine geringere Tiefe als die im Z-Buffer gespeicherte Tiefe aufweist, wird für den Schnitt das zugehörige Pixel aktualisiert. Der SLIM-Algorithmus muss im Vergleich zum in der Arbeit verwendeten Raytracing gar nicht ermitteln welcher Strahl welche Surfel treffen könnte, da die Strahlen genau für ein Surfel erstellt werden. Surfel, deren umschließendes Rechteck nicht im Bild liegt, erzeugen keine Strahlen und können frühzeitig verworfen werden. Einen Nachteil hat der genannte Ansatz, wenn die Tiefenkomplexität der Modelle stark zunimmt und die Surfel in einer ungünstigen Reihenfolge getestet werden. In diesem Fall steigt der Aufwand der Berechnung linear mit der Anzahl der Surfel. Der in der Arbeit verwendete Ansatz mit einem kd-tree erzeugt bei steigender Surfelzahl hingegen einen logarithmischen Anstieg des Berechnungsaufwands. Da der kdtree aber schon eine hohe Grundlast an Berechnungen aufweist, die dann nur langsam mit steigender Surfelanzahl wächst, während der SLIM-Ansatz eine relativ geringe Grundlast aufweist, zeigt sich der Vorteil des kd-trees bei den verwendeten Szenen noch nicht. Ein weiteres Problem des Z-Buffer Ansatzes tritt auf, wenn die Surfel eine Fläche kleiner als ein Pixel auf dem Bildschirm belegen, da dann für ein Pixel sehr viele unnötige Schnittberechnungen durchgeführt werden. SLIM begegnet dem Problem, indem in der Hierarchie der Surfel nur solange abgestiegen wird, dass die Surfel noch mehrere Pixel belegen. Zusammengefasst führt die geringere Grundlast des SLIM-Darstellungsalgorithmus und die Hierarchie der SLIM-Surfel dazu, dass die SLIM-Modelle schneller dargestellt werden können als die GA-Modelle. 86 Abbildung 6.11: Der Stanford-Dragon mit Interpolation der Schnittnormalen 6.4 Fazit In dieser Arbeit wurde ein neues Surfel-Modell für punktbasierte Szenen eingeführt. Die Surfel basieren auf den geometrischen Objekten Kugel und Ebene. Auf Grund dieser Eigenschaft ist das Surfel-Modell sehr gut für Berechnungen mit Konformer Geometrischer Algebra geeignet. Die genannten geometrischen Objekte sind direkt als Objekte der Algebra in Geometrischer Algebra vorhanden. Zur Modellerzeugung wurde ein vollständig parallelisierter Algorithmus entworfen. Somit profitiert der Algorithmus auch in Zukunft von der Entwicklung der parallelen Prozessoren wie Mehrkern-CPUs und GPUs. Dabei wurden die einzelnen Teilstufen so ausgewählt, dass sie gut mit dem OpenCL-Programmiermodell harmonieren. Dadurch ist der Algorithmus zur Modellerzeugung portabel auf verschiedenen Prozessoren einsetzbar. Die Zielplattform des Algorithmus stellen jedoch GPUs dar. Aus diesem Grund wurde der Algorithmus zur Berechnung der Eigenwerte extra für die effiziente Ausführung auf Grafikprozessoren ausgewählt. Zusätzlich zum Surfel-Modell wurde ein Visualisierungsverfahren eingeführt, mit dem die Surfel-Modelle punktbasierter Szenen in computergenerierten Bildern dargestellt werden können. Das Visualisierungsverfahren verwendet einen Raytracingalgorithmus. Der Raytracingalgorithmus wird mit Hilfe der räumlichen Datenstruktur kd-tree beschleunigt. Auf diese Weise sind interaktive Bildraten für mittelgroße Modelle bei der Bildberechnung auf der CPU möglich. Eine zusätzlichen Implementierung des Raytracingalgorithmus in der OpenCL-Umgebung ermöglicht mit der Ausführung auf GPUs selbst für große Modelle interaktive Bildraten. Der Raytracingalgorithmus wurde sowohl in Konformer Geometrischer Algebra als auch in Linearer Algebra umgesetzt. Mit Hilfe von Zeitmessungen und der Analyse des von OpenCL erzeugten hardwarespezifischen Assemblercodes konnte gezeigt werden, dass die Algorithmen in GA besser von ILP-ausnutzender Hardware profitieren als die Algorithmen in LA. Somit kann der theoretisch höhere Rechenaufwand der GA-Algorithmen abgefangen werden. In einzelnen Fällen sind die Algorithmen in Geometrischer Algebra sogar leicht schneller als in Linearer Algebra. Neben dem Vergleich zwischen GA und LA wurde das hier vorgestellte Visualisierungsverfahren mit dem SLIM-Verfahren [43] zur Darstellung von Punktwolken verglichen. So87 mit ist eine Einordnung des Verfahrens dieser Arbeit möglich. Das hier besprochene SurfelModell erreicht bei kleinen Punktwolken nicht die visuelle Qualität des SLIM-Verfahrens. Bei größeren Punktwolken ist die Qualität jedoch vergleichbar, obwohl sie noch immer leicht unterlegen ist. Die Darstellungsgeschwindigkeit beider Verfahren liegt auf der CPU in der gleichen Größenordnung. 6.5 Ausblick Die zentrale Frage dieser Arbeit nach dem größeren Vorteil paralleler Recheneinheiten für Algorithmen in Geometrischer Algebra verglichen mit Algorithmen in Linearer Algebra konnte vollständig beantwortet werden. Dennoch existieren einige Verbesserungsmöglichkeiten für das hier umgesetzte Visualisierungsverfahren, um es noch stärker in visueller Qualität und Geschwindigkeit etablierten Verfahren anzunähern. Im Modellerzeugungsprozess muss ein stabiler Fitting-Algorithmus zur Berechnung der Surfeloberflächen gefunden werden. Eine Möglichkeit den Prozess zu verbessern besteht in der Einführung von Normalen [49]. Für Punktwolken die von Dreiecksnetzen stammen liegen Normalen meist schon vor oder können auf Grund der Konnektivität der Dreiecke berechnet werden. Des Weiteren können einige Laserscanner die Konnektivität verschiedener abgetasteter Punkte während des Scannens ermitteln. Somit kann auch direkt aus diesen Punktwolken eine Normale ermittelt werden. Mit Hilfe der Normale wird die Suche nach Eigenvektoren auf eine 3 × 3 Matrix beschränkt. Bei der Lösung des Eigenwertproblems dieser Matrix treten auf Grund der verringerten Anzahl der Einträge weniger Fehler bei den Gleitkommaberechnungen auf. Zusätzlich kann die Lösung schneller als bei der hier verwendeten 5 × 5 Matrix berechnet werden. Ein weiterer Nachteil des hier vorgestellten Algorithmus ist die Abhängigkeit von möglichst uniform über die Oberfläche verteilten Punkten. Da der LOP-Algorithmus einen starken Anteil an der Laufzeit des hier vorgestellen Algorithmus 2 aufweist, sollten weitere Möglichkeiten untersucht werden, eine gleichförmige Verteilung der Punkte zu erhalten. Für Punktwolken mit vorhandener Konnektivität der Punkte können verschiedene Remeshing-Verfahren angewendet werden, um die Verteilung der Punkte zu verbessern. Für ein Dreiecksnetz scheint der Algorithmus aus [21] sehr vielversprechend zu sein. Eine Beschleunigung des Renderings ist hauptsächlich über die Verwendung eines besseren kd-tree-Erstellungsalgorithmus möglich. Der implementierte Algorithmus wendet immer nur den einfachen Objektmediansplit an. Zumindest nahe der Blätter des kd-trees sollte der SAH-Algorithmus zur Wahl der Teilungsebene verwendet werden. Mit dem Algorithmus von Hunt et al. [30] ist sogar die Erstellung eines kd-trees für interaktive Anwendungen mit nahezu SAH-Qualität möglich. 88 Literaturverzeichnis [1] Aim@shape project - shape repository. http://shapes.aim-at-shape.net/, March 2007. [2] The Stanford 3D Scanning Repository. 3Dscanrep/, March 2010. http://graphics.stanford.edu/data/ [3] Inc. Advanced Micro Devices. ATI Stream SDK Getting Started Guide (v2.2). http://developer.amd.com/gpu/ATIStreamSDK/assets/ATI_Stream_ SDK_Getting_Started_Guide_v2.2.pdf, September 2010. [4] Inc. Advanced Micro Devices. Stream KernelAnalyzer. http://developer.amd.com/ GPU/SKA/Pages/default.aspx, February 2010. [5] Tomas Akenine-Möller, Eric Haines, and Natty Hoffman. Real-Time Rendering 3rd Edition. A. K. Peters, Ltd., Natick, MA, USA, 2008. [6] Marc Alexa, Johannes Behr, Daniel Cohen-Or, Shachar Fleishman, David Levin, and Claudio T. Silva. Point set surfaces. In VIS ’01: Proceedings of the conference on Visualization ’01, pages 21–28, Washington, DC, USA, 2001. IEEE Computer Society. [7] Gene Amdahl. Validity of the single processor approach to achieving large-scale computing capabilities. In AFIPS Conference Proceedings, pages 483–485, 1967. [8] James F. Blinn. Models of light reflection for computer synthesized pictures. SIGGRAPH Comput. Graph., 11(2):192–198, 1977. [9] Hilary Bowdler, R. S. Martin, C. Reinsch, and J. H. Wilkinson. The QR and QL algorithms for symmetric matrices. Numerische Mathematik, 11(4):293–306, 1968. [10] Carl B. Boyer. A History of Mathematics (Second Edition ed.), chapter The Heroic Age, pages 77–78. John Wiley and Sons, 1991. [11] Simon Bratel. Deferred rendered radiosity from first person perspective. Master’s thesis, IT university of Göteborg, 2007. [12] Ian Buck, Tim Foley, Daniel Horn, Jeremy Sugerman, Kayvon Fatahalian, Mike Houston, and Pat Hanrahan. Brook for gpus: Stream computing on graphics hardware. ACM TRANSACTIONS ON GRAPHICS, 23:777–786, 2004. [13] W. Clifford. Applications of grassmann’s extensive algebra. American Journal of Mathematics, 1(4):350–358, 1878. [14] Robert Davies. Newmat10 documentation. http://www.robertnz.net/nm10.htm, April 2006. [15] Eugene d’On and David Luebke. Advanced techniques for realistic real-time skin rendering. In Hubert Nguyen, editor, GPUGems 3, pages 293–345. Addison-Wesley, 2007. 89 [16] Tim Foley and Jeremy Sugerman. Kd-tree acceleration structures for a gpu raytracer. In HWWS ’05: Proceedings of the ACM SIGGRAPH/EUROGRAPHICS conference on Graphics hardware, pages 15–22, New York, NY, USA, 2005. ACM. [17] D. Fontijne, T. Bouma, and L. Dorst. Gaigen 2: A geometric algebra implementation generator. http://staff.science.uva.nl/ fontijne/gaigen2.html. [18] D. Fontijne and L. Dorst. Performance and elegance of 5 models of geometry in a ray tracing application. Software and other downloads available at http://www.science.uva.nl/∼fontijne/raytracer, 2002. [19] Daniel Fontijne. Efficient Implementation of Geometric Algebra. PhD thesis, University of Amsterdam, 2007. [20] Silvia Franchini, Antonio Gentile, Filippo Sorbello, Giorgio Vassallo, and Salvatore Vitabile. An embedded, fpga-based computer graphics coprocessor with native geometric algebra support. Integr. VLSI J., 42(3):346–355, 2009. [21] Simon Fuhrmann. Curvature-adaptive and feature-sensitive isotropic surface remeshing. Master’s thesis, Technische Universität Darmstadt, Germany, 2009. [22] Markus Geimer and Stefan Müller. A cross-platform framework for interactive ray tracing. In Tagungsband Graphiktag der Gesellschaft für Informatik, pages 25–34, Frankfurt/Main, Germany, September 2003. [23] Hermann Grassmann. Die lineale Ausdehnungslehre ein neuer Zweig der Mathematik: dargestellt und durch Anwendungen auf die übrigen Zweige der Mathematik, wie auch auf die Statik, Mechanik, die Lehre vom Magnetismus und die Krystallonomie erläutert. O. Wigand, 1844. [24] Gaël Guennebaud and Markus Gross. Algebraic point set surfaces. In SIGGRAPH ’07: ACM SIGGRAPH 2007 papers, page 23, New York, NY, USA, 2007. ACM. [25] D. Hildenbrand, D. Fontijne, C. Perwass, and L. Dorst. Tutorial geometric algebra and its application to computer graphics. In Eurographics conference Grenoble, 2004. [26] D. Hildenbrand and E. Hitzer. Analysis of point clouds using conformal geometric algebra. In GRAPP conference Madeira, 2008. [27] D. Hildenbrand, H. Lange, Florian Stock, and Andreas Koch. Efficient inverse kinematics algorithm based on conformal geometric algebra using reconfigurable hardware. In GRAPP conference Madeira, 2008. [28] D. Hildenbrand, J. Pitt, and A. Koch. Gaalop - high performance parallel computing based on conformal geometric algebra. In E. Bayro-Corrochano and G. Scheuermann, editors, Geometric Algebra Computing for Engineering and Computer Science. SpringerVerlag, 2009. [29] Dietmar Hildenbrand. Geometric Computing in Computer Graphics and Robotics using Conformal Geometric Algebra. PhD thesis, Darmstadt University of Technology, 2006. [30] Warren Hunt, William R. Mark, and Gordon Stoll. Fast kd-tree construction with an adaptive error-bounded heuristic. In 2006 IEEE Symposium on Interactive Ray Tracing. IEEE, Sep 2006. 90 [31] Carl Gustav Jacob Jacobi. Über ein leichtes Verfahren die in der Theorie der Säculärstörungen vorkommenden Gleichungen numerisch aufzulösen. Journal für die reine und angewandte Mathematik, 30(1):51–95, 1846. [32] D. Levin. Geometric Modeling for Scientific Visualization, chapter Mesh-independent surface interpolation, pages 37–49. Springer-Verlag, 2003. [33] Marc Levoy, Kari Pulli, Brian Curless, Szymon Rusinkiewicz, David Koller, Lucas Pereira, Matt Ginzton, Sean Anderson, James Davis, Jeremy Ginsberg, Jonathan Shade, and Duane Fulk. The digital michelangelo project: 3d scanning of large statues. In SIGGRAPH ’00: Proceedings of the 27th annual conference on Computer graphics and interactive techniques, pages 131–144, New York, NY, USA, 2000. ACM Press/AddisonWesley Publishing Co. [34] Yaron Lipman, Daniel Cohen-Or, David Levin, and Hillel Tal-Ezer. Parameterizationfree projection for geometry reconstruction. ACM Trans. Graph., 26(3):22, 2007. [35] Geomerics Ltd. Enlighten. http://www.geomerics.com/products.htm, June 2010. [36] Edgar Luttmann, Daniel L. Ensign, Vishal Vaidyanathan, Mike Houston, Noam Rimon, Jeppe Øland, Guha Jayachandran, Mark Friedrichs, and Vijay S. Pande. Accelerating molecular dynamic simulation on the cell processor and playstation 3. Journal of Computational Chemistry, 30(2):268–274, 2009. [37] David J. MacDonald and Kellogg S. Booth. Heuristics for ray tracing using space subdivision. The Visual Computer, 6(3):153–166, 1990. [38] R. S. Martin, C. Reinsch, and J. H. Wilkinson. Householder’s tridiagonalization of a symmetric matrix. Numerische Mathematik, 11(3):181–195, 1968. [39] Kurt Meyberg and Peter Vachenauer. Höhere Mathematik 1. Springer, 1999. [40] Richard v. Mises and Hilda Pollaczek-Geiringer. Praktische Verfahren der Gleichungsauflösung. ZAMM - Zeitschrift für Angewandte Mathematik und Mechanik, 9(1):58–77, 1929. [41] David Mount. Ann - approximate nearest neighbor library. http://www.cs.umd. edu/~mount/ANN/, January 2010. [42] David Mount and Sunil Arya. Ann: A library for approximate nearest neighbor searching. In CGC 2nd Annual Fall Workship on Computational Geometry, 1997. [43] Yutaka Ohtake, Alexander Belyaev, and Marc Alexa. Sparse low-degree implicit surfaces with applications to high quality rendering, feature extraction, and smoothing. In SGP ’05: Proceedings of the third Eurographics symposium on Geometry processing, page 149, Aire-la-Ville, Switzerland, Switzerland, 2005. Eurographics Association. [44] G. Scott Owen. Ray-sphere intersection. http://www.siggraph.org/education/ materials/HyperGraph/raytrace/rtinter1.htm, June 1996. [45] C. Perwass. The CLU home page. HTML document http://www.clucalc.info, 2010. 91 [46] C. Perwass and D. Hildenbrand. Aspects of geometric algebra in euclidean, projective and conformal space. Technical Report Number 0310, Christian-Albrechts-Universität zu Kiel, Institut für Informatik und Praktische Mathematik, September 2003. [47] Bui Tuong Phong. Illumination for computer generated pictures. Commun. ACM, 18(6):311–317, 1975. [48] A. Schönhage. Zur konvergenz des jacobi-verfahrens. 3(1):374–380, 1961. Numerische Mathematik, [49] Helmut Seibert, Dietmar Hildenbrand, Meike Becker, and Arjan Kuijper. Estimation of curvatures in pointsets based on geometric algebra. In VISAPP International Conference on Computer Vision Theory and Applications, Angers, France, 2010. [50] Ingo Wald and Vlastimil Havran. On building fast kd-trees for ray tracing, and on doing that in O(N log N). In Proceedings of the 2006 IEEE Symposium on Interactive Ray Tracing, pages 61–69, 2006. [51] J. H. Wilkinson. Householder’s method for symmetric matrices. Numerische Mathematik, 4(1):354–361, 1962. [52] Georg Wiora. Optische 3d-messtechnik : Präzise gestaltvermessung mit einem erweiterten streifenprojektionsverfahren, 2001. [53] Kun Zhou, Qiming Hou, Rui Wang, and Baining Guo. Real-time kd-tree construction on graphics hardware. In SIGGRAPH Asia ’08: ACM SIGGRAPH Asia 2008 papers, pages 1–11, New York, NY, USA, 2008. ACM. 92