Ãbungen zu den Lehrveranstaltungen 710.003 Computergrafik 1 ...
Ãbungen zu den Lehrveranstaltungen 710.003 Computergrafik 1 ...
Ãbungen zu den Lehrveranstaltungen 710.003 Computergrafik 1 ...
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