18.07.2014 Aufrufe

Übungen zu den Lehrveranstaltungen 710.003 Computergrafik 1 ...

Übungen zu den Lehrveranstaltungen 710.003 Computergrafik 1 ...

Übungen zu den Lehrveranstaltungen 710.003 Computergrafik 1 ...

MEHR ANZEIGEN
WENIGER ANZEIGEN

Erfolgreiche ePaper selbst erstellen

Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.

Übungen <strong>zu</strong> <strong>den</strong> <strong>Lehrveranstaltungen</strong><br />

<strong>710.003</strong> <strong>Computergrafik</strong> 1<br />

710.005 Computer Vision 1<br />

Institut für Maschinelles Sehen und Darstellen<br />

Sommersemester 2011<br />

Horst Bischof, Gerhard Reitmayr,<br />

Andreas Wendel, Thomas Mauthner, Denis Kalkofen, Marc Streit, Alexander Lex<br />

4. März 2011<br />

1 Allgemeines<br />

Dieses Dokument enthält die Aufgabenstellungen für die Einführungslehrveranstaltungen „<strong>Computergrafik</strong><br />

1“ und „Computer Vision 1“, die vom Institut für Maschinelles Sehen und Darstellen im Sommersemester<br />

2011 angeboten wer<strong>den</strong>. Die Aufgabenstellungen <strong>zu</strong> „<strong>Computergrafik</strong> 2“ und „Computer Vision 2“ folgen<br />

in einem separaten Dokument. Bitte lesen Sie in Tabelle 1 nach, welche Aufgaben für die von Ihnen<br />

besuchten <strong>Lehrveranstaltungen</strong> benötigt wer<strong>den</strong>.<br />

Lehrveranstaltung Übungsbeispiel(e)<br />

<strong>710.003</strong> <strong>Computergrafik</strong> 1 cg/task1a + cg/task1b<br />

710.004 <strong>Computergrafik</strong> 2 cg/task2<br />

710.005 Computer Vision 1 cv/task1a + cv/task1b<br />

710.006 Computer Vision 2 cv/task2<br />

Tabelle 1: Zuordnungen der Übungsbeispiele <strong>zu</strong> <strong>den</strong> Modulen<br />

Diese <strong>Lehrveranstaltungen</strong> wur<strong>den</strong> mit dem Sommersemester 2007 neu eingeführt und ersetzen die entsprechen<strong>den</strong><br />

bis <strong>zu</strong>m Sommersemester 2006 angebotenen <strong>Lehrveranstaltungen</strong>. Sollten in Ihrem Studienplan<br />

also noch die alten Bezeichnungen aufgelistet sein, gelten die Äquivalenzen laut Tabelle 2.<br />

alte Lehrveranstaltung<br />

äquivalent <strong>zu</strong><br />

710.040 Bildanalyse und <strong>Computergrafik</strong> <strong>710.003</strong> + 710.005<br />

710.060 Visuelle Informationssysteme <strong>710.003</strong> + 710.005<br />

710.106 Computer Vision 710.005 + 710.006<br />

710.107 Computer Grafik <strong>710.003</strong> + 710.004<br />

710.552 Bildanalyse und <strong>Computergrafik</strong> für SEWM 710.006<br />

710.662 Visuelle Informationssysteme für Telematiker 710.004<br />

Tabelle 2: Äquivalenzen zwischen <strong>den</strong> bis 2006 angebotenen und <strong>den</strong> ab 2007 angebotenen Einführungslehrveranstaltungen<br />

des ICG<br />

1


Die Aufgaben bestehen jeweils aus mehreren Schritten, die <strong>zu</strong>m Teil aufeinander aufbauen, jedoch unabhängig<br />

voneinander beurteilt wer<strong>den</strong>. Dadurch ist einerseits eine objektive Beurteilung sichergestellt<br />

und andererseits gewährleistet, dass auch bei unvollständiger Lösung der Aufgaben Punkte erzielt wer<strong>den</strong><br />

können. Als Ergän<strong>zu</strong>ng <strong>zu</strong>r Aufgabenstellung wer<strong>den</strong> im Sekretariat des ICG ab 07. März 2011 einzelne<br />

Abschnitte der im Literaturverzeichnis erwähnten Bücher <strong>zu</strong>r Kopie aufliegen (gegen Hinterlegung des<br />

Stu<strong>den</strong>tenausweises). Wir empfehlen Ihnen, von diesem Angebot Gebrauch <strong>zu</strong> machen, da Sie in diesem<br />

Dokument nur eine kurze Einführung <strong>zu</strong> <strong>den</strong> jeweiligen Übungsthemen fin<strong>den</strong>.<br />

Wir weisen ausdrücklich darauf hin, dass die Übungsaufgaben von jedem Teilnehmer eigenständig<br />

gelöst wer<strong>den</strong> müssen. Wenn Quellcode anderen Teilnehmern <strong>zu</strong>gänglich gemacht wird (bewusst<br />

oder durch Vernachlässigung eines gewissen Mindestmaßes an Datensicherheit), wird das betreffende<br />

Beispiel bei allen Beteiligten mit 0 Punkten bewertet, unabhängig davon, wer <strong>den</strong> Code ursprünglich<br />

erstellt hat. Ebenso ist es nicht <strong>zu</strong>lässig, Code aus dem Internet, aus Büchern oder aus anderen<br />

Quellen <strong>zu</strong> verwen<strong>den</strong>. Es erfolgt sowohl eine automatische als auch eine manuelle Überprüfung auf<br />

Plagiate.<br />

Da die abgegebenen Programme halbautomatisch getestet wer<strong>den</strong>, muss die Übergabe der Parameter mit<br />

Hilfe von entsprechen<strong>den</strong> Konfigurationsdateien genauso erfolgen wie bei <strong>den</strong> einzelnen Beispielen spezifiziert.<br />

Insbesondere ist eine interaktive Eingabe von Parametern nicht <strong>zu</strong>lässig. Sollte aufgrund von Änderungen<br />

am Konfigurationssystem die Ausführung der abgegebenen Dateien mit <strong>den</strong> Testdaten fehlschlagen,<br />

wird das Beispiel mit 0 Punkten bewertet. Die Konfigurationsdateien liegen im XML-Format vor, <strong>zu</strong> deren<br />

Auswertung steht Ihnen die Klasse Config <strong>zu</strong>r Verfügung. Die Verwendung der Klasse ist aus dem<br />

Programmgerüst ersichtlich.<br />

Jede Konfigurationsdatei enthält <strong>zu</strong>mindest einen Testfall und dessen Konfiguration. Es ist auch möglich,<br />

dass eine Konfigurationsdatei mehrere Testfälle enthält, um gemeinsame Parameter nicht mehrfach in verschie<strong>den</strong>en<br />

Dateien spezifizieren <strong>zu</strong> müssen. In manchen Konfigurationsdateien fin<strong>den</strong> sich auch einstellbare<br />

Parameter, die in Form eines select Feldes vorliegen. Diese sollen die Handhabung der Konfigurationsdateien<br />

erleichtern und ein einfaches Umschalten der Modi gewährleisten.<br />

Es steht Ihnen frei, z.B. <strong>zu</strong> Testzwecken eigene Erweiterungen <strong>zu</strong> implementieren. Stellen Sie jedoch sicher,<br />

dass solche Erweiterungen in Ihrem abgegebenen Code deaktiviert sind, damit ein Vergleich der<br />

abgegebenen Arbeiten mit unserer Referenzimplementierung möglich ist.<br />

Die Programmgerüste, die <strong>zu</strong>r Verfügung gestellt wer<strong>den</strong>, sind unmittelbar aus unserer Referenzimplementierung<br />

abgeleitet, indem nur jene Teile entfernt wur<strong>den</strong>, die dem Inhalt der Übung entsprechen. Die<br />

Verwendung dieser Gerüste ist nicht zwingend, aber Sie ersparen sich sehr viel Arbeit, wenn Sie davon<br />

Gebrauch machen.<br />

2 Steganographie („CV/task1a“)<br />

Der Begriff Steganographie steht für das Verbergen von digitalen Inhalten in Computerdateien. Audiound<br />

Bilddaten enthalten meist einen hohen Rauschanteil, der durch gezielt platzierte Informationen ersetzt<br />

wer<strong>den</strong> kann. Diese Veränderung fällt nicht auf und ermöglicht es, Nachrichten <strong>zu</strong> übertragen, ohne dass<br />

ein Dritter es überhaupt bemerkt.<br />

In diesem Übungsbeispiel soll ein verstecktes Bild im gegebenen Bild mittels Verschiebung einiger Bits<br />

und Zuordnung von Farbwerten aus einer Tabelle rekonstruiert wer<strong>den</strong>. Weiters soll der im Bild enthaltene<br />

Text segmentiert wer<strong>den</strong>, wofür eine Grauwertkonvertierung und zwei Schwellwerte benötigt wer<strong>den</strong>.<br />

2.1 Aufgabenstellung<br />

Rekonstruktion des versteckten Bildes (2 Punkte) Es gibt mehrere Möglichkeiten, Informationen in<br />

digitalen Bildern <strong>zu</strong> verstecken. Für die Kodierung des gegebenen Bildes wurde das LSB-Verfahren verwendet,<br />

bei dem eine bestimmte Anzahl der niederwertigsten Bits (bitshift) jedes Farbkanals für die<br />

2


Abbildung 1: Beispielhafte Rekonstruktion eines Pixels für 2 LSB (least significant bits) pro Farbkanal.<br />

Zusatzinformation verwendet wer<strong>den</strong>. Diese Bits wer<strong>den</strong> dann in der Reihenfolge B-G-R <strong>zu</strong>sammengesetzt<br />

und ergeben somit eine einzelne Zahl, <strong>den</strong> sogenannten Farbindex. Durch Verwendung der Farbtabelle (lookup<br />

table) kann jedem Index ein Farbwert <strong>zu</strong>geordnet wer<strong>den</strong>. Durch Wiederholung dieser Vorgehensweise<br />

für jedes Pixel entsteht ein Farbbild mit der Größe des gegebenen Bildes. Ein Beispiel ist in Abbildung 1<br />

<strong>zu</strong> sehen.<br />

Segmentierung mittels Schwellwert (2 Punkte) Aufbauend auf dem Resultat der vorherigen Aufgabenstellung<br />

soll nun mittels eines Schwellwert ein bestimmter Teil des Bildes segmentiert wer<strong>den</strong>. Konvertieren<br />

Sie <strong>zu</strong>nächst das Farbbild in ein Grauwertbild. Verwen<strong>den</strong> Sie da<strong>zu</strong> die allgemein übliche Gewichtung<br />

30% Rot, 59% Grün und 11% Blau. Wen<strong>den</strong> Sie dann die bei<strong>den</strong> Schwellwerte an, wobei unterer Schwellwert<br />

≤ gesuchter Bereich ≤ oberer Schwellwert ist.<br />

{ 255 lowerthreshold ≤ outputgray_img(x,y) ≤ upperthreshold<br />

outputmask_img(x,y) =<br />

0 else<br />

Das resultierende Binärbild (auch Maske genannt) sollte weiße Bildsegmente auf schwarzem Hintergrund<br />

zeigen. Diese sehr einfache Methode <strong>zu</strong>r Segmentierung kann natürlich nur angewandt wer<strong>den</strong>, wenn sich<br />

die gesuchte Region eindeutig vom Hintergrund abhebt (vergleiche Abbildung 2).<br />

(a) Eingabebild<br />

(b) Rekonstruiertes Bild<br />

(c) Grauwertbild<br />

(d) Maskenbild<br />

Abbildung 2: Eingabebild und Ergebnisbilder von „route66.xml“<br />

3


2.2 Ein- und Ausgabeparameter<br />

Die folgen<strong>den</strong> Parameter in der Konfigurationsdatei bestimmen das Verhalten des Programms (Anwendungsbeispiele<br />

fin<strong>den</strong> Sie in <strong>den</strong> Dateien „graz.xml“ und „route66.xml“):<br />

• Input-Bild: input<br />

• Output-Farbbild: outputrgb<br />

• Output-Grauwertbild: outputgray<br />

• Output-Maskenbild: outputmask<br />

• Tabelle <strong>zu</strong>r Farb<strong>zu</strong>weisung (LUT): colormap<br />

• Anzahl der verwendeten Bits (LSB) pro Farbkanal: bitshift<br />

• Unterer Schwellwert <strong>zu</strong>r Segmentierung: lowerthreshold<br />

• Oberer Schwellwert <strong>zu</strong>r Segmentierung: upperthreshold<br />

2.3 Programmgerüst<br />

Die folgende Funktionalität ist in dem vom ICG <strong>zu</strong>r Verfügung gestellten Programmgerüst bereits implementiert<br />

und muss von Ihnen nicht selbst programmiert wer<strong>den</strong>:<br />

• Die Konfigurationsdatei (XML) wird vom Programmgerüst gelesen.<br />

• Lesen des Eingabebildes.<br />

• Schreiben der Ausgabebilder.<br />

Sie müssen die in der Variable bitshift angegebenen LSB in eine passende Bitmaske konvertieren, um<br />

aus jedem Farbkanal die least significant bits auslesen <strong>zu</strong> können. Dann müssen Sie daraus indexed_color<br />

erstellen und dann mit Hilfe der indexed_color aus der colormap (LUT) die passende Farbe in das<br />

image_out schreiben. Anschließend erfolgt die Grauwertkonvertierung mit <strong>den</strong> gegebenen Gewichten<br />

der Farbkanäle in das Bild image_out_gray. Danach müssen Sie unter Verwendung der bei<strong>den</strong><br />

Schwellwerte lowerthreshold und upperthreshold das image_out_mask erstellen.<br />

Diese Aufgabe ist mit Hilfe von OpenCV 1 <strong>zu</strong> implementieren. Nutzen Sie die Funktionen, die Ihnen<br />

OpenCV <strong>zu</strong>r Verfügung stellt und achten Sie auf die unterschiedlichen Parameter!<br />

3 Steerable Filters („cv/task1b“)<br />

In diesem Beispiel sollen Sie einfache Filteroperationen eines Bildes mit verschie<strong>den</strong>en Faltungskernen<br />

implementieren. Das Ziel bei dieser Übung ist Ihnen die Faltung sowohl im Bild-, als auch im Frequenzbereich<br />

verständlich <strong>zu</strong> machen und ein Anwendungsbeispiel von steuerbaren Filtern auf<strong>zu</strong>zeigen.<br />

Steuerbare Filter kommen oft dann <strong>zu</strong>m Einsatz, wenn es darum geht die Filterantwort eines Faltungskernes<br />

in vielen verschie<strong>den</strong>en Orientierungen aus<strong>zu</strong>werten. Hier gibt es nun zwei mögliche Varianten dies<br />

durch<strong>zu</strong>führen: Die erste Variante ist das Bild mit verschie<strong>den</strong> orientierten Kernen <strong>zu</strong> falten, was bei vielen<br />

Orientierungen aber extrem rechenaufwändig wird. Die weitaus effektivere Variante ist das Bild mit nur<br />

wenigen Faltungskernen <strong>zu</strong> falten und aus diesen die Antworten der anderen Orientierungen <strong>zu</strong> interpolieren.<br />

Dies wird in Abbildung 3 veranschaulicht.<br />

1 http://opencv.willowgarage.com<br />

4


Abbildung 3: Blockdiagramm eines steuerbaren Filters. Das Bild wird mit ausgesuchten Orientierungen<br />

eines Filterkerns gefalten. Deren Antworten wer<strong>den</strong> über Koeffizienten linear kombiniert, um die Orientierung<br />

des synthetisierten Filters <strong>zu</strong> kontrollieren. [FA91]<br />

Somit kann man sich - insofern der Faltungskern gewisse Steuerbarkeitskriterien erfüllt (siehe [FA91]) -<br />

viele Faltungen sparen und alle möglichen Filterantworten über Linearkombination der Antworten der Basisfilter<br />

interpolieren.<br />

3.1 Convolution und Correlation<br />

Wenn man in der Computer Vision von einer Convolution oder Faltung spricht, so wird in der Praxis oftmals<br />

eine Correlation gemeint. Dies kommt daher, dass bei einer Convolution und einer Correlation dasselbe<br />

Ergebnis erzielt wird, wenn ein isotroper beziehungsweise ein rotationsinvarianter Faltungskern <strong>zu</strong>m<br />

Einsatz kommt. Sehen wir uns da<strong>zu</strong> kurz die mathematischen Definitionen der bei<strong>den</strong> Verfahren für <strong>den</strong><br />

zweidimensionalen Fall an, wobei F für <strong>den</strong> N × N großen Faltungskern und I(x, y) für einen Bildpunkt<br />

stehen und N stets ungerade sein muss:<br />

F ◦ I(x, y) =<br />

F ∗ I(x, y) =<br />

N−1<br />

2∑<br />

j=− N−1<br />

2<br />

N−1<br />

2∑<br />

j=− N−1<br />

2<br />

N−1<br />

2∑<br />

i=− N−1<br />

2<br />

N−1<br />

2∑<br />

i=− N−1<br />

2<br />

F (i, j)I(x + i, y + j) (1)<br />

F (i, j)I(x − i, y − j) (2)<br />

Formel 1 definiert die Correlation und Formel 2 die Convolution. Was uns hier nun auffallen sollte, sind<br />

die unterschiedlichen Vorzeichen beim Zugriff auf die Bildpunkte während die Faltungsmaske durchiteriert<br />

wird. Wo bei der Correlation stets auf <strong>den</strong> Pixelwert des Bildes direkt unter dem aktuellen Punkt in der<br />

Maske <strong>zu</strong>gegriffen wird, greift man bei der Convolution auf <strong>den</strong> um die x- und y-Achse gespiegelten Wert<br />

im Bild <strong>zu</strong>.<br />

Wichtig für diese Übung ist, dass wenn wir hier von einer Convolution oder Faltung sprechen stets auch<br />

eine solche meinen und keine Correlation! Als kleiner Hinweis sei hier gesagt, dass man durch einfaches<br />

Spiegeln des Faltungskernes um die x- und y-Achse mit einer Methode die normalerweise eine Correlation<br />

berechnet, eine Convolution berechnen kann.<br />

Verwen<strong>den</strong> wir <strong>den</strong> Faltungskern<br />

⎡<br />

a b<br />

⎤<br />

c<br />

F = ⎣d e f⎦ ,<br />

g h i<br />

so kann man anhand der folgen<strong>den</strong> Grafik 4 die Unterschiede der bei<strong>den</strong> Verfahren gut erkennen.<br />

5


(a) Correlation<br />

(b) Convolution<br />

Abbildung 4: In <strong>den</strong> bei<strong>den</strong> Bildern kann man unter Verwendung desselben Faltungskerns F die Unterschiede<br />

der bei<strong>den</strong> Verfahren Correlation und Convolution deutlich erkennen.<br />

3.2 Aufgaben<br />

Das Beispiel ist in mehrere Unteraufgaben gegliedert, wobei jede Unteraufgabe auf der vorherigen aufbaut.<br />

Der prinzipielle Bearbeitungsablauf besteht aus folgen<strong>den</strong> Schritten:<br />

• Einlesen und Konvertieren des Eingangsbildes<br />

• Berechnen der Faltungskerne G x und G y für die erste Ableitung des Gaußkerns in x- und y-Richtung<br />

• Faltung des Eingangsbildes mit G x im Bildbereich<br />

• Faltung des Eingangsbildes mit G y im Frequenzbereich<br />

• Interpolation der Filterantworten für verschie<strong>den</strong>e Orientierungen, Fin<strong>den</strong> der dominanten Orientierung<br />

via max pooling und Darstellung der dominanten Orientierung in sogenannter orientation<br />

map<br />

• Einzeichnen der dominanten Orientierung im Eingangsbild<br />

Einlesen und Konvertieren des Eingangsbildes Sie bekommen von uns stets Grauwertbilder für diese<br />

Aufgabe (siehe <strong>zu</strong>m Beispiel Abbildung 5). Das Einlesen des Bildes ist bereits erledigt. Für Sie gilt es<br />

nunmehr das Bild vom Grauwertebereich [min_gray_val,max_gray_val] linear auf Werte zwischen [0,1]<br />

<strong>zu</strong> konvertieren, so dass der gesamte Bereich [0,1] ausgenutzt wird.<br />

Hier hilfreiche OpenCV-Befehle:<br />

• cv::minMaxLoc(...)<br />

• cv::Mat::convertTo(...)<br />

Berechnen der Faltungskerne G x und G y für die erste Ableitung des Gaußkerns Die Gaußfunktion<br />

(siehe Abbildung 6(a)) ist definiert als<br />

G = e − 1 (x 2 +y 2 )<br />

2 σ 2 (3)<br />

6


Abbildung 5: Eingangsbild<br />

und somit ergeben sich die Ableitung G x (siehe 6(b)) in x-Richtung und die Ableitung G y (siehe 6(c)) in<br />

y-Richtung wie folgt:<br />

G x = − x σ 2 · 1 (x 2 +y 2 )<br />

e− 2 σ 2 (4)<br />

G y = − y σ 2 · 1 (x 2 +y 2 )<br />

e− 2 σ 2 (5)<br />

Verwen<strong>den</strong> Sie die Parameter σ = in_sigma und N = in_window_size und befüllen Sie die G x und<br />

G y jeweils N ×N großen Faltungskerne entsprechend Ihrer mathematischen Definition. Die Faltungskerne<br />

sollen in der Maske zentriert und somit mittelwertfrei sein. Abschließend normalisieren Sie je<strong>den</strong> der Kerne<br />

so, dass für <strong>zu</strong>m Beispiel für G x folgendes gilt:<br />

N−1<br />

2∑<br />

j=− N−1<br />

2<br />

N−1<br />

2∑<br />

i=− N−1<br />

2<br />

(6)<br />

|G x (i, j)| = 1 (7)<br />

Faltung des Eingangsbildes mit Gx im Bildbereich (2 Punkte) Nun falten Sie das Eingangsbild mit<br />

G x im Bildbereich. Achten Sie dabei darauf, dass Sie, wie weiter oben beschrieben, eine Convolution und<br />

keine Correlation durchführen!<br />

Da der Faltungskern an <strong>den</strong> Bildrändern um bis <strong>zu</strong> (N − 1)/2 Pixel über <strong>den</strong> Bildrand hinausragt ist eine<br />

Randbehandlung nötig, um die anfängliche Bildgröße bei<strong>zu</strong>behalten. Wiederholen Sie da<strong>zu</strong> die Randpixel<br />

dementsprechend oft, wie in Abbildung 7 skizziert.<br />

Abschließend speichern Sie das Ergebnis der Filterung unter out_x_derivative_filename. Bringen<br />

Sie da<strong>zu</strong> das Bild, das Sie speichern wieder linear auf einen Wertebereich zwischen [0,255], so dass<br />

auch hier der gesamte Bereich ausgenutzt wird. Achten Sie aber gleichzeitig darauf mit dem vorhergehen<strong>den</strong><br />

Ergebnis weiter<strong>zu</strong>rechnen!<br />

Weitere hilfreiche OpenCV-Befehle:<br />

• cv::flip(...)<br />

• cv::filter2D(...)<br />

7


(a) G<br />

(b) G x<br />

(c) G y<br />

Abbildung 6: In <strong>den</strong> 3 Bildern kann man <strong>den</strong> Gausskern und dessen x- und y-Ableitung sehen. Links jeweils<br />

die 3D-Ansicht und rechts die Betrachtung von oben.<br />

8


Abbildung 7: Randbehandlung durch Wiederholung der Randpixel: blau: Faltugnskern mit N = 5,<br />

schwarz: Bild, grau: (N − 1)/2 Pixel breiter Rand<br />

Abbildung 8: Eingangsbild gefaltet mit G x<br />

Faltung des Eingangsbildes mit Gy im Frequenzbereich (4 Punkte) Falten Sie das Eingangsbild mit<br />

G y im Frequenzbereich. Da<strong>zu</strong> müssen Sie das Bild sowie <strong>den</strong> Faltungskern <strong>zu</strong>erst vom Bild- mittels Fouriertransformation<br />

in <strong>den</strong> Frequenzbereich transformieren. Das Faltungstheorem 8 besagt, dass eine Faltung<br />

im Bildbereich einer Multiplikation im Frequenzbereich gleich kommt. Somit können Sie Bild und Kern<br />

nun punktweise miteinander multiplizieren und abschließend auf das Ergebnis die inverse Fouriertransformation<br />

anwen<strong>den</strong>.<br />

F{(i ∗ f)(x, y)} = I(x, y)F (x, y) (8)<br />

Bevor Sie das Eingangsbild jedoch in <strong>den</strong> Frequenzbereich transformieren, müssen Sie sich noch im Bildbereich<br />

um die Randbehandlung kümmern. Auch hier verwen<strong>den</strong> Sie wiederum die in Abbildung 7 beschriebene<br />

Art der Randbehandlung und wiederholen <strong>den</strong> äußeren Rand.<br />

Da die Fouriertransformation für Bildgrößen die einer Potenz von 2, oder dem Produkt von 2, 3 und 5 entsprechen<br />

effizient implementiert wer<strong>den</strong> kann, verwen<strong>den</strong> Sie bitte die Funktion cv::getOptimalDFTSize(...)<br />

<strong>zu</strong>m berechnen der optimalen Bildgröße. Anschließend erzeugen Sie sowohl für das randbehandelte Eingangsbild,<br />

als auch für <strong>den</strong> Faltungskern zwei dementsprechend große Bilder. Da diese nun größer sind,<br />

als die tatsächlichen Bilder, müssen Sie sowohl das randbehandelte Bild, als auch <strong>den</strong> Faltungskern in<br />

das linke obere Eck der vergrößerten Bilder kopieren (vergleiche Abbildung 9). Nun können beide Bilder<br />

fouriertransformiert wer<strong>den</strong>. Haben Sie beide Bilder im Frequenzbereich vorliegen, so können Sie die Faltung<br />

durch punktweise Multiplikation durchführen. Achten Sie darauf, dass durch die Transformation nun<br />

komplexe Zahlenwerte vorliegen!<br />

9


Abbildung 9: Einlesen des Eingangsbildes in ein größeres Bild, bei dem die Bildgröße eine effizientere<br />

Ausführung der Fouriertransformation erlaubt<br />

Lassen Sie sich nun <strong>den</strong> Amplitu<strong>den</strong>gang des komplexen Fourierspektrums mittels der mitgelieferten Funktion<br />

shiftdft(...) berechnen, die <strong>den</strong> Ursprung in die Bildmitte bringt und die 4 Quadranten richtig anordnet.<br />

Die Funktion shiftdft(...) besitzt als Argumente zwei Bilder, wobei das erste Bild ein 2-Kanalbild sein muss.<br />

Im Kanal 1 muss der Real- und im Kanal 2 der Imaginärteil Ihrer Multiplikation im Frequenzbereich stehen.<br />

Im zweiten Argument übergeben Sie ein leeres Bild derselben Größe mit nur 1 Kanal. In dieses wird<br />

das Ergebnis von shiftdft(...) geschrieben. Das Ergebnis ist in Abbildung 10 dargestellt.<br />

Abbildung 10: Fourierspektrum des Ergebnisses der Faltung dargestellt mit Hilfe von shiftdft(...)<br />

Speichern Sie das Ergebnisbild unter out_magnitude_filename ab. Vor dem Abspeichern des Bildes<br />

sollte dieses natürlich wieder auf <strong>den</strong> gesamten Wertebereich zwischen [0,255] linear aufskaliert wer<strong>den</strong>.<br />

Nach der Multiplikation im Frequenzbereich müssen Sie das Ergebnis mittels inverser Fouriertransformation<br />

wieder <strong>zu</strong>rück in <strong>den</strong> Bildbereich bringen. Achten Sie dabei sowohl auf die richtige Skalierung, als<br />

auch darauf, dass Ihr Ergebnis nicht complex ist! Weiters müssen Sie aus dem Ergebnis der inversen Transformation<br />

nun unter Anwendung der Randbehandlung wieder ein Bild richtiger Größe, nämlich derselben<br />

Größe, wie jene des Eingangsbildes, beginnend bei [N − 1, N − 1] ausschnei<strong>den</strong> (siehe Abbildung 11).<br />

10


Speichern Sie das gefilterte Ergebnisbild unter out_y_derivative_filename. Skalieren Sie das<br />

Bild, das Sie speichern wollen wiederum auf <strong>den</strong> Wertebereich [0,255] und achten Sie gleichzeitig darauf<br />

mit dem vorhergehen<strong>den</strong> Ergebnis weiter<strong>zu</strong>rechnen!<br />

Abbildung 11: Eingangsbild gefaltet mit G y<br />

Hinweis: Sollten Sie nicht in der Lage sein diesen Punkt aus<strong>zu</strong>programmieren, so falten Sie das Bild mit<br />

G y im Bildbereich, um fortfahren <strong>zu</strong> können. Sie bekommen dann aber für diese Teilaufgabe keine Punkte!<br />

Weitere hilfreiche OpenCV-Befehle:<br />

• cv::copyMakeBorder(...)<br />

• cv::Mat::copyTo(...)<br />

• cv::merge(...)<br />

• cv::dft(...)<br />

• cv::split(...)<br />

• cv::Rect(...)<br />

Interpolation der Filterantworten für verschie<strong>den</strong>e Orientierungen, Fin<strong>den</strong> der dominanten Orientierung<br />

via max pooling und Darstellung der dominanten Orientierung in sogenannter orientation<br />

map (4 Punkte) Da die erste Ableitung des Gausskerns die Steuerbarkeitskriterien erfüllt, kann mit nur<br />

2 Basisfiltern jede beliebige Orientierung der ersten Ableitung des Gausskerns G θ◦<br />

1 wie folgt interpoliert<br />

wer<strong>den</strong>:<br />

G θ◦<br />

1 = cos (θ)G 0◦<br />

1 + sin (θ)G 90◦<br />

1<br />

= cos (θ)G x + sin (θ)G y (9)<br />

Weiters kommt uns <strong>zu</strong> Gute, dass die Faltung eine lineare Operation darstellt. Somit kann auch jedes Bild,<br />

welches mit einem Kern bestimmter Orientierung gefalten wer<strong>den</strong> soll, direkt über die mit <strong>den</strong> 2 Basisfiltern<br />

bereits gefilterten Bilder interpoliert wer<strong>den</strong>. Das bedeutet für Sie, dass Sie nicht <strong>zu</strong>erst einen Filterkern<br />

von <strong>zu</strong>m Beispiel G 135◦<br />

1 erzeugen und dann das Bild mit diesem falten müssen, sondern, dass Sie das<br />

11


Ergebnis direkt aus <strong>den</strong> bereits mit G x und G y gefilterten Versionen des Bildes durch Linearkombinieren<br />

der bei<strong>den</strong> Bilder erhalten. Somit benötigen wir lediglich 2 Faltungen<br />

I x = G x ∗ I (10)<br />

I y = G y ∗ I (11)<br />

und können nun jedes Bild gefalten mit einem G 1 -Kern beliebiger Orientierung θ erzeugen aus<br />

I θ◦<br />

1 = cos (θ)I x + sin (θ)I y (12)<br />

Erzeugen Sie nun zwischen −90 ◦ ≤ θ < 90 ◦ in 1 ◦ -Schritten die Filterantworten für die jeweilige Orientierung<br />

des mit G θ◦<br />

1 gefaltenen Bildes. Nun machen Sie ein max pooling indem Sie sich für jedes Pixel sowohl<br />

die dominante Orientierung, als auch die Amplitude der <strong>zu</strong>gehörigen Filterantwort merken (Hinweis: Es<br />

ist egal, ob die Filterantwort positiv oder negativ ist. Es geht um <strong>den</strong> Absolutwert!). Abschließend konvertieren<br />

Sie die Amplitu<strong>den</strong> der Filterantworten linear auf einen Wertebereich zwischen [0,1]. Speicher<br />

Sie dieses Bild unter out_max_response_filename ab (verlgeiche Abbildung 12), nachdem Sie es<br />

wieder auf <strong>den</strong> vollen Wertebereich von [0,255] gebracht haben.<br />

Abbildung 12: Maximale Filterantwort pro Pixel<br />

Nun sollen Sie eine Orientation-Map generieren, indem Sie die Orientierung und die Amplitude jedes<br />

Pixels als HSV-Farbwert in ein RGB-Farbbild kodieren. Da<strong>zu</strong> sehen Sie <strong>den</strong> HSV-Farbraum in Abbildung<br />

13. Die Orientierungen zwischen −90 ◦ ≤ θ < 90 ◦ kodieren wir im HSV-Farbraum nun von 0 ◦ ≤ hue <<br />

180 ◦ und die Amplitude der Filterantwort von 0 ≤ saturation ≤ 1. Da wir <strong>den</strong> value-Wert nicht benötigen<br />

setzen wir diesen auf value = 1.<br />

Gegeben sei eine Farbe mit hue H ∈ [0 ◦ , 360 ◦ ) und saturation S ∈ [0, 1]. Um nun ein RGB-Bild <strong>zu</strong><br />

generieren wen<strong>den</strong> Sie folgende HSV-RGB-Farbkonvertierung nach <strong>den</strong> Formeln 13, 14, 15 und 16 an:<br />

(K, L, M) =<br />

H ′ H<br />

=<br />

60<br />

(13)<br />

◦<br />

X = S · (1 − |mod2 (H ′ , 2) − 1|) (14)<br />

⎧<br />

⎪⎨<br />

⎪⎩<br />

(S, X, 0) if 0 ≤ H ′ < 1<br />

(X, S, 0) if 1 ≤ H ′ < 2<br />

(0, S, X) if 2 ≤ H ′ < 3<br />

(0, X, S) if 3 ≤ H ′ < 4<br />

(X, 0, S) if 4 ≤ H ′ < 5<br />

(S, 0, X) if 5 ≤ H ′ < 6<br />

(R, G, B) = (K + (1 − S), L + (1 − S), M + (1 − S)) (16)<br />

(15)<br />

12


(a) HSV Zylinder<br />

(b) HSV-RGB<br />

Abbildung 13: In <strong>den</strong> bei<strong>den</strong> Abbildungen sind sowohl der Aufbau des HSV-Farbraumes, als auch die<br />

Umwandlung HSV-RGB dargestellt (Bildquelle: http://en.wikipedia.org)<br />

Speichern Sie die farbige Orientation-Map nun unter out_orientation_map_filename ab. Achten<br />

Sie bitte darauf, dass bei OpenCV die Farbkanalreihenfolge nicht RGB, sondern BGR ist (vergleiche<br />

Abbildung 14)!<br />

Abbildung 14: Orientation-Map: maximale Orientierung und Stärke sind HSV-farbkodiert<br />

Einzeichnen der dominanten Orientierung im Eingangsbild (2 Punkte) In diesem Punkt gilt es nun<br />

anhand der bereits berechneten, lokal dominanten Orientierungen eine einzige Hauptorientierung für das<br />

Bild <strong>zu</strong> berechnen. Dies geschieht indem Sie ein gewichtetes Histogramm über alle Orientierungen generieren,<br />

welches alle Filterantworten der jeweiligen lokal dominanten Orientierung aufsummiert. Anschließend<br />

Suchen Sie ausgehend von −90 ◦ das erste Maximum des Histogramms und haben somit die Hauptrichtung<br />

gefun<strong>den</strong>.<br />

Erzeugen Sie sich die gefilterte Variante des Eingangsbildes entlang der Hauptrichtung über eine Linearkombination<br />

der mit G x und G y gefaltenen Bilder, wie in Formel 12 beschrieben. Nun erstellen Sie sich<br />

ein Binärbild indem Sie die gefilterte Version mit in_threshold schwellwerten (vergleiche Abbildung<br />

15). Speichern Sie das Binärbild unter out_threshold_filename ab!<br />

Abschließend erzeugen Sie ein 3-Kanalbild, welches das Eingangsbild (Grauwertbild) enthält, und markieren<br />

in diesem jene Pixel in rot ( (R,G,B) = (255,0,0) ), die im Schwellwertbild angeschlagen haben, aber<br />

maximal um ±10 ◦ von der dominanten Hauptrichtung abweichen (vergleiche Abbildung 16). Speichern<br />

Sie das Ergebnisbild unter out_result_filename.<br />

13


Abbildung 15: Binäres Schwellwertbild durch Faltung des Eingangsbildes entlang der dominanten Orientierung<br />

und anschließendem Schwellwerten<br />

Abbildung 16: Endergebnis: Markieren der Hauptrichtung im Eingangsbild<br />

Weitere hilfreiche OpenCV-Befehle:<br />

• cv::threshold(...)<br />

3.3 Ein- und Ausgabeparameter<br />

Die folgen<strong>den</strong> Parameter in der Konfigurationsdatei bestimmen das Verhalten des Programms (Anwendungsbeispiele<br />

fin<strong>den</strong> Sie in der Datei „test.xml“):<br />

• Eingabe - Bild: in_filename<br />

• Parameter - Faltungskern N × N mit N: in_window_size<br />

• Parameter - Faltungskern σ: in_sigma<br />

• Parameter - Threshold t: in_threshold<br />

14


• Ausgabe - Ergebnisbild der Faltung mit G x : out_x_derivative_filename<br />

• Ausgabe - Amplitu<strong>den</strong>gang der Faltung mit G y im Frequenzbereich: out_magnitude_filename<br />

• Ausgabe - Ergebnisbild der Faltung mit G y : out_y_derivative_filename<br />

• Ausgabe - Orientation Map: out_orientation_map_filename<br />

• Ausgabe - Binäres Thresholdbild: out_threshold_filename<br />

• Ausgabe - Ergebnisbild: out_result_filename<br />

3.4 Programmgerüst<br />

Die folgende Funktionalität ist in dem vom ICG <strong>zu</strong>r Verfügung gestellten Programmgerüst bereits implementiert<br />

und muss von Ihnen nicht selbst programmiert wer<strong>den</strong>:<br />

• Die Konfigurationsdatei (XML) wird vom Programmgerüst gelesen.<br />

• Lesen des Eingabebildes<br />

• Für je<strong>den</strong> Unterpunkt der Aufgabenstellung wer<strong>den</strong> in main() entsprechende Bilder mit <strong>den</strong> in der<br />

Konfigurationsdatei spezifizierten Namen abgespeichert. Zur Zeit wer<strong>den</strong> hier nur leere Bilder abgespeichert.<br />

Diese sind von Ihnen durch Ihre Ergebnisbilder <strong>zu</strong> ersetzen. Diese Ausgabebilder wer<strong>den</strong><br />

später verwendet, um die einzelnen Teilaufgaben <strong>zu</strong> bewerten. Sollten Sie einen Punkt nicht ausprogrammieren,<br />

so achten Sie bitte darauf, dass trotzdem ein Bild entsprechender Größe abgespeichert<br />

wird, da diese <strong>zu</strong>m automatischen Testen benötigt wer<strong>den</strong>.<br />

• In main.cpp ist eine Funktion shiftdft() <strong>zu</strong>m korrekten Darstellen des Amplitu<strong>den</strong>gangs im<br />

Frequenzbereich ausimplementiert. Sollten Sie diese Methode <strong>zu</strong> Testzwecken verändern, so sollten<br />

Sie diese vor Abgabe unbedingt in Ihre Ursprungsform <strong>zu</strong>rückbringen, da sonst das automatische<br />

Testen beeinträchtigt wer<strong>den</strong> kann.<br />

Auch diese Aufgabe ist mit Hilfe von OpenCV 2 <strong>zu</strong> implementieren. Nutzen Sie die Funktionen, die<br />

Ihnen OpenCV <strong>zu</strong>r Verfügung stellt und achten Sie auf die unterschiedlichen Parameter!<br />

2 http://opencv.willowgarage.com<br />

15


(a) TU Logo<br />

(b) Die<br />

Abbildung 17: Ray-Casting Beispiele: Das TU Graz Logo bestehend aus Dreiecken (Abbildung 17(a)), ein<br />

Würfel bestehend aus Dreiecken und Kugeln (Abbildung 17(b)).<br />

4 Ray-Casting („CG/task1a“)<br />

Ray-Casting ist ein bildbasiertes Darstellungssverfahren für 3 dimensionale Objekte. Das Verfahren bestimmt<br />

pixelweise die Sichtbarkeit der Szene-Elemente und sendet dafür für je<strong>den</strong> Pixel ein Blickstrahl<br />

oder Ray vom Ursprungspunkt („Augpunkt“/„Origin“) in die Szene. Ein solcher Strahl entspricht einer<br />

gera<strong>den</strong> Linie vom Ursprungspunkt durch einen Pixel in die Szene. Wie in Abbildung 18 illustriert, wer<strong>den</strong><br />

für je<strong>den</strong> Strahl alle Schnittpunkte mit <strong>den</strong> Objekten der Szene berechnet. Zur Berechnung der Pixelfarbe<br />

wird im Anschluss jener Schnittpunkt herangezogen, der dem Betrachter am nächsten liegt. Ray-Casting<br />

wird als Spezifallfall des „Ray-Tracing“ Verfahrens angesehen. Im Vergleich <strong>zu</strong>m klassischen Ray-Tracer<br />

verzichtet ein Ray-Caster auf Reflexionen und Transparenz [HB04][Kapitel 10.11].<br />

Abbildung 18: Erster Schnittpunkt wird <strong>zu</strong>r Berechnung der Pixelfarbe benutzt; die verdeckten Objekte<br />

wer<strong>den</strong> am Bildschirm nicht gezeigt.<br />

4.1 Aufgaben<br />

In dieser Übung ist ein Sichtstrahl vom Augpunkt durch je<strong>den</strong> Bildpunkt einer Bildebene („View-Plane“)<br />

<strong>zu</strong> erstellen. Danach wer<strong>den</strong> alle Schnittpunkte mit <strong>den</strong> Dreiecken und Kugeln der Szene berechnet und derjenige<br />

ausgewählt der dem Betrachter am nächsten liegt. Um eine 3 dimensionale Darstellung <strong>zu</strong> erzielen,<br />

wer<strong>den</strong> die Farbwerte an <strong>den</strong> Schnittpunkten im Anschluss schattiert. Zum Testen der Strahlenberechnung<br />

sind im Framework die Berechnungen <strong>zu</strong>r Schattierung sowie die Schnittberechnung für Kugeln bereits<br />

implementiert. Zur Fertigstellung der Aufgabe ist daher nur noch die Erstellung und das Aussen<strong>den</strong> der<br />

Sichtstrahlen sowie die Schnittberechnungen der Strahlen mit <strong>den</strong> Dreiecken der Szenen <strong>zu</strong> implementieren.<br />

16


Abbildung 19: Augpunkt und Sichtstrahlen durch die Pixel an <strong>den</strong> Ecken der Bildebene. Pixelkoordinaten<br />

sind in grün, View Plane Koordinaten in weiß und schwarz eingezeichnet. Die Entfernung zwischen View<br />

Plane und Augpunkt ist f<br />

.<br />

Erstellen und Aussen<strong>den</strong> der Rays (3 Punkte) Wie in Abbildung 19 dargestellt, hat die Bildebene<br />

(„View Plane“) eine fixierte Ausdehnung im World Space, die unabhängig von der Anzahl der Pixel ist.<br />

Dies ist nötig, um <strong>zu</strong> verhindern, dass z.B. ein Bild mit geringerer Auflösung weniger von der Szene darstellt.<br />

Die Position (−1/aspect, 1) entspricht der linken oberen Ecke der View Plane, und damit auch der<br />

linken oberen Ecke des Pixels (0, 0). Der Parameter aspect sei hier das Verhältnis Hoehe/Breite des<br />

Ausgabebildes und ist durch dessen Dimensionen bereits vorgegeben. Ihre Aufgabe ist es, in der Methode<br />

calculateImage der Klasse RayCaster einen Strahl <strong>zu</strong> erstellen, der vom Augpunkt durch <strong>den</strong><br />

Mittelpunkt des <strong>zu</strong> untersuchen<strong>den</strong> Pixels läuft.<br />

Ein n-dimensionaler Vektorraum kann durch n Einheitsvektoren, die <strong>zu</strong>einander normal stehen, beschrieben<br />

wer<strong>den</strong>. Im Fall des Ray-Castings handelt es sich um einen dreidimensionalen Raum, der üblicherweise<br />

durch die drei euklidischen Einheitsvektoren (oder auch „Achsen“) x, y, z beschrieben wird. Das verwendete<br />

Koordinatensystem sieht folgende Richtungen vor: x = Horizontale, y = V ertikale, z = T iefe.<br />

Man unterscheidet hier zwischen Welt- und Sichtkoordinatensystem - „World Space“ und „View Space“.<br />

Objekte wer<strong>den</strong> in Weltkoordinaten platziert und durch die kanonischen Einheitsvektoren beschrieben. Der<br />

Augpunkt („Origin“) und somit die Kamera kann allerdings an einer beliebigen Position platziert sein und<br />

eine beliebige Ausrichtung haben. Dadurch können die Koordinatenachsen der Kamera ungleich <strong>den</strong>en des<br />

Weltkoordinatensystems wer<strong>den</strong>. Beispielsweise ist der z-Vektor der Kamera in Abbildung 20 ungleich<br />

dem z-Vektor des Weltkoordinatensystems<br />

⎛<br />

SightVector = ⎝<br />

2<br />

−1<br />

0<br />

⎞<br />

⎛<br />

⎠, normalisiert ⎝<br />

⎞ 2√<br />

5<br />

√−1<br />

⎠<br />

5<br />

0<br />

Da die Kameraposition in World Space Koordinaten vorliegt, lassen sich mit <strong>den</strong> transformierten Einheitsvektoren<br />

alle Strahlen durch die einzelnen Pixel in Weltkoordinaten beschreiben. Die benötigten Parameter<br />

wer<strong>den</strong> im Programm übergeben wobei die euklidischen Einheitsvektoren bereits berechnet sind. Sie müssen<br />

also nicht mehr verändert, sondern nur für die Erstellung der Sichtstrahlen benutzt wer<strong>den</strong>.<br />

17


Abbildung 20: Achsen des Worldspace (x, y, z) und z-Achse aus Kamerasicht (SightV ector)<br />

Es existiert bereits eine verschachtelte for-Schleife, die für Sie die Pixel des Ausgabebildes durchläuft.<br />

Die aktuelle Pixelposition wird durch das Paar (pixel_x, pixel_y) beschrieben. Konvertieren Sie also<br />

die horizontale Pixelposition pixel_x, im Bereich zwischen 0 und dim_x, <strong>zu</strong> einem horizontalen Abstand<br />

viewplane x mit einem Wert zwischen −1/aspect und 1/aspect, und speichern Sie das Ergebnis als<br />

Fließkommazahl. Die vertikale Pixelposition pixel_y soll <strong>zu</strong> einem Wert viewplane y zwischen 1 und −1<br />

konvertiert wer<strong>den</strong>. Beachten Sie bei bei<strong>den</strong>, dass der Strahl durch <strong>den</strong> Mittelpunkt der Pixel geschossen<br />

wer<strong>den</strong> soll!<br />

Wenn sie diese Werte viewplane x und viewplane y für <strong>den</strong> aktuellen Pixel berechnet haben, muss nur<br />

mehr der Strahl erstellt wer<strong>den</strong>. Verwen<strong>den</strong> sie dafür eine float3 und die nachfolgende Formel<br />

ray = viewplane x · x + viewplane y · y + f_z (17)<br />

Das Ergebnis muss noch normalisiert wer<strong>den</strong>, da die verwendeten Formeln <strong>zu</strong>r Schnittberechnung sonst<br />

ungültig sind. Rufen Sie danach die Methode shootRay mit der Startposition camera.eye, dem berechneten<br />

Strahl, der Objektliste shapes, der Lichtquelle light und dem Farbwert pixel_color auf.<br />

Wenn die Methode true retourniert, wurde ein Objekt getroffen und dessen Farbwert ist in pixel_color<br />

gespeichert. Sie können daraufhin einen Pixelwert R8G8B8A8 mit der berechneten pixel_color erstellen.<br />

Die Pixel können mit output_image_(pixel_x, pixel_y) addressiert wer<strong>den</strong>.<br />

Schnitt mit Dreiecken (3 Punkte) Überschreiben Sie die Methode findIntersectionPoints der<br />

Klasse Triangle, um die Kontrolle der Schnittpunkte für Dreiecke <strong>zu</strong> aktivieren. Als erstes muss der<br />

Schnittpunkt mit der Ebene gefun<strong>den</strong> wer<strong>den</strong>, die von dem <strong>zu</strong> testen<strong>den</strong> Dreieck aufgespannt wird. Benutzen<br />

Sie da<strong>zu</strong> die Methode MathUtils::rayPlaneIntersect. Als Parameter müssen der Startpunkt<br />

und der Vektor des Rays, sowie die Instanzattribute face_.plane_D und face_.normal_vector<br />

übergeben wer<strong>den</strong>. Bei einem normalisierten Ray ist der Returnwert s der Funktion die Distanz zwischen<br />

Schnittpunkt auf der Ebene und Startpunkt. Sollte diese Distanz


Abbildung 21: Problematik der Unterscheidung bei Punkten zwischen Inliern und Outlieren<br />

Nun muss eine Technik angewandt wer<strong>den</strong>, um die Inlier von <strong>den</strong> Outliern (die Punkte innerhalb von <strong>den</strong>en<br />

au¨sserhalb des Dreiecks) <strong>zu</strong> unterschei<strong>den</strong>. Dafür existieren verschie<strong>den</strong>ste Algorithmen mit unterschiedlicher<br />

Rechenzeit im dreidimensionalen Raum. Von Ihnen soll ein Algorithmus umgesetzt wer<strong>den</strong> der die<br />

Eigenschaften von Kreuzprodukt und Normalvektoren ausnutzt.<br />

(a) (0, 1, 0) × (1, 0, 0) (b) (1, 0, 0) × (0, 1, 0)<br />

Abbildung 22: Die Illustrationen in (a) und (b) zeigen die unterschiedlichen Ergebnisse für Normalvektoren,<br />

abhängig von Reihenfolge und Orientierung der verwendeten Vektoren im Kreuzprodukt<br />

Bei dieser Methode wird überprüft, ob der Punkt P auf der gleichen Seite einer Kante (zwischen zwei<br />

Punkten des Dreiecks) liegt wie der verbleibende dritte Punkt. Dies wird für alle Kanten überprüft. Anfangs<br />

wird der Normalvektor n des Dreiecks durch −→ −→<br />

−→<br />

AB × AC gebildet. Sei nun AP der Vektor von Punkt A <strong>zu</strong>m<br />

gefun<strong>den</strong> Schnittpunkt Punkt P . Der Normalvektor n a sei nun das Ergebnis von −→ −→ AB × AP . Ist nun das<br />

Skalarprodukt 〈n a , n〉 >= 0, liegt P auf der gleichen Seite von −→ AB wie C und der Test für diese Kante war<br />

erfolgreich.<br />

Der Vektor n wurde für Sie bereits vorberechnet und ist als Instanzattribut face_.normal_vector<br />

verfügbar. Es gilt also für jede Kante x <strong>den</strong> Normalvektor n x <strong>zu</strong> berechnen. Die Punkte eines Dreiecks<br />

befin<strong>den</strong> sich in dem Instanzarray vertices_. Die Berechnung des Normalvektors n x soll stets mit<br />

n x = (vertices_[x + 1] − vertices_[x]) × (P − vertices_[x]) (19)<br />

durchgeführt wer<strong>den</strong>. Berücksichtigen Sie dabei auch die Kante vertices_[0] − vertices_[2].<br />

War der Test für alle Seiten erfolgreich, wird nur mehr die IntersectionInfo geupdated.<br />

Fin<strong>den</strong> des ersten Schnittpunktes Die schnellstmögliche Methode <strong>den</strong> relevanten Schnittpunkt <strong>zu</strong> erhalten,<br />

besteht darin schon während der Schnittpunktberechnung der Objekte die Ergebnisse mit <strong>den</strong> bisherigen<br />

Schnittpunkten <strong>zu</strong> vergleichen. Das Struct IntersectionInfo soll immer <strong>den</strong> Schnittpunkt<br />

19


enthalten, welcher der Kamera momentan am nächsten ist, solange bis alle Objekte durchlaufen wur<strong>den</strong>.<br />

Das Struct enthält folgende Attribute, die von Ihnen über die Methode updateIntersectionInfo<br />

bei jedem relevanten Schnittpunkt aktualisiert wer<strong>den</strong> sollen:<br />

• found: Wird automatisch true wenn die Instanzmethode updateIntersectionInfo aufgerufen<br />

wird.<br />

• min_dist: Die Entfernung des Schnittpunktes von der Kamera. Da der Strahl normalisiert ist,<br />

entspricht s in <strong>den</strong> oberen Formeln direkt der Distanz von der Kamera.<br />

• intersection: Der näheste Schnittpunkt in World Space Koordinaten.<br />

• normal_vector: Der Normalvektor der Oberfläche an der Stelle des Schnittpunktes wird für die<br />

Beleuchtung benötigt. Aus <strong>den</strong> Algorithmen sollte hervorgehen, wie Sie diesen beim Schnittpunkttest<br />

erhalten können.<br />

• color: Der Farbwert des Objektes. Ableitungen der Klasse Shape enthalten <strong>den</strong> Farbwert als<br />

Instanzattribut color_.<br />

4.2 Vektorklasse<br />

Für die schnelle Berechnung von Vektorergebnissen wurde eine eigene Vektorklasse erstellt. Es existieren<br />

verschie<strong>den</strong>e Templates, von Ihnen soll aber nur float3 verwendet wer<strong>den</strong>. Verwen<strong>den</strong> Sie diese Klasse<br />

für Punkte und Vektoren im dreidimensionalen Raum. Die Klasse besitzt die Attribute x,y,z, welche die<br />

Bewegung des Vektors/Position des Punktes darstellen. Eine float3 kann normalisiert wer<strong>den</strong> durch die<br />

Instanzmethode VEKTORNAME.normalize. Alternativ erhalten Sie mit normalize(float3&) eine<br />

normalisierte Kopie eines Vektors als Rückgabewert, ohne <strong>den</strong> Originalvektor <strong>zu</strong> verändern.<br />

Um das Skalarprodukt zweier Vektoren in Form einer Fließkommazahl <strong>zu</strong> erhalten, verwen<strong>den</strong> Sie<br />

dot(float3&, float3&). Für das Ergebnis des Kreuzprodukts in Form einer float3, verwen<strong>den</strong><br />

Sie cross(float3&, float3&). Die Operatoren + und - sind überschrieben, sodass z.B. der Vektor<br />

zwischen <strong>den</strong> bei<strong>den</strong> float3-Punkten A und B mit B − A berechnet wer<strong>den</strong> kann. Das Ergebnis ist dann<br />

wiederum eine float3. Die Klassenheader befin<strong>den</strong> sich <strong>zu</strong>r Einsicht in „CG/common/vector.h“.<br />

4.3 Ein- und Ausgabe<br />

Alle benötigten Paramter wer<strong>den</strong> aus einer Konfigurationsdatei (deren Name als einziges Argument beim<br />

Programmstart angegeben wird) gelesen. Da Ray-Casting zeitlich aufwändig ist, wer<strong>den</strong> während dem<br />

Programm Statusmeldungen ausgegeben. Das Ausgabebild befindet sich nach erfolgreichem Been<strong>den</strong> in<br />

„output/TESTCASENAME.png“.<br />

4.4 Programmgerüst<br />

Das <strong>zu</strong>r Verfügung gestellte Programmgerüst implementiert bereits die komplette Infrastruktur (Konfigurationsdatei<br />

lesen und interpretieren, Ausgabebild speichern, Beleuchtung und Schnittpunktetests für Kugeln)<br />

Für die Implementierung wichtige Parameter sind:<br />

• Camera: Enthält die Parameter für die Berechnung der Einheitsvektoren und die Position eye.<br />

• Surface: Das Surface output_image_ ist ein Attribut der Klasse RayCaster und enthält die<br />

Farbwerte für <strong>den</strong> Output. Pixel können über output_image_(pixel_x, pixel_y) addressiert<br />

wer<strong>den</strong> und Farbwerte im Format R8G8B8A8 <strong>zu</strong>gewiesen bekommen. Benutzen Sie dafür <strong>den</strong><br />

Konstruktor R8G8B8A8(float3&) und übergeben Sie die berechnete pixel_color.<br />

Für die Vektorberechnungen soll ausschließlich die Klasse float3 verwendet wer<strong>den</strong>, für Kommazahlen<br />

ausschließlich float.<br />

20


Wichtige Metho<strong>den</strong><br />

• calculateImage: Zu fin<strong>den</strong> in der Datei „RayCaster.cpp“. Hier muss der Strahl für je<strong>den</strong><br />

einzelnen Pixel berechnet, abgeschossen und das Ausgabebild bei einem Treffer mit der Ergebnisfarbe<br />

geupdated wer<strong>den</strong>.<br />

• findIntersectionPoints: Muss in dem File „Triangle.cpp“ ergänzt wer<strong>den</strong>. In dieser<br />

Methode soll der angegebene Algorithmus umgesetzt wer<strong>den</strong>, wobei darauf <strong>zu</strong> achten ist, dass nur<br />

relevante Punkte (der Kamera nähere) in das Struct IntersectionInfo geschrieben wer<strong>den</strong>.<br />

Verwen<strong>den</strong> sie dafür die Instanzmethode updateIntersectionInfo.<br />

• shootRay: Zu fin<strong>den</strong> in der Datei „RayCaster.cpp“. Hier wer<strong>den</strong> bereits alle Objekte der Szene<br />

durchlaufen und auf Schnittpunkte überprüft. Zu ergänzen ist hier nichts.<br />

4.5 Q&A<br />

Q: Eine float3 kann ein Punkt und ein Vektor sein? Wie geht das?<br />

A: Alles, was nötig ist um einen Vektor <strong>zu</strong> beschreiben sind drei Richtungswerte. Ein Punkt ist nichts anderes,<br />

als der Vektor der vom Punkt (0, 0, 0) aus <strong>zu</strong>rückgelegt wer<strong>den</strong> muss um ihn <strong>zu</strong> erreichen.<br />

Q: Wie funktioniert die View Plane Transformation? Was bedeutet „durch <strong>den</strong> Mittelpunkt der Pixel“?<br />

A: Zeichnen Sie sich eine 3x3 View Plane auf Papier auf und markieren sie die Ecken so wie in Abbildung<br />

19. Be<strong>den</strong>ken Sie, dass die linke obere Ecke der View Plane (-1/aspect, 1) auch die linke obere Ecke des<br />

ersten Pixels (0, 0) darstellt. Markieren Sie nun die Mittelpunkte der Pixel und überlegen Sie sich, welche<br />

Anpassungen der Koordinaten nötig sind, um diese Mittelpunkte <strong>zu</strong> erreichen. Ein Hinweis: die Werte<br />

viewplane x und viewplane y können niemals <strong>den</strong> Wert der jeweiligen View Plane Ausdehnung haben, da<br />

diese ja <strong>den</strong> linken/oberen Rand des ersten bzw. rechten/unteren Rand des letzten Pixels anvisieren wür<strong>den</strong>.<br />

Q: Welche Tests sollen in welcher Reihenfolge gemacht wer<strong>den</strong>?<br />

A: Folgende Aufteilung wäre vom Ablauf her <strong>zu</strong> empfehlen:<br />

a) sphere.xml, colorspheres.xml: Testen das Aussen<strong>den</strong> der Strahlen mit bereits implementierten Kugeln.<br />

b) screen.xml, aspect.xml, depth.xml, cube.xml, tu.xml: Testen die Schnittpunktfunktion von Dreiecken.<br />

c) triforce.xml, die.xml, solar.xml: Testen die Anwendung von Kugeln und Dreiecken in Kombination.<br />

d) Alles übrige...: Eignet sich um Ihre Implementierung auf kleinere Schwächen <strong>zu</strong> prüfen.<br />

Q: Ist das <strong>den</strong>n alles schaffbar?<br />

A: Es wurde hier sehr detailliert auf die einzelnen Schritte eingegangen, um <strong>zu</strong> verdeutlichen warum die<br />

jeweiligen Berechnungen benötigt und die gegebenen Formeln verwendet wer<strong>den</strong>. Ein Renderingverfahren<br />

ist natürlich mit viel Verständnisarbeit verbun<strong>den</strong>, beginnen Sie also früh genug. Tatsächlich kommt die<br />

Referenzlösung aber mit 20 LOC (Lines of Code) aus.<br />

21


5 Rasterization („CG/task1b“)<br />

Unter Rasterisierung versteht man <strong>den</strong> Prozess bei dem graphische Primitive (Linien, Polygone, etc.) in eine<br />

Rastergrafik gewandelt wer<strong>den</strong>. Rasterizer bil<strong>den</strong> die Grundlage für einen Großteil aller heutigen <strong>Computergrafik</strong>systeme,<br />

vor allem im Bereich der Echtzeitgrafik. Zweck dieser Übung ist es mit <strong>den</strong> Grundlagen<br />

der Rasterisierung sowie dem Konzept einer Grafikpipeline und damit <strong>den</strong> wesentlichen Abläufen auf<br />

dem Weg von einer dreidimensionalen Szenenbeschreibung <strong>zu</strong> einem zweidimensionalen Abbild der Szene<br />

vertraut <strong>zu</strong> wer<strong>den</strong>. Da<strong>zu</strong> sollen die einzelnen Schritte einer vereinfachten Grafikpipeline implementiert<br />

wer<strong>den</strong>.<br />

Der <strong>zu</strong> implementierende Rasterizer soll auf Objekte, die aus Dreiecken <strong>zu</strong>sammengefügt sind anwendbar<br />

sein. Dreiecke verfügen über eine Reihe wünschenswerter Eigenschaften die sie <strong>zu</strong>m idealen Kandidaten<br />

für ein graphisches Primitiv machen, <strong>zu</strong>m Beispiel:<br />

• Dreiecke sind als einfachste zweidimensionale Form sehr simpel und effizient <strong>zu</strong> Rasterisieren.<br />

• Jedes komplexere Polygon lässt sich aus Dreiecken <strong>zu</strong>sammensetzen und auch beliebige räumliche<br />

Flächen können über Dreiecksnetze approximiert wer<strong>den</strong>.<br />

• Ein Dreieck bleibt auch unter einer perspektivischen Projektion immer ein Dreieck (außer in Grenzfällen<br />

wo ein Dreieck <strong>zu</strong>r Linie wer<strong>den</strong> kann).<br />

Aus all diesen Grün<strong>den</strong> sind Dreiecke die wichtigste Art von Primitiven in der <strong>Computergrafik</strong>. Praktisch<br />

alle Rasterizer heut<strong>zu</strong>tage arbeiten auf Basis von Dreiecken und auch der im Rahmen dieser Übung entwickelte<br />

soll hier keine Ausnahmen sein.<br />

Eine 3D Szene kann durch eine Liste von Dreiecken (gegeben durch die Positionen ihrer Vertices im Raum),<br />

sowie einigen Kameraparametern (Position, Ausrichtung, etc.) beschrieben wer<strong>den</strong>. Vor der Rasterisierung<br />

müssen die Dreiecke einer Reihe von Koordinatentransformationen unterzogen wer<strong>den</strong> um von <strong>den</strong> 3D<br />

Positionen <strong>zu</strong> <strong>den</strong> 2D Positionen an <strong>den</strong>en ein Dreieck schlussendlich ins Ausgabebild gezeichnet wer<strong>den</strong><br />

soll <strong>zu</strong> gelangen. Abschießend ist noch eine Überprüfung der Sichtbarkeit konkreter Pixel notwendig.<br />

5.1 Aufgaben<br />

Die Übung setzt sich aus <strong>den</strong> folgen<strong>den</strong> Schritten <strong>zu</strong>sammen:<br />

Einlesen der Dreieckslisten Die Dreieckslisten und alle anderen benötigten Paramter wer<strong>den</strong> aus einer<br />

Konfigurationsdatei (deren Name als einziges Argument beim Programmstart angegeben wird) gelesen.<br />

Das Format ist i<strong>den</strong>tisch <strong>zu</strong> dem bereits in „CG/task1a“ verwendeten und kann anhand der bereitgestellten<br />

Testcases nachvollzogen wer<strong>den</strong>.<br />

Vertex Tansformation (2 Punkte) Durch homogene Koordinaten lassen sich alle affinen Transformationen<br />

sowie eine perspektivische Projektion über Matrizen beschreiben. Die Position und Ausrichtung der<br />

virtuellen Kamera ist gegeben durch die Kameraposition eye, <strong>den</strong> Punkt auf <strong>den</strong> die Kamera gerichtet ist<br />

lookat und einen Richtungsvektor up, der die Rotation der Kamera um die Blickrichtung festlegt. Wie in<br />

Abbildung 23 dargestellt lassen sich aus diesen Parametern sehr einfach die Basisvektoren des Viewspace<br />

berechnen:<br />

cz =<br />

lookat − eye<br />

‖lookat − eye‖<br />

cx =<br />

up × cz<br />

‖up × cz‖<br />

cy = cz × cx (20)<br />

22


up<br />

cy<br />

y<br />

eye<br />

cx<br />

cz<br />

z<br />

lookat<br />

Abbildung 23: Kameramodell<br />

x<br />

Man beachte dass in dieser Übung ein linkshändiges Koordinatensystem verwendet wird. Sind die Basisvektoren<br />

bestimmt ergibt sich die Viewmatrix nach:<br />

⎛<br />

⎞<br />

cx x cx y cx z −〈cx, eye〉<br />

View = ⎜cy x cy y cy z −〈cy, eye〉<br />

⎟<br />

⎝cz x cz y cz z −〈cz, eye〉 ⎠ (21)<br />

0 0 0 1<br />

Auf die Transformation in Kamerakoordinaten durch die Viewmatrix folgt die Transformation in <strong>den</strong><br />

Clipspace durch die Projectionmatrix. Die Projektion ist bestimmt durch die Kameraparameter fov, near<br />

und far sowie das Seitenverhältnis aspect = w h<br />

des Ausgabebildes (wobei w die Breite und h die Höhe<br />

bezeichnet):<br />

⎛<br />

⎞<br />

1<br />

0 0 0<br />

aspect·tan( fov<br />

2 )<br />

1<br />

Projection =<br />

0 0 0<br />

tan(<br />

⎜<br />

fov<br />

2 )<br />

far near·far ⎟<br />

(22)<br />

⎝ 0 0<br />

⎠<br />

far−near near−far<br />

0 0 1 0<br />

View- und Projectionmatrix können <strong>zu</strong> einer gemeinsamen Transformation konkateniert wer<strong>den</strong>:<br />

Transform = Projection · View (23)<br />

Um einen dreidimensionalen Punkt p = ( x y z ) T<br />

von Welt- in Bildschirmkoordinaten <strong>zu</strong> transformieren<br />

wen<strong>den</strong> wir einfach diese Gesamttransformation auf <strong>den</strong> Punkt an:<br />

⎛ ⎞<br />

⎛ ⎞<br />

x C<br />

x<br />

p C = ⎜y C<br />

⎟<br />

⎝ z C<br />

⎠ = Transform · ⎜y<br />

⎟<br />

⎝z⎠ (24)<br />

w C 1<br />

Durch anschließende Homogenisierung erhalten wir <strong>den</strong> Punkt p D<br />

⎛ ⎞ ⎛ ⎞<br />

x D<br />

p D = ⎝y D<br />

⎠ = 1 x C<br />

⎝y C<br />

⎠ (25)<br />

w<br />

z C D z C<br />

Dieser Punkt liegt nun in sogenannten normalisierten Gerätekoordinaten vor. Dabei handelt es sich um ein<br />

Koordinatensystem in dem der Punkt (−1, 1) der linken oberen Ecke und (1, −1) der rechten unteren Ecke<br />

des Ausgabebildes entspricht. Die z-Koordinate liegt für alle Punkte die sich zwischen der near und far<br />

23


y<br />

x<br />

z<br />

(−1, 1, 0)<br />

y<br />

z<br />

x<br />

(1, −1, 1)<br />

Abbildung 24: Transformation des Viewspace in normalisierte Gerätekoordinaten.<br />

Plane befin<strong>den</strong> im Intervall [0, 1] (0 entspricht einem Punkt genau auf der near- und 1 einem Punkt auf der<br />

far Plane). Wie in Abbildung 24 veranschaulicht kann man sich diesen ganzen Vorgang geometrisch als<br />

eine Abbildung der Sichtpyramide in einen Quader vorstellen. Durch eine einfache lineare Transformation<br />

können diese Koordinaten nun in Screenspace-Koordinaten umgerechnet wer<strong>den</strong>, wie der Rasterizer sie<br />

erwartet. Der Screenspace soll sich von der linken oberen Ecke des linken oberen Pixels des Framebuffer<br />

mit <strong>den</strong> Koordinaten (0, 0) bis <strong>zu</strong>r rechten unteren Ecke des rechten unteren Pixels mit <strong>den</strong> Koordinaten<br />

(w, h) erstrecken (wobei w wieder die Breite und h die Höhe des Framebuffer ist).<br />

Triangle Rasterization (5 Punkte) Die in <strong>den</strong> Screenspace transformierten Dreiecke sollen schlussendlich<br />

mit Hilfe eines Scanline-Algorithmus rasterisiert wer<strong>den</strong>. Dabei wird jedes Dreieck wie in Abbildung<br />

25 dargestellt zeilenweise abgetastet. In jeder Zeile (Scanline) wird der erste und letzte <strong>zu</strong>m Dreieck gehörende<br />

Pixel bestimmt und dieser Bereich dann mit der Dreiecksfarbe gefüllt.<br />

Zunächst wer<strong>den</strong> die Vertices da<strong>zu</strong> aufsteigend nach ihrer y-Koordinate geordnet sodass das Problem sich<br />

auf die zwei in Abbildung 26 dargestellten Fälle reduziert. Dann wer<strong>den</strong> die linke und rechte Dreieckskante<br />

entsprechend dem DDA Algorithmus (in der Vorlesung behandelt, sie Folien) abgetastet um <strong>den</strong> Start<br />

24


Abbildung 25: Scanline-Rasterisierung (Scan-Conversion) eines Dreiecks.<br />

v 0<br />

v 0<br />

v 2<br />

v 1<br />

(a)<br />

v 2<br />

v 1<br />

(b)<br />

Abbildung 26: Die zwei möglichen Konfigurationen bei der Scanline-Rasterisierung.<br />

sowie Endpunkt jeder Scanline <strong>zu</strong> bestimmen. Da<strong>zu</strong> wer<strong>den</strong> die Steigungen der linken und rechten Kante<br />

bestimmt und die Start und Endpunkte dann für jede Scanline inkrementell berechnet. Das Dreieck wird<br />

effektiv in zwei Teilen rasterisiert: Zeilenweise erst von v 0 nach v 1 und dann von v 1 nach v 2 .<br />

Wesentlich für die Funktion eines Rasterizers ist die sogenannte Fill-Convention die festlegt unter welchen<br />

Bedingungen ein Pixel <strong>zu</strong>m Dreieck gehört. Der Rasterizer in dieser Übung soll der in der Praxis üblichen<br />

Top-Left Fill-Convention folgen. Dabei zählt ein Pixel dann als innerhalb eines Dreiecks wenn sein Mittelpunkt<br />

innerhalb aller Dreieckskanten liegt. Liegt der Mittelpunkt eines Pixels genau auf einer Kante so<br />

zählt er als innerhalb wenn es sich dabei um eine linke oder horizontale obere Kante des Dreiecks handelt<br />

(Top oder Left). Ohne eine solche Fill-Convention wür<strong>den</strong> beim Rendern von <strong>zu</strong>sammenhängen<strong>den</strong><br />

Dreiecksnetzen störende Artefakte auftreten, da es zwischen angrenzen<strong>den</strong> Dreiecken <strong>zu</strong> Lücken oder <strong>zu</strong>m<br />

abwechseln<strong>den</strong> Übermalen von Pixeln kommen kann. Die Fill-Convention stellt sicher dass jeder Pixel<br />

immer genau einem Dreieck <strong>zu</strong><strong>zu</strong>ordnen ist und solche Probleme nicht auftreten. Abbildung 27 illustriert<br />

dies an einigen Beispielen.<br />

25


Abbildung 27: Dreiecke rasterisiert mit Top-Left Fill-Convention.<br />

Visibility (3 Punkte) Ohne Berücksichtigung anderer Dreiecke und deren Tiefe, wer<strong>den</strong> Dreiecke einfach<br />

in der Reihenfolge, in welcher der Rasterizer sie bearbeitet gezeichnet. Da sich Dreiecke im Raum in<br />

der Regel natürlich gegenseitig verdecken oder gar durchdringen können ist dies für die Darstellung von<br />

3D Szenen un<strong>zu</strong>reichend. Um das Sichtbarkeitsproblem <strong>zu</strong> lösen bedienen wir uns eines Depth-Buffers<br />

(Z-Buffer). Dabei handelt es sich um einen <strong>zu</strong>sätzlichen Buffer in dem für je<strong>den</strong> Pixel ein Tiefenwert gespeichert<br />

wird. Bevor ein Pixel überschrieben wird, wird <strong>zu</strong>erst überprüft ob der Tiefenwert des neuen<br />

Pixels kleiner (Pixel liegt näher am Betrachter) oder gleich dem momentan im Depth-Buffer befindlichen<br />

Tiefenwert ist. Nur wenn dies der Fall ist wird der Pixel gezeichnet. So wer<strong>den</strong> alle Dreiecke unabhängig<br />

von ihrer Rasterisierungsreihenfolge immer korrekt dargestellt. Um diesen Algorithmus implementieren<br />

<strong>zu</strong> können muss für je<strong>den</strong> Pixel dessen Tiefenwert bekannt sein. Dieser wird berechnet indem nicht nur<br />

die Position sondern – nach dem selben Prinzip (DDA Algorithmus) – auch die Tiefenwerte der Vertices<br />

entlang der Kanten und dann von Beginn <strong>zu</strong>m Ende jeder Scanline linear interpoliert wer<strong>den</strong>.<br />

5.2 Programmgerüst<br />

Das <strong>zu</strong>r Verfügung gestellte Programmgerüst enthält bereits die notwendige Infrastruktur (Konfigurationsdatei<br />

einlesen, Ausgabebild speichern, etc.). Um die Übungsaufgabe <strong>zu</strong> lösen müssen nur noch die<br />

jeweiligen Metho<strong>den</strong> der Klasse Rasterizer entsprechend implementiert wer<strong>den</strong>:<br />

• Rasterizer::SetCamera(): Hier wer<strong>den</strong> die View- und Projectionmatrix berechnet.<br />

• Rasterizer::TransformVertex(): Diese Methode transformiert einen Vertex vom Worldin<br />

<strong>den</strong> Screenspace und liefert ein entsprechendes Objekt vom Typ ScreenVertex <strong>zu</strong>rück.<br />

• Rasterizer::RasterizeTriangle(): Implementiert das Rasterisieren eines Dreiecks.<br />

26


5.3 Hinweise<br />

Alle Metho<strong>den</strong> befin<strong>den</strong> sich in der Datei „Rasterizer.cpp“ und sind durch Kommentare (TODO)<br />

gekennzeichnet. In jeder dieser Metho<strong>den</strong> befindet sich auch ein kurzes Codebeispiel das <strong>den</strong> Umgang<br />

mit <strong>den</strong> Framework-Klassen demonstriert. Dieser Beispielcode ist durch Ihre eigene Implementierung <strong>zu</strong><br />

ersetzen.<br />

Je nach Konfiguration wer<strong>den</strong> entweder zwei oder drei Bilddateien erzeugt. Die Datei „testcase.png“<br />

enthält das gerenderte Bild, „testcase.depth.png“ <strong>den</strong> Inhalt des Depth-Buffers. Eine vergrößerte<br />

Version des Ausgabebildes ist in „testcase.upsample.png“ enthalten. Hier sind die Dreieckskanten<br />

als Linien überlagert. Das Letzte Bild wird nur erzeugt wenn der Parameter upsample angegeben wurde.<br />

Dies dient der praktischen Überprüfung der Fill-Convention. Das Generieren dieses Bildes kann unter<br />

Umstän<strong>den</strong> länger dauern. Verwen<strong>den</strong> Sie dieses Feature vor<strong>zu</strong>gsweise für Testcases deren Ausgabebilder<br />

eine geringe Auflösung haben.<br />

Es empfiehlt sich die einzelnen Punkte der Aufgabestellung in der Reihenfolge in der sie hier beschrieben<br />

wur<strong>den</strong> <strong>zu</strong> lösen. Der Beispielcode in Rasterizer::RasterizeTriangle() sorgt dafür dass die<br />

jeweilige Testszene basierend auf <strong>den</strong> von ihnen transformierten Vertices automatisch als Drahtgittermodell<br />

ausgegeben wird. So erhalten Sie von Anfang an (auch implementierte Rasterisierungsfunktion) visuelles<br />

Feedback.<br />

Entwickeln Sie eigene Testkonfigurationen um ihr Programm ausgiebig <strong>zu</strong> testen. Die beigelegten Testcases<br />

dienen hier nur als Ausgangspunkt.<br />

Literatur<br />

[FA91] William T. Freeman and Edward H. Adelson. The design and use of steerable filters. IEEE<br />

Transactions on Pattern Analysis and Machine Intelligence, 13:891–906, 1991.<br />

[HB04] Donald Hearn and M. Pauline Baker. Computer Graphics with OpenGL. Prentice Hall, 3rd edition,<br />

2004.<br />

27

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!