GPU-basiertes Raycasting

Werbung
GPU-basiertes Raycasting
Studienarbeit
Vorgelegt von
Frank Sawitzki
Institut für Computervisualistik
Arbeitsgruppe Computergraphik
Betreuer:
Dipl.-Inform. Matthias Biedermann
Prüfer:
Prof. Dr.-Ing. Stefan Müller
September 2005
Inhaltsverzeichnis
1 Einleitung
4
1.1
Motivation und Anforderung
. . . . . . . . . . . . . . . . . .
4
1.2
Ziel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
1.3
Gliederung der Arbeit
5
. . . . . . . . . . . . . . . . . . . . . .
2 Volumenvisualisierung
6
2.1
Einleitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
2.2
Die Visualisierungs-Pipeline . . . . . . . . . . . . . . . . . . .
6
2.2.1
Aquisition . . . . . . . . . . . . . . . . . . . . . . . . .
6
2.2.2
Sampling
. . . . . . . . . . . . . . . . . . . . . . . . .
7
2.2.3
Filtering . . . . . . . . . . . . . . . . . . . . . . . . . .
8
2.2.4
Klassikaton
9
2.3
2.4
. . . . . . . . . . . . . . . . . . . . . . .
Indirektes Volume Rendering
. . . . . . . . . . . . . . . . . .
9
2.3.1
Marching Cubes
. . . . . . . . . . . . . . . . . . . . .
10
2.3.2
Marching Tetrahedra . . . . . . . . . . . . . . . . . . .
10
2.3.3
Surface from Contours . . . . . . . . . . . . . . . . . .
10
Direktes Volume Rendering
. . . . . . . . . . . . . . . . . . .
11
. . . . . . . . . . . . . . . . . . . . . . . .
11
. . . . . . . . . . . . . . . . . . . . . . . . .
15
Shear-Warp . . . . . . . . . . . . . . . . . . . . . . . .
15
2.4.1
Raycasting
2.4.2
Splatting
2.4.3
3 Raycasting auf der GPU
17
3.1
GPU Hardware . . . . . . . . . . . . . . . . . . . . . . . . . .
17
3.2
GPU Programmiersprachen
. . . . . . . . . . . . . . . . . . .
21
OpenGL Shader Language . . . . . . . . . . . . . . . .
21
Bisherige Ansätze zum GPU-Raycasting . . . . . . . . . . . .
23
3.3.1
Acceleration Techniques for GPU-based VR . . . . . .
23
3.3.2
Advanced GPU Raycasting
26
3.2.1
3.3
. . . . . . . . . . . . . . .
4 Implementierung
30
4.1
Ziel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
30
4.2
Aufbau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
30
4.2.1
30
4.3
Das Hauptprogramm . . . . . . . . . . . . . . . . . . .
Die verwendeten Shader
. . . . . . . . . . . . . . . . . . . . .
31
4.3.1
colorcube.vert + colorcube.frag . . . . . . . . . . . . .
32
4.3.2
emabs-kw.frag
. . . . . . . . . . . . . . . . . . . . . .
33
4.3.3
mip.frag . . . . . . . . . . . . . . . . . . . . . . . . . .
33
4.3.4
mean.frag . . . . . . . . . . . . . . . . . . . . . . . . .
34
4.3.5
m.frag
34
4.3.6
emabs-tf.frag
. . . . . . . . . . . . . . . . . . . . . . .
34
4.3.7
emabs-ert.frag . . . . . . . . . . . . . . . . . . . . . . .
34
4.3.8
emabs-ess.frag . . . . . . . . . . . . . . . . . . . . . . .
34
. . . . . . . . . . . . . . . . . . . . . . . . . .
1
4.3.9
4.4
emabs-sm.frag . . . . . . . . . . . . . . . . . . . . . . .
34
Die verwendeten Volumendaten . . . . . . . . . . . . . . . . .
35
5 Visuelle Beurteilung
5.1
5.2
44
Ergebnisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
44
5.1.1
Emission-Absortion . . . . . . . . . . . . . . . . . . . .
44
5.1.2
Maximum-Intensity-Projection
. . . . . . . . . . . . .
44
5.1.3
Mittelwert . . . . . . . . . . . . . . . . . . . . . . . . .
44
5.1.4
First-Local-Maximum
44
5.1.5
Emission-Absortion mit Transferfunktion
. . . . . . . . . . . . . . . . . .
. . . . . . .
45
Vergleich . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
45
5.2.1
Emission-Absortion . . . . . . . . . . . . . . . . . . . .
45
5.2.2
Maximum-Intensity-Projection
45
. . . . . . . . . . . . .
6 Beurteilung der Geschwindigskeit
53
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
53
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
53
Vergleich . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
55
6.1
Frameraten
6.2
Analyse
6.3
7 Fazit und Ausblick
56
2
Abbildungsverzeichnis
1
Visualisierungs Pipeline [10] . . . . . . . . . . . . . . . . . . .
7
2
Voxel Objekt [2]
. . . . . . . . . . . . . . . . . . . . . . . . .
7
3
Topologien [10]
. . . . . . . . . . . . . . . . . . . . . . . . . .
8
4
Rekonstruktions-Filter [2]
5
Marching Cubes [12]
6
Marching Tetrahedra [12]
. . . . . . . . . . . . . . . . . . . .
10
7
Raycasting [2] . . . . . . . . . . . . . . . . . . . . . . . . . . .
11
8
Emission und Absorption [2] . . . . . . . . . . . . . . . . . . .
13
9
Shear-Warp bei paralleler Projektion [2]
16
10
Shear-Warp bei perspektivischer Projektion [2]
11
OpenGL Processing Pipeline mit xen per-Vertex Operati-
. . . . . . . . . . . . . . . . . . . .
9
. . . . . . . . . . . . . . . . . . . . . . .
10
. . . . . . . . . . . .
. . . . . . . .
ons(2) und xem Fragment Processing(6).[9] . . . . . . . . . .
12
16
17
OpenGL Processing Pipeline mit programmierbarem Vertex(1) und Fragment-Processor(2)[9]. . . . . . . . . . . . . . . . .
18
13
Front- und Backfaces[4]
. . . . . . . . . . . . . . . . . . . . .
24
14
Bounding Geometrie[5] . . . . . . . . . . . . . . . . . . . . . .
27
15
First front-face und last back-face[5]
. . . . . . . . . . . . . .
28
16
Interleaved Sampling[7] . . . . . . . . . . . . . . . . . . . . . .
28
17
Dithering[5] . . . . . . . . . . . . . . . . . . . . . . . . . . . .
28
18
Color-Cube Vertex-Shader . . . . . . . . . . . . . . . . . . . .
32
19
Color-Cube Fragment-Shader
. . . . . . . . . . . . . . . . . .
33
20
Emission-Absorption Fragment-Shader . . . . . . . . . . . . .
36
21
Maximum Intensity Projection
. . . . . . . . . . . . . . . . .
37
22
Mittelwert . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
38
23
First Local Maximum
. . . . . . . . . . . . . . . . . . . . . .
39
24
Transfer-Funktion . . . . . . . . . . . . . . . . . . . . . . . . .
40
25
Early-Ray-Termination . . . . . . . . . . . . . . . . . . . . . .
41
26
Einfaches Empty-Space-Skipping
42
27
Modizierter Emission-Absorbtion Fragment-Shader
28
Ausgaben des Emissions-Absorptions Shaders
29
Ausgaben des Maximum-Intensity-Projection Shaders
30
Ausgaben des Mean-Value Shaders
31
Ausgaben des First-Local-Maximum Shaders
32
. . . . . . . . . . . . . . . .
. . . . .
43
. . . . . . . . .
46
. . . .
47
. . . . . . . . . . . . . . .
48
. . . . . . . . .
49
Ausgaben des Emissions-Absorptions Shaders mit aktivierter
Transferfunktion
. . . . . . . . . . . . . . . . . . . . . . . . .
33
Ausgaben des Emission-Absorbtions Verfahrens (Vergleich)
34
Ausgaben des Maximum-Intensity-Projection Verfahrens (Ver-
50
.
51
gleich) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
52
3
1
Einleitung
1.1
Motivation und Anforderung
Für die Visualisierung von volumetrischen Daten, wie sie beispielsweise in der
Medizin oder Simulation verarbeitet werden, gab es bislang die Entscheidung
zwischen hoher Geschwindigkeit oder hoher Qualität. Doch gerade medizinische Anwendungen benötigen aufgrund ihrer hohen Anforderungen an die
visuelle Qualität und Korrektheit bei gleichzeitiger Interaktivität bessere Lösungen. Die rasante Entwicklung im Bereich der Grakhardware hat in den
letzten Jahren die Darstellung von Volumendaten insgesamt sehr beschleunigt. Aufgrund der hohen Rasterisierungsleistung waren texturbasierte Verfahren mit Hilfsgeometrie (sog. slicing) bisher die einzige Möglichkeit, selbst
umfangreichere Datensätze aus der medizinischen Bildgebung in interaktiven
Geschwindigkeiten darzustellen. Dennoch war es nur mit erheblichem Aufwand möglich, die Daten ohne störende Schichtartefakte zu rendern. Ebenso wie Raytracing bei der Darstellung von Oberächenmodellen selbst auf
einfachen PCs mittlerweile in interaktive Geschwindigkeitsbereiche vorstöÿt,
machen aktuellste Entwicklungen programmierbarer Grak-prozessoren das
Verfahren auch für Volumendaten interessant; als Bezeichnungen werden dabei meist Raycasting oder Raymarching verwendet. Neben der höheren Flexibilität und visuellen Qualität bietet dieses Verfahren zudem die Möglichkeit, auch volumetrische Eekte wie Rauch, Feuer oder Wolken sehr leicht
und überzeugend darstellen zu können - und somit zukünftige Spiele noch
realistischer aussehen zu lassen.
1.2
Ziel
Ziel der Arbeit ist es, sich in die Thematik der Shaderprogrammierung anhand einer modernen Hochsprache einzuarbeiten. Auf der Basis verschiedener optimierender Ansätze soll das Raycasting-Verfahren auf aktueller Grakhardware implementiert und getestet werden. Der Schwerpunkt soll dabei
auf (statischen) Volumendaten aus der medizinischen Bildgebung liegen und
hinsichtlich der Renderingleistung und visuellen Qualität beurteilt werden.
Schwerpunkte dieser Arbeit sind:
•
Auswahl und Einarbeitung in eine Shaderhochsprache
•
Konzeption und Implementierung von interaktivem Raycasting für Volumendatensätze auf aktueller Grakhardware
•
Demonstration der Ergebnisse in einer Beispielanwendung mit verschiedenen volumetrischen Daten
•
Dokumentation und Beurteilung der Ergebnisse
4
1.3
Gliederung der Arbeit
Im zweiten Kapitel dieser Arbeit werden die Grundsätze des Volumen Rendering erläutert, die sich in indirekte und direkte Verfahren aufteilen. Dabei
werden kurz die einzelnen Verfahren vorgestellt. Der Hauptteil wird dabei
vom Raycasting eingenommen.
Das dritte Kapitel beschreibt die Möglichkeiten, die mit heutiger Hardware zur Verfügung stehen, um das Volumen Rendering ezient umzusetzen.
Die OpenGL Shader Sprache wird in ihren Grundzügen vorgestellt. Anschlieÿend werden bereits bestehende Lösungen des Volumen Rendering mittels
Grakhardware vorgestellt.
Im vierten Kapitel wird die Implementierung eines hardwarebeschleunigten Volumen Renderers vorgestellt, der im Rahmen dieser Studienarbeit
entwickelt wurde.
Das fünfte Kapitel zeigt die Visuellen Ergebnisse der Implementierung
auf und vergleicht diese mit Ergebnissen aus anderen Raycasting Anwendungen.
Im sechsten Kapitel wird die Geschwindigkeit der Implementierung gemessen und mit Benchmarks aus anderen Raycasting Anwendungen verglichen.
Im siebten Kapitel wird ein Fazit erstellt und ein Ausblick auf weitere
Möglichkeiten gegeben.
5
2
Volumenvisualisierung
2.1
Einleitung
In der Computergrak werden dreidimensionale Objekte überwiegend durch
Oberächendarstellungen visualisiert. Diese Visualisierung bieten sich in den
Bereichen an, in denen regelmäÿige Strukturen auftreten. Diese können dann
recht einfach in Form von Polygonächen, Meshes, Nurbs, usw. dargestellt
werden. Bei Daten mit unregelmäÿigen Strukturen z.B. aus numerischen
Simulationen oder Scans von dreidimensionalen Volumen
1 ist es schwie-
rig diesen eine eindeutige Oberäche zuzuordnen, weil die Strukturen ieÿend ineinander übergehen. Bei einer Oberächendarstellung würden feine
Strukturen verloren gehen. Deshalb geschieht die Visualisierung dieser Daten
durch Volume Rendering. Unter diesem Begri werden verschiedene Techniken zusammengefasst, bei denen Bilder aus diesen Volumendaten erzeugt
werden. Mittlerweile gewinnen diese Volumen Rendering Verfahren auch bei
der Visualisierung von Eekten in Computerspielen immer mehr an Bedeutung. Mit ihnen lassen sich Objekte wie Flüssigkeiten, Gase oder andere
Naturphänomene realistisch darstellen. Im Gegensatz zur Oberächenvisualisierung bieten diese Verfahren den weiteren Vorteil, mehrschichtige oder
transparente Informationen, z.B. aus dem Inneren eines Körpers, darstellen
zu können. Allerdings war bisher durch eine aufwendige Berechnung für das
Volume Rendering die Bildwiederholungsrate für eine üssige Animation zu
gering. Deswegen wurden unterschiedliche Verfahren entwickelt, die durch eine Vereinfachung der Berechnungen eine schnelle Darstellung ermöglichten,
2 für die Darstellung
oder es wurde spezielle und somit auch teure Hardware
eingesetzt. Gerade für eine optimale Interpretation von medizinischen Aufnahmen ist eine Interaktion mit den Bildmaterial wichtig. Erst durch die
Entwicklung von programmierbaren Grakprozessoren ist es möglich eine
gute Bildqualität in Echtzeit auch kostengünstig umzusetzen.
2.2
Die Visualisierungs-Pipeline
Bei dem Volumen Rendering sind mehrere Schritte notwendig, die in Form
einer Pipeline angeordnet werden können (Abbildung 1).
2.2.1 Aquisition
Als Volumen versteht man ein kontinuierliches dreidimensionales Signal
das durch eine
Aquisition,
3,
z.B. durch einen CT-Scan eines menschlichen
1
Diese stammen z.B. aus der Medizin, und werden durch Computer Tomographie (CT)
oder Magnet Resonanz Tomographie (MRI) erzeugt.
2
z.B. VolumePro 1000. Eine Hardwarelösung von Terarecon.
3
Das Signal ist allerdings durch das Abtasttheorem bandlimitiert mit einer cutt-o
Frequenz νs , bei der für eine exakte Rekonstruktion die Abtastrate mehr als das Doppelte
der cut-o Frequenz betragen muss (Nyquist Rate).
6
Abbildung 1: Visualisierungs Pipeline [10]
Körpers, als Datensatz auf dem Computer vorliegt.
f (~x) ∈ R
mit
~x ∈ R3
(1)
2.2.2 Sampling
Zur Rekonstruktion von Volumen Daten wird ein Gitter durch den Datensatz
gelegt, welches das Volumen in ein dreidimensionales Array aufteilt.
Abbildung 2: Voxel Objekt [2]
Die Abtastwerte werden entweder an den Knoten (Voxel
4 ) oder im Zen-
trum eines von den Schnittpunkten des Gitters gebildeten Würfels bestimmt
(Voxelzelle) (Abbildung 2). Die Anordnung der Voxel bzw. Voxelzellen kann
dabei unterschiedliche Formen aufweisen (Abbildung 3).
4
VOXEL = VOlume (X) ELement, in Anlehnung an PIXEL = PIcture (X) ELement
7
(a) kartesisch
(b) gleichmässig
(c) rechtwinklig
(d) strukturiert
(e) unstrukturiert
(f) gestreut
Abbildung 3: Topologien [10]
2.2.3 Filtering
Zwei Probleme ergeben sich bei der Rekonstruktion des abgetasteten Volumens, die durch eine
•
Filterung
der Daten ausgeglichen werden:
Nach dem Abtasttheorem benötigt die exakte Rekonstruktion des Signals eine Faltung durch eine Sinus Funktion (Abbildung 4, C), die für
den eindimensionalen Fall lautet:
sinc(x) =
sin(πx)
πx
(2)
5
Im dreidimensionalen Fall geschieht die Faltung über ein Tensor-Produkt .
•
Es entstehen Aliasing Artefakte, falls ein Signal rekonstruiert wird,
welches nicht bandlimitiert gewesen ist.
Um das kontinuierliche Signal mittels eines Arrays von Voxel rekonstruieren zu können, wird die Sinc-Faltung durch einen Box- oder Tent-Filter
ersetzt (Abbildung 4, A und B).
•
Der Box-Filter interpoliert nach dem Nearest-Neighbor-Verfahren. Dieses erzeugt allerdings Unterbrechnungen zwischen Nachbarwerten und
eine insgesamt blockartige Erscheinung.
5
Es werden die gesamten Abtastpunkte berücksichtigt. Dies ist rechnerisch aufwendig
zu lösen.
8
Abbildung 4: Rekonstruktions-Filter [2]
•
Der Tent-Filter interpoliert trilinear. Dadurch erhält man ein gutes
Verhältnis zwischen Rechenzeit und Qualität des rekonstruierten Signals.
2.2.4 Klassikaton
Das Volumen bezeichnet eine Verteilung von Licht emittierenden Partikeln
mit einer bestimmten Dichte. Diese Dichte wird für die Abbildung auf dem
Bildschirm in RGBA Werte umgewandelt. Die
Klasskation beschreibt die
Auswirkung des Lichts auf das Voxel. Dabei wird der Skalarwert mit Hilfe
einer
Transferfunktion
von einer physikalische Gröÿe (z.B. Materialdich-
te) in eine optische Gröÿe (z.B. Farbe (RGB), Absorptionseigenschaften und
Opazität (a)) umgewandelt. Es wird zwischen Pre- oder Post-Klassikation
unterschieden, je nachdem ob die die Klassikation vor oder nach der Interpolation erfolgt.
2.3
Indirektes Volume Rendering
Die Verfahren des Volumen Rendering werden grob in zwei Klassen aufgeteilt. In das indirekte Volume Rendering und das direkte Volume Rendering.
Bei dem indirekten Volume Rendering werden Flächen innerhalb des Volumens bestimmt, die eine identische Dichte besitzen. Diese Flächen werden
als Isoächen bezeichnet. Die Schwierigkeit besteht darin, diesen Isowert zu
bestimmen. Dieses Verfahren setzt eine aufwendige Vorverarbeitung voraus
und besitzt den Nachteil, das viele im Volumen enthaltene Informationen
verloren gehen können, da die Darstellung auf die gefunden Isoächen beschränkt ist. Es existieren mehrere Techniken zur Bestimmung der Isoächen,
die im folgenden kurz dargestellt werden[12].
9
2.3.1 Marching Cubes
Bei dem Marching-Cubes Verfahren wird die Voxelzelle als ein Würfel betrachtet, bei dem die 8 Eckpunkte in + oder - klassiziert werden, abhängig
davon, ob der Eckpunkt einen höheren oder niedrigeren Wert als ein gesetzter Isowert besitzt. Falls sich die Werte der Eckpunkte in einem Würfel in +
und - unterscheiden lassen, werden in diesem Würfel Ebenen aufgespannt, die
zwischen dieser + und - Klassikation verlaufen. Diese Ebenen ergeben dann
über mehrere Zellen betrachtet die Isoäche (Abbildung 5). Das Problem der
Marching Cubes ist allerdings ein Auftreten von Mehrdeutigkeiten.
Abbildung 5: Marching Cubes [12]
2.3.2 Marching Tetrahedra
Bei den Marching Tetrahedra werden Tetraeder anstatt der Würfel betrachtet, um so das Problem der Mehrdeutigkeiten zu beseitigen (Abbildung 6).
Abbildung 6: Marching Tetrahedra [12]
2.3.3 Surface from Contours
Bei dem Surface-from-Contours Verfahren werden Schichtweise durch Segmentierungsverfahren die Konturen des Volumens bestimmt um auf diese
10
Weise die Oberäche zu rekonstruieren.
2.4
Direktes Volume Rendering
Bei dem direkten Volumenrendering Verfahren lässt sich das Volumen ohne
eine Repräsentation der Oberäche anzeigen. Bei den Verfahren wird das
Volumen als Skalarfeld mit transparenten Werten aufgefasst, bei dem mit
Hilfe von physikalischen Modellen die Lichtausbreitung nachgebildet wird.
Dadurch lassen sich alle im Volumen enthaltenen Informationen verlustfrei
darstellen.
Die direkten Verfahren werden aufgeteilt in Bildraum-, Objektraum und
hybride Verfahren. Bei dem Bildraumverfahren wird die Darstellung vom
Bild aus gesehen erzeugt, während bei dem Objektraumverfahren die Betrachtungsrichtung von jeder Zelle des Volumen zum Bild führt. Bei dem
Hybriden Verfahren wird das Bild aus beiden Richtungen zusammengesetzt.
2.4.1 Raycasting
Abbildung 7: Raycasting [2]
Ein Vertreter des Bildraumverfahrens ist das Raycasting. Es ist einfach
zu implementieren und erzielt dabei die eine gute Bildqualität, erfordert aber
dafür auch eine hohe Rechenleistung. Dieses Verfahren wurde im medizinischen Bereich erstmals 1988 von Paolo Sabella vorgestellt [6]. Der in diesem
Kapitel vorgestellte Algorithmus stammt von Markus Hadwiger und Christof
Rezk-Salama [2].
Beim Raycasting wird vom Auge des Betrachters durch jeden Pixel des
Bildschirms ein simulierter Sehstrahl
bildung 7).
t
~x(t) durch das Volumen geschickt (Ab-
bezeichnet in diesem Fall die zurückgelegte Strecke auf dem
Sehstrahl.
Nun wird in gleichmäÿigen Intervallen der Skalarwert der getroenen Voxel
s(~x(t)) im Volumen ermittelt und mit der Distanz zur aktuellen Position
11
gewichtet. Dies kann entweder über einen Box-Filter mit Nearest-NeighborInterpolation oder einen Tent-Filter mit trilinearer Interpolation geschehen.
Compositing:
Basierend auf einem optischen Modell wird die Lichtver-
breitung entlang des Sehstrahls im Volumen berechnet. Dieses optische Modell beschreibt, ob die Voxel des Volumens das Licht emittieren, reektieren,
streuen, absorbieren oder verdecken. Folgende Compositing Verfahren werden z.B. angewendet:
•
Integration von Absorption und Emission:
Das Licht wird von den Partikeln emittiert und auch absorbiert. Dieses
Modell ist am weitesten verbreitet.
•
Integration von Emission und Absorption über eine Transferfunktion:
Die Transferfunktion ermöglicht eine farblich unterschiedliche Darstellung der verschiedenen Dichten im Volumen (siehe Klassikation). Bestimmte Bereiche lassen sich damit auch transparent darstellen.
•
Mittelwert:
Es wird der Mittelwert über alle auftretenden Farbwerte entlang eines
Sehstrahls gebildet und dieser dargestellt.
•
Maximum Intensity Projection:
Es wird nur der Maximalwert aller aufgetretenen Farbwerte entlang
eines Sehstrahls ermittelt und dieser dargestellt.
•
First Local Maximum:
Es wird der Farbwert ermittelt und dargestellt, der das erste lokale
Maximum entlang des Sehstrahls bildet.
Das Raycasting basiert im Allgemeinen auf dem Absorptions-Emissions
Modell und man erhält dabei für die Absorption
an der Position
k(s)
und die Emission
c(s)
t:
k(t) := k(s(~x(t)))
und
c(t) := c(s(~x(t))
(3)
In Abbildung 8 wird die einmalige Emission und anschlieÿende Absorption des Lichts verdeutlicht, dass mit einer Strahlungsenergie (c) am Punkt
(t=d) ausgesendet wird und auf dem Weg bis zum Auge (t=0) kontinuierlich
0
an Energie (c ) verliert. Für
c0
gilt:
c0 = c · e−kd
12
(4)
Abbildung 8: Emission und Absorption [2]
Sollte die Absorption nicht kontinuierlich verlaufen, z.B. durch eine Emission von Licht an einem Punkt entlang des Strahls, wird die Strahlungsenergie
d
c0 durch eine Integration der Absorptionskoeezienten entlang der Strecke
berechnet:
Z
∞
C=
c(t) · e−τ (0,t) dt̂
(5)
0
Optische Tiefe:
Als optische Tiefe bezeichnet man das Integral der Absorptions-
Koezienten im Exponenten.
Z
d2
τ (d1 , d2 ) =
k(t̂)dt̂)
(6)
d1
Die Menge der Strahlungsenergie
c
die das Auge aus einer Richtung er-
reicht wird durch Integration der gesamten Strahlungsenergie aller Punkte
t
entlang des Strahls berechnet.
Z
C=
∞
c(t) · e−τ (0,t) dt
(7)
0
Die Auswertung des Integrals erfolgt numerisch durch back-to-front oder
front-to-back Faltung (Alpha blending). Durch die Bestimmung einer Riemann Summe lässt sich die optische Tiefe
τ
approximieren. Diese beschreibt
dann die anwachsende Absorption bis zu einer bestimmten Position
~x(t). ∆t
ist die Distanz zwischen den Resampling Punkten.
t/∆t
τ (0, t) ≈ ~τ (0, t) =
X
k(i · ∆t)∆t
(8)
i=0
Über eine Multiplikation kann die Summierung der Exponenten substituiert werden:
13
e−τ̃ (0,t) =
t/d
Y
e−k(i·∆t)∆t
(9)
i=0
Opazität:
Nun kann die Opazität
A
berücksichtigt werden, mit der die
Lichtundurchlässigkeit des Voxels angegeben wird:
Ai = 1 − e−k(i·∆t)∆t
(10)
Ai
Als Vereinfachung kann durch eine Umformung die Opazität
als Ap-
proximation für den gesamten i-ten Strahl angenommen werden:
−τ̃ (0,t)
e
t/d
Y
=
(1 − Aj )
(11)
i=0
Die Farbe des i-ten Strahls lässt sich schlieÿlich approximieren über:
Ci = c(i · ∆t)∆t
(12)
Nachdem nun die Skalarwerte mit Hilfe des optischen Modells in RGBA
Werte umgewandelt wurden, kann durch eine numerische Lösung das Volume
Rendering Integral bestimmen:
C̃ =
n
X
i=0
Alpha Blending:
Ci
i−1
Y
(1 − Ai )
(13)
j=0
Die Auswertung des Integrals erfolgt iterativ durch
Alpha Blending, bei der zwischen back-to-front oder front-to-back Reihenfolge unterschieden wird. Mit einem Stepping
i
mit Schrittweiten von n
- 1 bis 0 erhält man für die back-to-front Reihenfolge:
0
Ci0 = Ci + (1 − Ai )Ci+1
Der Wert
Farbe
Ci ,
Ci0
(14)
wird dabei unter der Berücksichtigung von der aktuellen
Opazität
Ai
und des Wertes an der vorherigen Position
0
rechnet. Startwert ist immer der Farbwert Cn
14
er-
= 0.
Alternativ läÿt sich das Alpha-Blending mit dem Stepping
in front-to-back Reihenfolge ausführen:
0
Ci+1
i
von 1 bis
n
0
Ci0 = Ci−1
+ (1 − A0i )Ci A0i = A0i−1 + (1 − A0i )Ai
Der Farbwert
Ci0
(15)
wird dabei unter der Berücksichtigung der aktuellen
0
Ci−1
errechnet.
0
Das gleiche gilt für die Opazität Ai , die sich auch aus der aktuellen Opazität
Ai und des vorherigen Opazitätswertes A0i−1 errechnet. Startwert ist auch
0
0
hier der Farbwert Cn = 0 und der Opazitätswert A0 = 0.
Farbe
Ci ,
der Opazität
Ai
Early-Ray-Termination:
und des vorherigen Farbwertes
Da die Berechnung des Sehstrahls im Fall der
front-to-back Reihenfolge vom Auge aus in das Volumen führt, lässt sich
mit der Early-Ray-Termination ein Verfahren nutzen, das es ermöglicht, die
Berechnung entlang eines Sehstrahls abzubrechen. Sobald ein bestimmter
Opazitätswert
A0i
bei der Integration erreicht wird, bei dem angenommen
wird, dass kein Licht mehr durchdringen kann, wird der Algorithmus gestoppt. D.h. bei allen dahinter liegenden Voxeln kann angenommen werden,
dass sie nicht mehr sichtbar sind und deshalb auch nicht mehr berechnet
werden müssen.
2.4.2 Splatting
Ein Vertreter des Objektraumverfahrens ist das Splatting. Dieses Verfahren
basiert darauf, dass die optischen Eigenschaften eines Raumes in einem bestimmten Bereich durch jeden diskreten Datenpunkt im Volumen beeinusst
werden. Der beeinusste Bereich ist gekennzeichnet durch seine charakteristische Funktion. In back-to-front Reihenfolge werden nun für jeden Voxel
dessen Mittelpunkt auf die Bildebene projiziert und der Beitrag mit einem
Emissionswert gewichtet und mit einem Over-Operator verknüpft.
2.4.3 Shear-Warp
Ein Vertreter des Bild- sowie Objektraum-Verfahrens ist das Shear-WarpVerfahren, bei dem die Berechnung grob in zwei Schritten abläuft. Im ersten
Schritt wird das Volumen in mehrere Schichten unterteilt, die alle parallel zur
Sehachse verlaufen. Im Falle einer Parallelprojektion wird nun das Volumen
so verzerrt (3D-Scherung), dass die Entfernung der Abtastpunkte auf diesen
Schichten zu einer bestimmten Ebene gleich ist (Abbildung 9). Im Falle einer
Perspektivischen Projektion erfolgt zusätzlich eine Skalierung (Abbildung
10).
Auf dieser Zwischenebene werden nun in beiden Fällen die Abtastwerte
von den Schichten projiziert. In einem weiteren Schritt wird das Ergebnis
von dieser Zwischenebene für eine korrekte Darstellung wieder entzerrt (2DVerzerrung).
15
Abbildung 9: Shear-Warp bei paralleler Projektion [2]
Abbildung 10: Shear-Warp bei perspektivischer Projektion [2]
Dieses Verfahren ermöglicht auf diese Weise die Implementierung eines
schnellen Berechnungsalgorithmus, der allerdings bei starker Vergröÿerung
zu Fehlern in der Darstellung führt, da die Zwischenräume zwischen den
Abtastungs-Schichten sichtbar werden. Auÿerdem müssen je nach Betrachtungsrichtung unterschiedlich ausgerichtete Schichtaufnahmen vorliegen, was
zu einem höherem Speicherbedarf führt.
16
3
Raycasting auf der GPU
3.1
GPU Hardware
Heutige Grakprozessoren liefern in bestimmten Bereichen ein vielfaches an
Leistung im Vergleich zu gängigen CPUs. So liegt bspw. die Leistung einer
Floating-Point Berechnung einer zwei Jahren alten GeForce 5900 Ultra bei
20 Milliarden Multiplikationen pro Sekunde. Im Vergleich dazu kommt eine
mit 3 GHz getaktete Pentium 4 CPU auf 6 Milliarden Multiplikationen pro
Sekunde. Die Floating-Point Leistung dieser GPU entspricht somit eines mit
10 GHz getakteten Pentium 4 Prozessors [1]. Und die Rechenleistung von
GPUs wächst im Vergleich zu der von CPUs schneller an und überschreitet
6
dabei sogar die Prognosen von Moores Law . In der Vergangenheit waren
allerdings die Möglichkeiten der Programmierung durch eine Fixed-Function
Rendering-Pipeline beschränkt (Abbildung 11). Mittels dieser Pipeline werden aus den 3D Koordinaten von Polygonen und zusätzlichen Informationen
wie Farbe, zugehöriger Textur, Transparenz, usw. eine Darstellung der Daten
auf dem Bildschirm erzeugt.
Abbildung 11: OpenGL Processing Pipeline mit xen per-Vertex Operations(2) und xem Fragment Processing(6).[9]
Die Pipeline stellt dabei eine Reihe von Funktionen zur Verfügung. Diese waren allerdings bei der xed-function Bauweise nicht erweiterbar. Dies
änderte sich mit der Einführung von frei programmierbaren Vertex- und
Fragment-Einheiten, mit denen die xen Vertex- und Fragment-Operationen
6
Gordon Moore war Mitbegründer von Intel und stellte 1965 eine nach ihm benannte
Faustregel auf, die beschreibt, dass sich die Anzahl der Transistoren in Prozessoren in 24
Monaten jeweils verdoppelt[8].
17
7 ausgetauscht werden konnten (Abbil-
durch selbst programmierte Shader
dung 12).
Abbildung 12: OpenGL Processing Pipeline mit programmierbarem Vertex(1) und Fragment-Processor(2)[9].
Die Vertex-Shader ersetzen alle Berechnungen, die die Verticies betreen.
Dazu zählen z.B.:[9]
•
Vertex-Transformation
•
Normalen-Transformation and Normalisierung
•
Erzeugen von Texturkoordinaten
•
Texturekoordinaten-Tansformation
•
Beleuchtung
Die Fragment-Shader ändern die Fragment-Daten, die nach der Rasterisierung entstanden sind. Funktionen eines Fragment-Shaders sind z.B.:[9]
•
Operationen auf interpolierten Werten
•
Texturzugrie
•
Nebel
•
Farbsummen
7
Als Shader wird der Code bezeichnet, der in der Vertex- oder Fragment-Einheit ausgeführt wird. Dessen Möglichkeiten gehen weit über das i.A. bezeichnete Shaden hinaus.
18
Fast jeder heutzutage verkaufte Grakprozessor besitzt diese frei programmierbare Shader-Einheiten. Die derzeit leistungsfähigste Grakkarte im
Consumer Bereich ist die 7800 GTX von Nvidia, die auf der G70 Architektur basiert. Sie besitzt 8 Vertex-Einheiten und eine Fragment-Einheit mit 24
Pipelines, die es erlauben, Berechnungen gleichzeitig parallel auf mehreren
Daten auszuführen.
Durch die Einführung von programmierbaren Shader-Einheiten, lässt sich
diese Leistung nicht nur alleine für die graphische Darstellung von Dreiecken nutzen, sondern auch für viele weitere Anwendungen. Diese Anwen-
General Purpose
dungen werden unter dem Begri GPGPU (
ons on
Graphic Processor Units)
computati-
zusammengefasst. Die Umsetzung eines
Raycasting-Algorithmus für die Berechnung auf der Grakhardware ist ein
Beispiel für eine dieser Anwendungen. Es gibt aber weiterhin noch zahlreiche Einschränkungen, die bei der Programmierung beachtet werden müssen,
auch wenn diese meistens durch kommende Hardwaregenerationen aufgehoben werden. Diese Arbeit wurde kurz vor dem Erscheinen der G70 Architektur begonnen und die Implementierung in dieser Arbeit orientiert sich
aus diesem Grund noch an den Features, die Nvidia seit der Vorherigen, der
NV4x Architektur eingeführt hat.
Für die Nvidia 6800 GT Ultra Grakkarte z.B. sind es [11]:
•
256Bit Prozessor
•
256Bit Speicherbus zu GDDR3 Speicher
•
6 Vertex-Shader Einheiten in MIMD
•
16 Pixel-Pipelines mit 32Bit Floating-Point Präzision in SIMD
8 Bauweise
9 Bau-
weise
•
1 Textur-Einheit/Pipe
•
10 Bit/Farbkanal
•
Vertex-Shader Version 3.0 Unterstützung
•
Pixel-Shader Version 3.0 Unterstützung
•
DirectX Version 9.0c Unterstützung
Im Detail sind für Pixel-Shader 3.0 folgende Eigenschaften festgelegt:
•
8
9
kein Dependent Texture Limit
Multiple Input, Multiple Data.
Single Instruction, Multiple Data.
19
•
unbegrenzte Texture Instruktionen
•
Position Register
•
>= 512 Instruktion Slots
•
65535 Executed Instruktionen
•
10 Interpolated Registers
•
Instruction Predication
•
Indexed Input Registers
•
32 Temp Registers
•
224 Constant Registers
•
Arbitrary Swizzling
•
Gradient Instructions
•
Loop Count Register
•
Face Register (2-sided lighting)
•
Dynamische Flusskontrolle mit einer Tiefe von 24
Für Vertex-Shader 3.0 gelten folgende Eigenschaften:
•
>= 512 Instruction slots
•
65535 Maximal ausführbare Instructionen
•
Instruction Predication
•
32 Temp. Register
•
>= 256 Konstanten Registers
•
Static Flow Control
•
Dynamische Flusskontrolle mit einer Tiefe von 24
•
Vertex Texture Fetch
•
4 Texture Samplers
•
Geometry Instancing Support
Durch diese Fülle an Funktionen bietet sich die NV40 Hardware geradezu
für das Raycasting an. Sie bietet einen Fragment-Prozessors mit vielen Pipelines für das parallele Ausführen von Operationen, eine Rechengenauigkeit
von 32Bit Floating Point Werten und die Möglichkeit der Flusskontrolle im
Shader (Branching). Die sich dadurch ergebenden Möglichkeiten werden in
einem späteren Kapitel bei der Implementierung erläutert.
20
3.2
GPU Programmiersprachen
Zuerst mussten die Shaderprogramme in Maschinensprache programmiert
werden. Vereinfacht wurde die Programmierung durch Einführung von ShaderHochsprachen wie Nvidias c for graphics (cg), Microsofts High Level Shader Language (HLSL) oder 3Dlabs OpenGL Shader Language (GLSL),
die sich allgemein sehr ähnlich sind. Nach einer kurzen Einarbeitung in die
genannten Sprachen wurde GLSL für die Implementierung gewählt.
3.2.1 OpenGL Shader Language
Die OpenGL Shader Language wurde als Erweiterung von OpenGL entwickelt, um die bisherigen Beschränkungen durch die xed-functionality der
traditionellen OpenGL Rendering-Pipeline aufzuheben. Die Sprache ist in
der Syntax an C orientiert worden. Dies ermöglicht es zukünftigen GLSL
Entwicklern einen schnellen Einstieg zu nden. Zusätzlich wurde die Sprache um mitgelieferte Funktionen erweitert, die gerade bei Algorithmen zu
Berechnung von Grak häug verwendet werden. Die Sprache wird gemeinsam für den Vertex- oder Fragment-Shader verwendet, unterscheidet sich
aber in der Verwendung in wenigen Details.
Gemeinsamkeiten zu C sind[9]:
•
Shader starten bei void main()
•
Geschweifte Klammern um Funktionen abzugrenzen
•
Konstanten, Bezeichnern, Operatoren, Expressions und Statements
•
Flusskontrolle für Schleifen, if-then-else und Funktionsaufrufe
Erweiterungen zu C sind[9]:
•
Vektor Typen für Floation-Point-, Integer- und Boolean Werte
•
Floating-Point Matrizen
•
Sampler Typen um auf den Textur-Speicher zuzugreifen
•
Qualier für die Ein- und Ausgaben und für die Kommunikation zwischen den Shadern
•
Traditionelle OpenGL States als mitgelieferte Variablen zur Kommunikation zur xed-functionality.
•
Eine Vielzahl an mitgelieferten Funktionen um die Programmierung zu
vereinfachen. Dazu zählen die Berechnungen von:
21
trigonometrischen Funktionen (Sinus, Cosinus, Tangenz, ...)
Exponentialfunktionen (Quadrat, Exponent, Logarithmus, Quadratwurzel)
allgemeinen mathematischen Funktionen (Betrag, Ober- und Untergrenze, Modulo, ...)
geometrischen Funktionen (Länge, Distanz, Skalarprodukt, Kreuzprodukt, Normalisierung, ...)
Vergleichsoperatoren auf Vektoren (grösser, kleiner, gleich, ..)
Aus C++ wurden übernommen[9]:
•
Überladen von Funktionen
•
Deklaration von Variablen, wenn sie benötigt werden.
Nicht aus C enthalten sind[9]:
•
Automatische Umwandlung von Datentypen
•
Zeiger, Strings- und Character-Datentypen
•
Double-precision Floats, Byte, Short, Long-Integer
•
Referenzen auf Dateien wie z.B.
•
Casting von Datentypen ohne eine Umwandlung
#include
Mittels dieser Sprache für die frei programmierbaren Shader-Einheiten
ergeben sich für den Programmierer eine Vielzahl neuer Möglichkeiten. Dazu
zählen z.B.[9]:
•
Realistische Materialoberächen, wie Metall, Stein, usw.
•
Realistische Lichteekte, wie Area-Lights, Soft-Shadows, usw.
•
Darstellung natürlicher Phenomäne, wie Feuer, Rauch, Wasser, Wolken, usw.
•
Nicht photorealistisches Rendering
•
Verwenden des Textur-Speichers zum Speichern von Normalen, Glanzwerten, Polynomialen Koezienten, usw.
•
Reduzieren von Textur-Zugrien durch Erzeugen von prozeduralen Texturen
22
•
Bildbearbeitungfunktionen, wie Faltung, unscharf Maskieren, Blending,
usw.
•
Animationseekte, wie Key-Frame-Interpolation, Partikelsysteme, prozedural denierte Bewegungen
•
Vom Benutzer programmierbare Antialiasing Methoden
Es muss allerdings beachtet werden, dass durch einen Shader die gesamte
Berechnung betreend der Verticies oder Fragmente ersetzt wird. Es ist nicht
möglich, eine Transformation über die xed-function Pipeline durchführen
zu lassen und dann einen Vertex-Shader für die Beleuchtung zu nutzen. Allerdings lassen sich in einem Shader die traditionellen OpenGl-States nutzen.
Auch werden durch den Fragment-Prozessor nicht die xed-functions am Ende der OpenGL-Rendering-Pipeline wie z.B. der Alpha-, Tiefen- oder Stencil
Test ersetzt.
3.3
Bisherige Ansätze zum GPU-Raycasting
Es existieren bereits mehrere Ansätze das Raycasting zu beschleunigen oder
auch speziell auf GPUs anzupassen, um deren Rechenleistung zu nutzen.
Beispiele hierfür sind:
•
Smart Hardware-Accelerated Volume Rendering [3]
•
Acceleration Techniques for GPU-based Volume Rendering [4]
•
Advanced GPU Raycasting [5]
Die oben genannten Arbeiten vertreten alle die Auassung, dass gerade
die besondere Fähigkeit der GPUs, mehrere unabhängige Prozesse in den
mehrfach vorhandenen Pipelines parallel ausführen zu können, die Berechnung des Raycasting mit seinen vielen voneinander unabhängigen Lichtstrahlen, beschleunigt. Die Arbeit Acceleration Techniques for GPU-based Volume Rendering wird im Folgenden ausführlicher vorgestellt, da sie einige
Ideen beinhaltet, wie das Raycasting einfach, aber auch ezient auf einer
GPU umgesetzt werden kann. Die Arbeit Advanced GPU Raycasting baut
auf der bereits genannten Arbeit auf und erweitert diese um weitere interessante Ansätze.
3.3.1 Acceleration Techniques for GPU-based VR
Die Implementierung dieser Studienarbeit soll mit der Early-Ray-Termination
eine Methode umsetzen, die von J.Krüger und R.Westermann in [4] für
23
die Anwendung auf einer GPU entwickelt wurde. Diese Methode soll dabei auf die verbesserten Möglichkeiten der aktuellen Grakhardware angepasst werden, die es im Gegensatz zu der von Krüger und Westermann verwendeten Hardware erlaubt, Schleifen und Conditional-Breaks zu benutzen.
Um die Beschränkungen der nicht verfügbaren Schleifen zu umgehen, wurden die Algorithmen in der Arbeit von Krüger und Westermann in einem
Multipass-Verfahren umgesetzt, bei denen die Ergebnisse zwischen den Rendering Durchläufen in Texturen gespeichert wurden.
Algorithmus
Zuerst werden die Volumendaten als eine 3D Textur im Spei-
cher der Grakkarte abgelegt. Nun werden die Sehstrahlen auf das Volumen
geschickt, und die Koordinaten der Schnittpunkte zwischen dem Sehstrahl
und dem Volumen errechnet. So lassen sich später die Richtungsvektoren
der Sehstrahlen sehr einfach bestimmen. Dazu wird eine Bounding-Box in
Form eines Würfels um das Volumen erzeugt (Abbildung 13). Die TexturKoordinaten der Front-Faces dieser Box, die gleichzeitig auch die Eintrittspunkte des Strahls in das Volumen sind, werden als RGB-Farbwerte temporär in eine 2D Textur geschrieben. Ebenso werden die Textur-Koordinaten
der Back-Faces dieser Box bestimmt, also die Austrittspunkte des Strahls
aus dem Volumen. Anschlieÿend werden die Farbwerte der vorher ermittelten Font-Faces subtrahiert. Das Ergebnis der Subtraktion wird normalisiert
und ergibt den Richtungsvektor des Sehstrahls, der nun in den RGB-Werten
einer weiteren 2D Textur geschrieben wird. Die Länge der Strahls, also der
des nicht normalisierten Richtungsvektors, wird in die Alpha-Komponente
dieser Textur geschrieben.
Abbildung 13: Front- und Backfaces[4]
Beim eigentlichen Raycasting werden nun die Farbwerte für jeden Pixel
des Ausgabebildes bestimmt. Dazu werden die Front-Faces nochmals berechnet und nun für jeden Pixel solange entlang des ermittelten Richtungsvektors
das Volumen abgetastet, bis der Opazitätswert einen bestimmten Grenzwert
überschreitet oder der Strahl das Volumen verlässt. Die Länge des Vektor
überschreitet dabei den in der Alpha-Komponente gespeicherten Wert. Die
24
Abtastwerte werden für das Compositing in einer weiteren 2D Textur gespeichert.
Empty-Space-Skipping
Eine weitere Berechnungsvereinfachung ist das
Überspringen von leeren Regionen oder die Vergröÿerung der Schrittweiten
bei einheitlichen Regionen im Volumen. Dadurch wird die Anzahl der durchzuführenden Operationen verringert und die Geschwindigkeit der Berechnung kann beträchtlich gesteigert werden. Für die Realisierung des EmptySpace-Skipping wird eine zusätzliche Datenstruktur für die Speicherung der
Positionen der leeren Regionen im Volumen verwendet, wie z.B. eine OctreeHierachie. Dabei werden in den inneren Knoten statistische Informationen
über die äuÿeren Knoten gespeichert, z.B. die Grenzen der Region, die von
einem Knoten abgedeckt werden. Mit Hilfe dieser Informationen kann die
Sampling-Distanz entlang des Strahls erhöht werden, sobald der Strahl auf eine leere Region trit. Im verwendeten Beispiel von J.Krüger und R.Westermann
wurde eine Rastergröÿe von
83
Zellen gewählt. Der minimal- und maximal-
Wert der in diesem Block enthaltenen Skalarwerte wird nun in den RGWerten einer 3D-Textur gespeichert, die folglich nur noch
1/8
der Grösse
des Volumens besitzt. Die RG-Werte indexieren eine weitere 2D-Textur
CT,
die bei jedem existierenden min/max Wertepaar kontrolliert, ob der enthaltene Wert von 0 abweicht. Sollte der Raum leer sein wird der gesamte Bereich
übersprungen.
Implementierung
Die Implementierung der Arbeit von J.Krüger und R.Westermann
basiert auf der Pixel Shader 2.0 API von Microsoft und wurde für die Ausführung auf einer ATI 9700 angepasst.
10 .
Diese Hardware ermöglicht:
• Per-fragment texture fetch operations:
Zugri eines Pixel-Shaders auf bis zu 8 verschiedene Texturen
• Texture-render-target:
Erzeugen einer 2D-Textur, in die direkt auf den Viewport ausgerichtet gerendert, und auf die in folgenden Rendering Schritten zugegrien
werden kann. Diese dient der Zwischenspeicherung der Rendering Ergebnisse. Die Textur kann Floating-Point Werte, sowie negative Werte
beinhalten.
• Texture-coordinate generation:
Es ist möglich mit einem Shader-Programm direkt über Textur-Koordinaten
auf Texturen zuzugreifen oder diese zu manipulieren.
10
Die Implementierung sollte auch auf allen neueren Grakkarten lauähig sein
25
• Per-fragment arithmetic:
Es können im Shader Programm arithmetische Operationen auf Skalaroder Vektor-Variablen ausgeführt werden
11 .
• Depth replace:
Es können beliebige Werte im z-Buer als Tiefenwerte für ein Fragment
geschrieben werden.
Ergebnis
J.Krüger und R.Westermann konnten in ihrer Arbeit die Anzahl
der benötigten Fragment-Operationen durch die Verwendung von Early-RayTermination und Empty-Space-Skipping verringern. Allerdings ergab sich
durch die Tatsache, dass bei ihnen hardwarebedingt ein Shader-Programm
nicht abgebrochen werden konnte, ein Problem. Der eigentliche RenderingSchritt musste in mehrere Durchläufe mit einer festen Anzahl an Abtastpunkten aufteilt werden, um so nach einem Durchlauf zu prüfen, ob weitere
Durchläufe nötigt sind, um das Volumen zu durchqueren. Schwierig war ein
optimales Verhältnis zwischen der Anzahl der Durchläufe und Anzahl der
Abtastschritte pro Durchlauf zu nden. Durch das Festlegen der Arbeitsschritte konnte sichergestellt werden, dass die Anzahl der Berechnungen in
einem Durchlauf diesen Wert nicht überschreiten würde. Bei dem relativ ungünstigen Fall, dass direkt im ersten Schritt, entweder der Opazitätswert das
Maximum erreicht oder der Strahl das Volumen verlässt, ist immer sichergestellt, dass nach diesen Schritten der Durchlauf beendet wird. Es wäre daher
sinnvoll diesen Wert relativ klein zu wählen. Allerdings erhöht sich dadurch
aber auch die Anzahl der Durchläufe und damit ein gewisser Overhead, der
durch das Rendern der Front-Faces und der Zugrie auf die Texturen entsteht. Optimaler ist es, die Anzahl der Durchläufe und die der Arbeitsschritte
gleichzusetzen. Im Falle der Implementation auf der ATI 9700 Karte war dies
allerdings nicht möglich, da die Anzahl der Arbeitsschritte durch die Anzahl
der Hardware-Shader auf 8 limitiert wurde. Des Weiteren versagt die Optimierung, wenn alle Abtastpunkte entlang des Strahls bis zum Verlassen des
Volumens berechnet werden müssen, weil sie z.B. stark transparent sind oder
eine hohe Dichte aufweisen.
3.3.2 Advanced GPU Raycasting
Die Ansätze von J.Krüger und R.Westermann wurden in der Arbeit von
H.Scharsach [5] um weitere Techniken erweitert.
Bounding Geometrie
Während für die eigentliche Berechnung der Seh-
strahlen nur die Fragment-Einheit der GPU genutzt wird, lässt sich die
11
d.h. Operationen wie +, -, *, dot oder Quadratwurzel
26
Vertex-Einheit z.B. dazu nutzen, die Bounding-Box der Struktur des Volumens anzupassen, indem die Bounding-Box um existierende Freiräume verkleinert wird (Abbildung 14).
Abbildung 14: Bounding Geometrie[5]
Bei dem von J.Krüger und R.Westermann vorgestellten Empty-SpaceSkipping wurde von Bounding-Box in Form eines Würfels mit 6 Flächen
ausgegangen. Die Rechenleistung heutiger Vertex-Einheiten erlaubt es mühelos ein Vielfaches an Flächen zu verwalten
12 . Mit dem von J.Krüger und
R.Westermann vorgestellten Blocking-Verfahren wird für jeden Block überprüft, ob der Inhalt für das Rendering interessant ist. Allerdings muss nebem
dem eigentlichen Block auch dessen unmittelbares Umfeld betrachtet werden,
da sich sonst durch die im Volumen verwendeten Filter Fehler ergeben können. Da nun diese Bounding-Geometrie als Grundlage für das Raycasting
verwendet wird, gestaltet sich die Unterscheidung in Front- und Back-Faces
etwas schwieriger, da nicht unbedingt von einer konvexen Struktur ausgegangen werden kann. Es wird somit zwischen dem ersten Front-Face und dem
letzten Back-Face unterschieden (Abbildung 15).
Interleaved Sampling
Die Berechnung von regulären Sampling-Pattern
sind einfach und schnell, führt aber zu dem Problem, dass über diesen Weg
auch Aliasing-Fehler entstehen können, die das Resultat verfälschen, z.B. in
Form von Ringstrukturen (Abbildung 16).
Das Interleaved Sampling basiert auf einer Arbeit von A. Keller and W.
Heidrich[7] und versucht dieses Problem durch die Verwendung von irregulären Pattern zu umgehen, die mehrere Pixel abdecken. Dabei wird ein kleiner
Oset in z-Richtung zwischen den Pixeln verwendet, die in unmittelbarer Nähe zueinander stehen, ähnlich dem des Supersamplings. Allerdings kann das
Ergebnis des Interleaved-Sampling auch zu ungewünschten Eekten führen,
z.B. wenn sich unmittelbar hintereinander zwei sehr dünne Strukturen im Volumen benden sollten. Das Interleaved-Sampling führt nun dazu, dass im
12
Die Anzahl sollte sich aber an der Shadereinheit der verwendeten Hardware orientieren
um die Gesamtleistung des Systems nicht auszubremsen
27
Abbildung 15: First front-face und last back-face[5]
Abbildung 16: Interleaved Sampling[7]
Ergebnisbild bei benachbarten Pixeln der Wert einmal aus der einen Struktur, und mal aus der anderen Struktur entnommen wird. Dies resultiert dann
in einem Dithermuster in den Farben der beiden Strukturen (Abbildung 17).
Abbildung 17: Dithering[5]
Umgangen werden kann der Eekt durch Verringern des Osets. Sollte dies nicht möglich sein, weil der Oset schon den kleinstmöglichen Wert
besitzt, zeigt dies, dass die Samplingrate des vorliegende Datensatz erhöht
werden sollte, um solch feine Strukturen unter Verwendung des Interleaved Sampling korrekt anzeigen zu können. Das Interleaved Sampling kann
28
im Fragment Shader einfach durch eine Modulo-Funktion der Bildschirmkoordinaten realisiert werden. Dieses Verfahren führt nicht zu wesentlichen
Einbuÿen in der Geschwindigkeit, und erzeugt dafür aber ein korrektes Ausgabebild.
29
4
Implementierung
4.1
Ziel
Das Ziel der Implementierung war es ein Programm zu entwickeln, mit dem
es möglich ist, den berechnungsintensiven Algorithmus des Raycastings auf
einer GPU zu berechnen. Der Ablauf sollte sich dabei an der bereits im Punkt
3.3.1 beschriebenen Arbeit Acceleration Techniques for GPU-based Volume
Rendering von J.Krüger und R.Westermann orientieren und die darin beschriebenen Ansätze, wie z.B. das Verfahren zur Richtungsbestimmung der
Sehstrahlen oder das Early-Ray-Termination berücksichtigen. Weiterhin ging
es darum, bei der Entwicklung den aktuellen Stand der Technik zu berücksichtigen um so das beschriebene Verfahren evtl. weiter zu beschleunigen.
Das in dieser Studienarbeit entwickelte Programm basiert auf OpenGL
und der OpenGL Shader Language. Die Wahl von OpenGL ermöglicht theoretisch den Einsatz auf unterschiedlichen Betriebssystemen und Hardwareumgebungen. Die Entwicklung der Anwendung erfolgte unter WindowsXP
mit einem OpenGL 2.0 unterstützendem Grakkartentreiber. Auf eine Bedienungsoberäche wurde verzichtet, stattdessen wurde GLUT für die Interaktion mittels der Maus oder Tastatur eingesetzt. Bei nur einer geringen
Anzahl an Interaktionsmöglichkeiten war dies absolut ausreichend.
4.2
Aufbau
Die Implementierung ist in ein Hauptprogramm und mehrere Shader-Programme
gegliedert.
4.2.1 Das Hauptprogramm
Der Ablauf des Hauptprogramms lässt sich folgendermaÿen darstellen:
1. Initialisieren von OpenGL und GLUT
2. Initialisieren und Laden von Texturen:
•
Eine 1D-Textur zur Speicherung einer Lookup-Tabelle für die
Transferfunktion.
•
Zwei 2D-Texturen, in denen jeweils die Front- und Back-Faces des
Würfels zwischengespeichert werden.
•
Eine 3D-Textur, in welche die Skalardaten des darzustellenden
Volumens geladen werden.
3. Laden, Kompilieren und Linken der zu verwendenden Shader.
4. Darstellung des Farbwürfels mittels der OpenGL xed-function Pipeline oder mittels Shader-Programm.
30
5. Zwischenspeichern der Front- und Backface-Ansichten des Würfels in
den 2D-Texturen.
6. Starten von Fragment-Shadern für die Berechnung des Raycastings.
7. Interaktionen:
•
Rotation und Skalierung des Würfels.
•
Wechseln zwischen unterschiedlichen Volumen.
•
Wechseln zwischen verschiedenen Optischen Modellen (in Form
unterschiedlicher Fragment-Shader).
•
Ändern der Anzahl von Abtastungen (Steps).
•
Gewichtung der Opazität (nur beim Emission-Absorptions Modell).
•
Verschieben der Farben bei der Transfer-Funktion (nur beim EmissionAbsorptions Modell mit Transfer-Funktion).
•
Messen der Berechnungsgeschwindigkeiten bei einer 360
◦ Drehung
des Volumens.
4.3
Die verwendeten Shader
Es wurden folgende Shader benutzt oder entwickelt:
•
colorcube.vert + colorcube.frag
13 :
Shader-Programme um einen RGB-Farbwürfel zu erzeugen.
•
emabs-kw.frag:
Fragment-Shader für die Darstellung des Emission-Absorbtions Compositings.
•
mip.frag:
Fragment-Shader für die Darstellung des Maximum-Intensity-Projection
Compositings.
•
mean.frag:
Fragment-Shader für die Darstellung des Mittelwert Compositings.
•
m.frag:
Fragment-Shader für die Darstellung des Raycastings mit erstem lokalem Maximum.
13
Übernommen aus den OpenGL Shading Language Tutorials von M. Christen[14]
31
•
emabs-tf.frag:
Fragment-Shader für die Darstellung des Emission-Absorbtions Compositings mit Transfer-Funktion.
•
emabs-ert.frag:
Fragment-Shader für die Darstellung des Emission-Absorbtions Compositings mit Early-Ray-Termination.
•
emabs-ess.frag:
Fragment-Shader für die Darstellung des Emission-Absorbtions Compositings mit vereinfachtem Empty-Space-Skipping.
•
emabs-sm.frag:
Fragment-Shader für eine alternative Darstellung des Emission-Absorbtions
Compositings.
4.3.1 colorcube.vert + colorcube.frag
Das Färben des Würfels in die Farben seiner Koordinaten geschieht über
eine Kombination eines Vertex- und Fragment-Shaders und lässt sich mit
wenigen Programmzeilen realisieren (Abbildung 18, Abbildung 19).
varying float xpos;
varying float ypos;
varying float zpos;
void main(void){
xpos = clamp(gl_Vertex.x,0.0,1.0);
ypos = clamp(gl_Vertex.y,0.0,1.0);
zpos = clamp(gl_Vertex.z,0.0,1.0);
}
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
Abbildung 18: Color-Cube Vertex-Shader
Zunächst werden die Vertex-Positionen des Würfels im Vertex-Shader bestimmt und auf einen Wertebereich von 0-1 übertragen (geclampt). Die Koordinaten werden mittels varying-Variablen an den Fragment-Shader übergeben, der diese Koordinaten als Farbinformationen für die Pixel der Oberäche des Würfels ausgibt.
32
varying float xpos;
varying float ypos;
varying float zpos;
void main (void){
}
gl_FragColor = vec4 (xpos, ypos, zpos, 1.0);
Abbildung 19: Color-Cube Fragment-Shader
4.3.2 emabs-kw.frag
Dieses Fragment-Shader Programm zeigt eine Implementation des Raycastings nach dem von J.Krüger und R.Westermann vorgestellten Verfahren
in der OpenGL Shader Language (Abbildung 20). Die weiteren FragmentProgramme basieren auf dieser Struktur und wurden entweder um einige
Code-Zeilen gekürzt oder erweitert. Die Änderungen werden in den jeweiligen Listings farblich kenntlich gemacht.
Zu Beginn des Fragment-Shaders werden aus der 2D-Textur mit der Darstellung der Front-Faces des Farbwürfels die Eintrittspunkte der Sehstrahlen
in das Volumen ausgelesen und in einem Vektor
texFront gespeichert. Eben-
so werden die Daten der Austrittspunkte über die 2D-Textur der Back-Faces
in einem weiteren Vektor
texBack abgelegt. Über eine Subtraktion des ersten
Vektors von dem Zweiten lässt sich die Richtung der Sehstrahlen ermitteln.
Diese wird normalisiert in einem weiteren Vektor
direction
gespeichert.
In dem Sehstrahl wird jetzt über die nicht normalisierte Länge des Richtungsvektors hinweg jeweils der Abtastpunkt ermittelt und der enthaltene
Voxelwert in
ses
alpha
alpha
gespeichert. Die Opazität wird anschlieÿend über die-
und einem gewählten Faktor
opacityfac
bestimmt. Im folgenden
Schritt erfolgt nun die Berechung des Ausgabewertes
output
unter Berück-
sichtigung des aktuellen und der bereits ermittelten alpha-Werte. Danach
wird der nächste Abtastpunkt bestimmt.
Sobald alle Abtastpunkte entlang des Sehstrahls ausgewertet wurden,
wird der für den aktuellen Pixel bestimmte Farbwert ausgegeben.
4.3.3 mip.frag
Bei diesem Programm wird eine Maximum-Intensity-Projektion ausgeführt,
bei der kein Blending, sondern eine Anzeige der Abtastpunkte mit der höchsten Ausprägung erfolgt (Abbildung 21).
33
4.3.4 mean.frag
In diesem Programm wird der Mittelwert über alle Abtastpunkte eines Sehstrahls errechnet und anschliessend dargestellt. Den Unterschied zu dem
Emission-Absorptions Modell sieht sieht man in Abbildung 22.
Die Werte der Abtastpunkte werden dazu aufaddiert (output
+= alpha)
und vor der Ausgabe als Fragmentfarbe durch die Anzahl der abgetasteten
Punkte dividiert (output
/= alphacount).
4.3.5 m.frag
Bei diesem Programm wird entlang eines Sehstrahls das erste auftretende
Maximum bestimmt (Abbildung 23).
Die Schleife wird sofort verlassen, sobald es einen Abtastwert mit einem
niedrigeren Skalarwert geben sollte.
4.3.6 emabs-tf.frag
Bei diesem Programm wurden zu dem Emission-Absorptions Shader eine
Transfer-Funktion hinzugefügt (Abbildung 24).
Dem aus dem Volumen ermittelten Abtastwert wird der Farbwert
aus einer Lookup-Tabelle
texUnit1D
alphaTF
zugeordnet.
4.3.7 emabs-ert.frag
Bei diesem Programm wurde dem bereits vorgestellten Emission-Absorptions
Shader nur eine Programmzeile für die Early-ray-Termination hinzugefügt
(Abbildung 25).
Das Programm springt aus der Schleife sobald der Abtastwert sein Maximum erreicht hat.
4.3.8 emabs-ess.frag
Bei diesem Programm wurde dem bereits vorgestellten Emission-Absorptions
Shader eine Bedingung für die Berechnung des Ausgabewertes hinzugefügt
(Abbildung 26).
Die Berechnung wird übersprungen, falls an der Abtaststelle kein Skalarwert vorhanden ist (alpha
= 0).
4.3.9 emabs-sm.frag
Dieser Shader unterscheidet sich vom bereits vorgestellten Verfahren in der
Längenbestimmung des Sehstrahls (Abbildung 27).
34
Hierbei wird nicht die in den anderen Verfahren ermittelte Distanz zwischen Eintritts- und Austrittspunkt des Sehstrahls in das Volumen berücksichtigt, sondern für jeden Abtastpunkt über jeweils zwei if-Abfragen bestimmt, ob er sich innerhalb des Volumens bendet. Der Durchlauf über die
Abtastpunkte erfolgt über eine for-Schleife mit int-Werten. Diese Maÿnahme
erhöht die Geschwindigkeit ein wenig, obwohl dies eine zusätzliche Division
im Shader erfordert.
4.4
Die verwendeten Volumendaten
Die folgenden Volumendatensätze wurden bei der Implementierung geladen
um die Darstellungsqualität, die Funktionen und die Geschwindigkeit der
Implementierung zu testen. Die Datensätze sind jeweils auf eine Auösung
von 8bit beschränkt. Andere Auösungen wurden in der Implementierung
nicht berücksichtigt.
•
head256.raw[15]:
Zeigt eine CT-Aufnahme eines Kopfes.
Auösung: 256x256x225 Pixel; 1,1,1 Schicht-Dicke; 8Bit Auösung.
•
BostonTeapot.raw[13]:
Zeigt eine CT-Aufnahme des Boston-Teapots mit einem innenliegenden
Hummer.
Auösung: 256x256x178 Pixel; 1,1,1 Schicht-Dicke; 8Bit Auösung.
•
engine.raw[13]:
Zeigt eine CT-Aufnahme eines Motorblocks.
Auösung: 256x256x128 Pixel; 1,1,1 Schicht-Dicke; 8Bit-Auösung.
•
backpack8.raw[13]:
Zeigt eine CT-Aufnahme eines mit Gegenständen gefüllten Rucksacks.
Auösung: 512x512x373 Pixel, 0.9766, 0.9766, 1.25 Schicht-Dicke, 8Bit
Auösung.
35
//GPU EMISSION-ABSORPTION - KRUEGER WESTERMANN
uniform sampler2D texUnit1;
uniform sampler2D texUnit2;
uniform sampler3D texUnit3D;
uniform float stepsize;
uniform float opacityfac;
void main (void){
vec3 texFront = texture2D(texUnit1, gl_TexCoord[0].st);
vec3 texBack = texture2D(texUnit2, gl_TexCoord[0].st);
vec3 direction = normalize(texBack - texFront);
float output = 0.0;
float alpha;
float opacity;
for (float dirlength = length(texBack - texFront);
dirlength > 0.0; dirlength - stepsize){
alpha = (texture3D(texUnit3D, texFront)).g;
//weight opacity
opacity = (alpha/opacityfac);
//raycast
output = opacity * alpha + (1.0 - opacity)*output;
//next position
texFront = texFront + (stepsize * direction);
}
}
gl_FragColor = vec4 (output,output,output,1.0);
Abbildung 20: Emission-Absorption Fragment-Shader
36
//GPU EMISSION-ABSORPTION - KRUEGER WESTERMANN
uniform sampler2D texUnit1;
uniform sampler2D texUnit2;
uniform sampler3D texUnit3D;
uniform float stepsize;
uniform float opacityfac;
void main (void){
vec3 texFront = texture2D(texUnit1, gl_TexCoord[0].st);
vec3 texBack = texture2D(texUnit2, gl_TexCoord[0].st);
vec3 direction = normalize(texBack - texFront);
float output = 0.0;
float alpha;
float opacity;
for (float dirlength = length(texBack - texFront);
dirlength > 0.0; dirlength - stepsize){
alpha = (texture3D(texUnit3D, texFront)).g;
if (alpha > output) output = alpha;
//next position
texFront = texFront + (stepsize * direction);
}
}
gl_FragColor = vec4 (output,output,output,1.0);
Abbildung 21: Maximum Intensity Projection
37
//GPU FIRST LOCAL MAXIMUM
uniform sampler2D texUnit1;
uniform sampler2D texUnit2;
uniform sampler3D texUnit3D;
uniform float stepsize;
void main (void){
vec3 texFront = texture2D(texUnit1, gl_TexCoord[0].st);
vec3 texBack = texture2D(texUnit2, gl_TexCoord[0].st);
vec3 direction = normalize(texBack - texFront);
float output = 0.0;
float alpha;
float opacity;
int alphacount;
for (float dirlength = length(texBack - texFront);
dirlength > 0.0; dirlength - stepsize){
alpha = (texture3D(texUnit3D, texFront)).g;
++alphacount;
output += alpha;
//next position
texFront = texFront + (stepsize * direction);
}
output /= alphacount;
}
gl_FragColor = vec4 (output,output,output,1.0);
Abbildung 22: Mittelwert
38
//GPU FIRST LOCAL MAXIMUM
uniform sampler2D texUnit1;
uniform sampler2D texUnit2;
uniform sampler3D texUnit3D;
uniform float stepsize;
void main (void){
vec3 texFront = texture2D(texUnit1, gl_TexCoord[0].st);
vec3 texBack = texture2D(texUnit2, gl_TexCoord[0].st);
vec3 direction = normalize(texBack - texFront);
float output = 0.0;
float alpha;
float opacity;
for (float dirlength = length(texBack - texFront);
dirlength > 0.0; dirlength - stepsize){
alpha = (texture3D(texUnit3D, texFront)).g;
if (output <= alpha){
output = alpha;
}
else break;
//next position
texFront = texFront + (stepsize * direction);
}
}
gl_FragColor = vec4 (output,output,output,1.0);
Abbildung 23: First Local Maximum
39
//GPU EMISSION-ABSORPTION - TRANSFER FUNCTION
uniform
uniform
uniform
uniform
sampler1D
sampler2D
sampler2D
sampler3D
texUnit1D;
texUnit1;
texUnit2;
texUnit3D;
uniform float stepsize;
uniform float opacityfac;
void main (void){
vec3 texFront = texture2D(texUnit1, gl_TexCoord[0].st);
vec3 texBack = texture2D(texUnit2, gl_TexCoord[0].st);
vec3 direction = normalize(texBack - texFront);
vec3 output = vec3(0.0, 0.0, 0.0);
float alpha;
float opacity;
vec3 alphaTF;
for (float dirlength = length(texBack - texFront);
dirlength > 0.0; dirlength - stepsize){
alpha = (texture3D(texUnit3D, texFront)).g;
alphaTF = (texture1D(texUnit1D, alpha).rgb);
//weight opacity
opacity = (alpha/opacityfac);
//raycast
output = opacity * alphaTF + (1.0 - opacity)*output;
//next position
texFront = texFront + (stepsize * direction);
}
gl_FragColor = vec4 (output,1.0);
}
Abbildung 24: Transfer-Funktion
40
//GPU EMISSION-ABSORPTION - EARLY-RAY-TERMINATION
uniform sampler2D texUnit1;
uniform sampler2D texUnit2;
uniform sampler3D texUnit3D;
uniform float stepsize;
uniform float opacityfac;
void main (void){
vec3 texFront = texture2D(texUnit1, gl_TexCoord[0].st);
vec3 texBack = texture2D(texUnit2, gl_TexCoord[0].st);
vec3 direction = normalize(texBack - texFront);
float output = 0.0;
float alpha;
float opacity;
for (float dirlength = length(texBack - texFront);
dirlength > 0.0; dirlength - stepsize){
alpha = (texture3D(texUnit3D, texFront)).g;
//weight opacity
opacity = (alpha/opacityfac);
//raycast
output = opacity * alpha + (1.0 - opacity)*output;
if (output >= 1.0) break;
//next position
texFront = texFront + (stepsize * direction);
}
}
gl_FragColor = vec4(output,output,output,1.0);
Abbildung 25: Early-Ray-Termination
41
//GPU EMISSION-ABSORPTION - EMPTY-SPACE-SKIPPING
uniform sampler2D texUnit1;
uniform sampler2D texUnit2;
uniform sampler3D texUnit3D;
uniform float stepsize;
uniform float opacityfac;
void main (void){
vec3 texFront = texture2D(texUnit1, gl_TexCoord[0].st);
vec3 texBack = texture2D(texUnit2, gl_TexCoord[0].st);
vec3 direction = normalize(texBack - texFront);
float output = 0.0;
float alpha;
float opacity;
for (float dirlength = length(texBack - texFront);
dirlength > 0.0; dirlength - stepsize){
alpha = (texture3D(texUnit3D, texFront)).g;
if (alpha > 0.0){
//weight opacity
opacity = (alpha/opacityfac);
//raycast
output = opacity * alpha + (1.0 - opacity)*output;
}
//next position
texFront = texFront + (stepsize * direction);
}
}
gl_FragColor = vec4 (output,output,output,1.0);
Abbildung 26: Einfaches Empty-Space-Skipping
42
//GPU EMISSION-ABSORPTION - MODIFIED
uniform sampler2D texUnit1;
uniform sampler2D texUnit2;
uniform sampler3D texUnit3D;
uniform int steps;
uniform float opacityfac;
void main (void){
vec3 texFront = texture2D(texUnit1, gl_TexCoord[0].st);
float output = 0.0;
if ((texFront.r > 0.0) ||( texFront.g > 0.0)
|| (texFront.b > 0.0)){
vec3 texBack = texture2D(texUnit2, gl_TexCoord[0].st);
vec3 direction = normalize(texBack - texFront);
float alpha;
float opacity;
float stepsize = 1.73/steps;
for (int step = 0; step < steps; ++step){
alpha = texture3D(texUnit3D, texFront).g;
//weight opacity
opacity = alpha/opacityfac;
//raycast
output = opacity * alpha + (1.0 - opacity)*output;
//next position
texFront = texFront + (stepsize * direction);
}
}
//ray is outside volume
if ((texFront.r >= 1.0) || (texFront.g >= 1.0)
|| (texFront.b >= 1.0)){
break;
}
}
gl_FragColor = vec4 (output,output,output,1.0);
43
Abbildung 27: Modizierter Emission-Absorbtion Fragment-Shader
5
Visuelle Beurteilung
Im Folgenden werden die Bildausgaben der Implementierung je nach gewähltem Volumendatensatz bewertet. Die Screenshots wurden auf einem
14 und einem
WindowsXP System mit einer Nvidia 6600 GT Grakkarte
OpenGL2.0 kompatiblen Treiber mit der Version 77.76 erstellt.
5.1
Ergebnisse
5.1.1 Emission-Absortion
Alle Emission-Absortions-Shader (emabs-kw.frag, emabs-ert.frag, emabs-ess.frag,
emabs-sm.frag) liefern für die ausgewählten Volumendatensätze die in Abbildung 28 dargestellten Ergebnisse.
5.1.2 Maximum-Intensity-Projection
Die Ergebnisse des Maximum-Intensity-Projection Shaders kann man in Abbildung 29 sehen.
Bei den Aufnahmen der Maximum-Intensity-Projection werden die Bereiche im Volumen mit der höchsten Dichte hervorgehoben.
5.1.3 Mittelwert
Die Ergebnisse des Mittelwert Shaders werden in Abbildung 30 dargestellt.
Die Darstellungen sehen denen von Röntgenaufnahmen ähnlich, da sich
jedes Voxel entlang eines Sehstrahls zu gleichen Teilen auf die Ausgabe auswirkt.
5.1.4 First-Local-Maximum
Die Ergebnisse des First-Local-Maximum sind in Abbildung 31 zu sehen.
Die Ergebnisse sind auf den ersten Blick verwirrend und es entsteht der
Eindruck, als würden sehr viele Fehler in den Bildern auftreten. Die Berechnung sollte aber korrekt sein und die unbrauchbare Visualisierung ergibt sich
durch die Tatsache, dass in den Volumen ein Rauschen enthalten ist. Sobald
der Sehstrahl nur auf einen einzigen verrauschten Punkt treen sollte, wird
die Berechnung abgebrochen. Der einzige Volumensatz, der weitestgehend
ohne Fehler dargestellt wird, ist der des Teapots(b). Dieser Shader eignet
sich gut zur Oberächenerkennung, jedoch nur, wenn die Aufnahmen frei
von Störungen sind.
14
Diese ist zu der in Abschnitt 3.1 vorgestellten Hardware der Nvidia 6800 Ultra in den
Funktionen voll kompatibel, allerdings in der Taktrate für Prozessor und Speicher und der
Anzahl der Pipelines für die Vertex- und Fragmenteinheit beschränkt worden.
44
5.1.5 Emission-Absortion mit Transferfunktion
Der Emission-Absortions Shader mit aktivierter Transferfunktion liefert die
in Abbildung 32 gezeigten Ergebnisse.
Die Ergebnisse verdeutlichen die Funktion der Lookup-Tabelle. Die Transferfunktion ist in diesen Beispielen in nur zwei Stufen eingeteilt und es erfolgt
bei einem Dichtewert von 37,5 % vom Maximalwert eine farbliche Trennung.
Getroenes Material mit einer hohen Dichte, wie z.B. die Knochenmasse(a)
oder das Metall des Motorblocks(b) werden grün dargestellt, während Material mit einer niedrigen Dichte rot gefärbt wird, wie z.B. die Haut(a).
5.2
Vergleich
Die Qualität der Bilder kann in einem direkten Vergleich mit den anderen,
bereits vorgestellten Implementierungen bestimmt werden. Als Vergleichsapplikationen wurden zwei Programme verwendet. Zum einen mit einem CPURaycaster, der von C.Rezk-Salama in der Übung zu der Vorlesung Visualisierung erstellt wurde[12], zum Anderen mit einer Implementierung zu der
Arbeit A Simple and Flexible Volume Rendering Framework for GraphicsHardware-based Raycasting, die Zeitgleich zur Studienarbeit von S. Stegmaier, M. Strengert, T. Klein, und T. Ertl vorgestellt wurde[15]. Bei allen
Programmen wurden die Volumen in die gleiche Richtung rotiert und sofern
möglich die gleichen Parameter gewählt. Der Vergleich beschränkt sich auf
den Volumendatensatz head256.raw, mit einer Abtastrate von 128 Schritten.
5.2.1 Emission-Absortion
In allen Bildern sind die gleichen Strukturen zu erkennen, so dass davon
ausgegangen werden kann, dass die Darstellung des GPURaycasters korrekt
ist (Abbildung 33).
5.2.2 Maximum-Intensity-Projection
In der Maximum-Intensity-Projection Visualisierung sind die im Volumen
enthaltenen Strukturen besser zu erkennen. Es fällt auf, dass die Bilder der
beiden anderen Implementierungen eine feinere Auösung liefern (Abbildung
34). Dies hängt mit der Skalierung der Ausgabe zusammen, bei der die Werte
nicht linear interpoliert werden.
45
(a) head256 (Opacity=8,Steps=256)
(b) BostonTeapot (Opacity=8,Steps=256)
(c) engine (Opacity=2,Steps=256)
(d) backpack8 (Opacity=2,Steps=512)
Abbildung 28: Ausgaben des Emissions-Absorptions Shaders
46
(a) head256 (Steps=256)
(b) BostonTeapot (Steps=256)
(c) engine (Steps=256)
(d) backpack8 (Steps=512)
Abbildung 29: Ausgaben des Maximum-Intensity-Projection Shaders
47
(a) head256 (Steps=256)
(b) BostonTeapot (Steps=256)
(c) engine (Steps=256)
(d) backpack8 (Steps=512)
Abbildung 30: Ausgaben des Mean-Value Shaders
48
(a) head256 (Steps=256)
(b) BostonTeapot (Steps=256)
(c) engine (Steps=256)
(d) backpack8 (Steps=512)
Abbildung 31: Ausgaben des First-Local-Maximum Shaders
49
(a) head256 (Opacity=8,Steps=256)
(b) BostonTeapot (Opacity=8,Steps=256)
(c) engine (Opacity=2,Steps=256)
(d) backpack8 (Opacity=2,Steps=512)
Abbildung 32: Ausgaben des Emissions-Absorptions Shaders mit aktivierter
Transferfunktion
50
(a) CPU-Raycaster (Steps=128)
(b) SPVolRen (Steps=128)
(c) GPU-Raycaster (Steps=128)
Abbildung 33: Ausgaben des Emission-Absorbtions Verfahrens (Vergleich)
51
(a) Raycaster (Steps=128)
(b) SPVolRen (Steps=128)
(c) GPU-Raycaster (Steps=128)
Abbildung
34:
Ausgaben
des
Maximum-Intensity-Projection
(Vergleich)
52
Verfahrens
6
Beurteilung der Geschwindigskeit
Die Güte der Implementierung läÿt sich nicht nur an der visuellen Qualität,
sondern auch an der Performanz feststellen. Bei allen in dieser Implementierung enthaltenen Shadern wurde die durchschnittliche Framerate gemessen,
die das Programm für eine Drehung des Volumens um 360
◦ um die X-Achse
benötigt. Diese Frameraten wurden ebenso mit den Frameraten der anderen
Implementierungen verglichen. Als Testrechner wurde ein AMD Athlon 64
3200+ mit 1024MB Ram und einer Nvidia 6600GT Grakkarte verwendet.
6.1
Frameraten
Bei der Implementierung dieser Studienarbeit wurden alle Compositing-Verfahren
mit ihren Variationen mit den bereits vorgestellten Volumensätzen in ihrer
Darstellungsgeschwindigkeit gemessen. Die Ergebnisse für die jeweiligen Volumensätze werden in den Tabellen 1-4 dargestellt.
emabs
mip
mean
m
-kw
emabs
emabs
emabs
emabs
-tf
-ert
-ess
-sm
128 Steps
05.68
05.69
06.04
27.59
05.31
04.51
05.67
08.80
256 Steps
04.71
04.72
04.96
17.57
04.48
03.87
04.72
04.67
Tabelle 1: Frameraten für head256.raw
emabs
mip
mean
m
-kw
emabs
emabs
emabs
emabs
-tf
-ert
-ess
-sm
128 Steps
06.39
06.40
06.89
09.96
05.95
04.96
06.40
09.83
256 Steps
05.49
05.51
05.76
08.93
05.17
04.40
05.51
05.28
emabs
Tabelle 2: Frameraten für BostonTeapot.raw
emabs
mip
mean
m
-kw
emabs
emabs
emabs
-tf
-ert
-ess
-sm
128 Steps
10.05
10.05
10.05
18.52
08.61
06.70
10.05
15.13
256 Steps
09.45
09.42
10.77
15.70
08.52
06.85
09.42
08.07
Tabelle 3: Frameraten für engine.raw
6.2
Analyse
Zunächst wird bei dieser Analyse für jeden Shader der Geschwindigkeitsunterschied in Bezug zu der Schrittgröÿe entlang des Sehstrahls ausgewertet
(Tabelle 5). Im Anschluss wird der Geschwindigkeitsunterschied im Vergleich
zum Emissions-Absorbtions Shader bestimmt (Tabelle 6).
53
emabs
mip
mean
m
emabs
emabs
emabs
-tf
-ert
-ess
-sm
-kw
emabs
256 Steps
02.58
02.58
02.66
06.50
02.51
02.31
02.58
02.69
512 Steps
01.89
01.89
01.93
05.12
01.85
01.74
01.89
01.78
Tabelle 4: Frameraten für backpack8.raw
emabs
mip
mean
m
emabs
emabs
emabs
-tf
-ert
-ess
-sm
-kw
emabs
128-256
-17%
-17%
-18%
-36%
-16%
-14%
-17%
-47%
256-512
-27%
-27%
-27%
-21%
-26%
-25%
-27%
-34%
Tabelle 5: Geschwindigkeitsunterschiede zwischen den Schrittgrössen
mip
mean
m
emabs
emabs
emabs
-tf
-ert
-ess
emabs
-sm
128 Steps
0%
+6%
+385%
-7%
-21%
0%
+54%
256 Steps
0%
+5%
+273%
-5%
-18%
0%
+1%
512 Steps
0%
+3%
+170%
-4%
-8%
0%
+6%
Tabelle 6: Geschwindigkeitsunterschiede zu emabs-kw.frag
54
6.3
Vergleich
Der Vergleich der Frameraten beschränkt sich auf das Emission-Absorbtions
und das Maximum-Intensity-Projection Verfahren, da diese als einzige von
allen drei Implementierungen zur Verfügung gestellt werden. Die SPVolRen
Applikation bietet von sich aus eine Benchmark Funktion an, bei der CPURaycaster Anwendung musste diese erst im Quellcode hinzugefügt werden.
Der Vergleich beschränkt sich ebenso wie der Darstellungsvergleich auf den
head256.raw Datensatz (Vergleichsergebisse siehe Tabelle 7).
Emisson-Abs.
Emisson-Abs.
MIP
MIP
128 Steps
256 Steps
128 Steps
256 Steps
CPU-Raycaster
00.08
00.08
SPVolRen
03.36
02.17
07.11
04.15
GPU-Raycaster
05.68
04.71
05.69
04.72
08.80
04.67
nach Krüger-W.
GPU-Raycaster
modiziert
Tabelle 7: Frameraten für head256.raw (Vergleich)
Durch die Implementierung des GPU-Raycasters konnte die Geschwindigkeit im Vergleich zu der des CPU-Raycasters erheblich gesteigert werden.
Die Geschwindigkeit liegt in drei von vier Tests über der der SPVolRen Implementierung, die ebenso auf einem Pixel-Shader 3.0 basiert. Allerdings liefert
SPVolRen ein feiner aufgelöstes Bild. Man kann davon ausgehen, dass die
erzielte Geschwindigkeit des GPU-Raycaster auf Kosten der Darstellungsqualität geht.
55
7
Fazit und Ausblick
Im Rahmen dieser Studienarbeit, ist es gelungen, einen GPU basierten Raycaster zu entwickeln, der sowohl visuell, als auch in der Geschwindigkeit mit
anderen Lösungen zum Raycasting-Verfahren mithalten kann. Die Implementierung dieser Studienarbeit lies sich durch die Verwendung von aktueller
Grakhardware in einem Single-PassVerfahren realisieren. Dabei wird das
Ergebnis in nur einem einzelnen Shader-Durchlauf berechnet. Ein Zwischenspeichern von Shader-Ergebnissen und ein nochmaliges Aufrufen konnte somit entfallen. Mit der Unterstützung von Abbruchsanweisungen durch die
Hardware und GLSL war es möglich eine Early-Ray-Termination zu realisieren, wie sie z.B. in Abbildung 25 zu sehen ist. Ein solcher Break ist auch für
das First-Local-Maximum Verfahren zwingend notwendig (Abbildung 23).
Weiterhin wurden von der Hardware und GLSL automatisch eine trilineare
Interpolation der 3D-Textur, sowie weitere hilfreiche Funktionen, wie z.B.
die Normalisierung und Längenbestimmung von Vektoren zur Verfügung gestellt.
Allerdings hat sich auch gezeigt, dass die Flusskontrolle in den Shadern
die Leistung stark mindert. So zeigt ein Geschwindigkeitsvergleich zwischen
dem Emissions-Absorbtions Shader ohne Optimierung (Abbildung 20) und
des Emissions-Absorbtions Shader mit Early-Ray-Termination (Abbildung
25), dass eine einzige hinzugefügte if-Abfrage incl. conditional break die Leistung um bis zu 21% mindert, obwohl eigentlich der Berechnungsaufwand
durch dieses Hinzufügen minimiert werden sollte (Tabelle 6). So konnte die
Early-Ray-Termination nicht den erhoten Erfolg erzielen. Verwunderlich
ist auch die Tatsache, dass die Leistung des Emissions-Absorbtions Shaders mit einem einfachen Empty-Space-Skipping (Abbildung 26) sich von
der des Emissions-Absorbtions Shader ohne Optimierung nicht unterscheidet, obwohl hier ebenfalls nur eine if-Anweisung hinzugefügt wurde, diese
allerdings in diesem Fall ohne ein break. Das Raycasting-Verfahren konnte
für die Darstellung mit grossen Schrittweiten (max. 256 Steps pro Sehstrahl)
sogar noch optimiert werden (Abbildung 27) und lieferte Geschwindigkeiten,
die im Vergleich zum Emissions-Absorbtions Shader ohne Optimierung um
bis zu 54% höher lagen (Tabelle 6).
Zunächst war auch angedacht, ein CPU-basiertes Raycasting Verfahren
zu Vergleichszwecken in die Implementierung zu integrieren. Dieses sollte
ebenso an das Verfahren von J.Krüger und R.Westermann angelehnt sein.
Die Entwicklung wurde allerdings nach kurzer Zeit wieder verworfen, da der
Umfang, dies zu realisieren, doch um einiges gröÿer war, als zunächst angenommen. Die Implementierung müsste um zahlreiche Methoden erweitert
werden, die standardmäÿig nicht zur Verfügung gestellt werden. Dazu zählen
z.B. die Unterstützung von Vektoren und die damit verbundenen Funktionen
wie die Längenbestimmung oder die Normalisierung.
56
Die Gesamtgeschwindingkeit der Visualisierung wird hauptsächlich durch
die der Grakkarte beschränkt. Die von Windows angezeigte CPU Auslastung lag bei dem verwendetem Testsystem bei 0 - 1%. Durch die Verwendung
von leistungsfähigeren Grakprozessoren, wie z.B. des NV40 auf der Nvidia
6800 Ultra Grakkarte konnte eine nochmals gesteigerte Leistung erzielt wer-
15 . Interessant dürfte ein Test mit den aktuell vorgestellten Grakkarten
den
von Nvidia basierend auf G70 Generation sein. Auch soll demnächst ATI eine neue Reihe von Grakkarten basierend auf der R520 GPU auf den Markt
bringen, die ebenso die in dieser Arbeit verwendeten Funktionen des Shader
Model 3.0 vollständig unterstützen.
Grosse Honung wird auch in den kommenden Cell-Prozessor von IBM,
Toshiba und Sony gesetzt
16 . Das das Thema Raycasting auch noch in naher
Zukunft aktuell sein wird, zeigen laufend neu veröentlichte Papers zu diesem
Thema. Dies unterstreicht, wie wichtig dieses Gebiet in der Forschung und
in der Industrie ist.
15
Die zugehörigen Testergebnisse mussten leider wegen einer Änderung des Berechnungsalgorithmus verworfen werden.
16
Die Firma Mercury Computer Systems plant z.B. den Cell Prozessor in der Medizinischen Bildverarbeitung für Diagnosesysteme einzusetzen[16].
57
Literatur
[1] Ian Buck and T. Purcell. A Toolkit for Computation on
GPUs. In GPU Gems. Addison-Wesley. 2004.
[2] M. Hadwiger and C. Rezk-Salama. Volume Rendering. In
Course 28: Real-Time Volume Graphics. ACM Computer
Graphics, Proc. SIGGRAPH 2004, 1828, August 2004.
[3] S. Röttger, S. Guthe, D. Weiskopf, and T. Ertl. Smart
Hardware-Accelerated Volume Rendering. In Procceedings
of EG/IEEE TCVG Symposium on Visualization VisSym
'03, pages 231-238, 2003.
[4] J. Krüger and R. Westermann. Acceleration Techniques
for GPU-based Volume Rendering. August 2003.
[5] Henning Scharsach. Advanced GPU Raycasting. March
2005.
[6] Paolo Sabella. A Rendering Algorithm for Visualizing
3D Scala Fields. ACM Computer Graphics, Proc. SIGGRAPH '84, 22(4):5158, August 1984.
[7] A. Keller and W. Heidrich. Interleaved sampling. In Proceedings of the 12th Eurographics Workshop on Rendering
Techniques, pages 269276, 2001.
[8] Mooresches
Gesetz,
Mooresches_Gesetz
http://de.wikipedia.org/wiki/
[9] R. Moore. OpenGL Shading Language. Addison Wesley.
2004.
[10] M. Biedermann. Visualisierung und Volumenrendering.
In
Computergrak
Institut
für
2.
Arbeitsgruppe
Computervisualistik.
Computergrak.
Universität
Koblenz-
http://www.uni-koblenz.de/
FB4/Institutes/ICV/AGMueller/Teaching/WS0405/CG2
Landau. WS 2004/2005.
[11] L.
Weinand.
Die
Gröÿe
machts:
GeForce
6800
Ultra
http://www.de.tomshardware.com/graphic/
20040414/index.html
(NV40).
[12] C.
Rezk-Salama.
Visualisierung.
Computergrak
und
Multimediasysteme. Universität Siegen. WS 2004/2005.
http://www.cg.informatik.uni-siegen.de/Teaching/
Lectures/WS_04_05/VIS
58
[13] volvis.org.
http://www.volvis.org
http:
//www.clockworkcoders.com/oglsl/tutorial4.htm
[14] M. Christen. OpenGL Shading Language Tutorials.
[15] S. Stegmaier, M. Strengert, T. Klein, and T. Ertl. A
Simple and Flexible Volume Rendering Framework for
Graphics-Hardware-based Raycasting, Proceedings of Volume
Graphics
2005,
Stony
Brook,
New
York,
USA,
industrielle
und
medizinische
pp.187-195, 2005.
[16] Cell-Prozessoren
Bildverarbeitung.
meldung/61141
für
die
http://www.heise.de/newsticker/
59
Herunterladen