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