CGExercises

CG Exercise #08

Computergraphik ├ťbungsblatt #08


OpenGL Tutorial: Shader



Aufgabe 8.1: Shader Editor

Starten Sie mit Ihrem Code aus der vorhergehenden ├ťbung #07.

Erg├Ąnzen Sie den Code um einen Shader bzw. bauen Sie in Ihren Code einen Shader-Editor ein. Dazu ben├Âtigen Sie die folgenden zus├Ątzliche Zeilen:

Im Globalen Namensraum:

GLuint prog_id;
const char shader[] = "";

In der Methode initializeOpenGL():

prog_id = lglCompileGLSLProgram(shader);
create_lgl_Qt_ShaderEditor("shader", &prog_id);

In der Methode renderOpenGL():

lglUseProgram(prog_id, false);

Sie sollten nun eine unbeleuchtete (und untexturierte) Szene sehen. Der Vertex Shader multipliziert nur die Vertices mit der MVP (die mit lglModelView gesetzt wurde). Und der Fragment Shader verwendet nur die aktuell spezifizierte Farbe (die zuletzt mit lglColor gesetzt wurde).

D.h. es wird der einfachste denkbare Shader - der sog. Plain-Shader - verwendet (siehe Cheat Sheet → Shaders).

Im Folgenden werden wir nun Schritt f├╝r Schritt diesen Shader weiterentwickeln, so dass die Szene wieder beleuchtet dargestellt wird. Editieren des Shaders im Editor Fenster erfolgt wie folgt:

  • Der aktuell editierte Shader wird benutzt
  • Save & Quit merkt sich den aktuellen Shader
  • Commit → Abspeichern des Shaders in den C++ Code

Zur Erinnerung vorher noch ein paar Verst├Ąndnisfragen:

  • Welche Daten werden im Vertex- bzw. Fragment-Shader verarbeitet?
  • In welchem Koordinatensystem befindet man sich im Vertex- bzw. Fragment-Shader?
  • Wo findet man den Vertex- bzw. Fragment-Shader im GLSL Shader-Editor?

Materialien:

Aufgabe 8.2: GLSL Fog


Non-Linear Fog

Wir steigen mit einem weiteren einfachen GLSL Shader ein: Fogging! D.h. verwenden Sie den folgenden Fragment Shader:

#version 120
uniform vec4 color; // kommt aus dem Hauptprogramm (via lglColor)
void main()
{
   const float density = 0.01f;
   float z = 1.0f / gl_FragCoord.w;
   float f = 1.0f - exp(-3 * density * z*z*z);
   gl_FragColor = (1.0f-f)*color + f*vec4(1);
}

Ändern Sie die Hintergrundfarbe, damit diese zur Darstellung durch den Shader passt. Ändern Sie die Dichte density so dass sie einen passenden Farbverlauf erhalten. Der passende Wert für die Dichte liegt je nach Szenenaufbau zwischen 0.1 und 1E-10.

Aufgabe 8.3: GLSL Uniform


glVertex Cheat Sheet

Ersetzen Sie die Konstante density im GLSL Shader durch einen uniformen Parameter, den Sie im C++ Hauptprogramm bestimmen k├Ânnen (via lglUniformf(“density”, value)). Verwenden Sie wie in ├ťbung #06 passende Tasten, um den Parameter zu ver├Ąndern.

Aufgabe 8.4: GLSL Varying

Momentan wird die Szene noch einfarbig pro Objekt gezeichnet, d.h. es gibt keinerlei Farbvariation. Diese bringen wir jetzt ins Spiel:

Transportieren Sie die Vertices aus dem Vertex- in den Fragment-Shader, indem Sie die Koordinaten im Vertex-Shader zus├Ątzlich in ein sog. Varying schreiben. Wir verwenden dazu die unver├Ąnderten Objekt-Koordinaten, d.h. es wird in diesem Fall nicht mit der MVP multipliziert!


Objekt-Koordinaten als Fragment-Farbe

Diese Objekt-Koordinaten lesen wir nun im Fragment-Shader aus dem Varying aus und verwenden sie als Fragment-Farbe.

Eventuell m├╝ssen die Farbwerte etwas angepasst werden, um eine leicht psychedelische Darstellung (wie rechts beispielhaft abgebildet) zu erreichen. Dazu multipliziert man die einzelnen Komponenten mit einer Konstante und verwendet nur den Nachkommaanteil (GLSL: fract).

Hinweis: Für diese und die folgenden Aufgaben sind keine weiteren Änderungen am C++ Programm erforderlich, lediglich der GLSL Shader ist anzupassen.

Aufgabe 8.5: GLSL Attribute (Normalen)

Transportieren Sie nun nicht nur die Vertices sondern auch die Normalen aus dem Vertex- in den Fragment-Shader. D.h. schreiben Sie das Attribut vertex_normal in ein zus├Ątzliches Varying.

Aufgabe 8.6: GLSL “Debugging”

Jetzt wird es noch psychedelischer: Verwenden Sie die Normalen f├╝r die Fragment-Farbe. Aber nicht die tats├Ąchlichen Normalen, sondern wir verwenden die folgende Abbildung:

$f(\vec{n}) = 0.5\cdot\vec{n} + (0.5, 0.5, 0.5)^T$

Auf diese Weise k├Ânnen wir die Normalen “lesen” und Fehler erkennen → Graphisches Debugging.

Wir erkennen dadurch, dass die Normalen nicht konsistent zwischen den einzelnen Robotersegmenten sind: Welche Normale bzw. welche Farbe m├╝sste eine Fl├Ąche tats├Ąchlich haben, auf die der Betrachter frontal schaut? Bestimmen Sie den konkreten RGB-Farbwert! Tipp: himmelblau.

Aufgabe 8.7: GLSL Normalentransformation

Erinnern Sie Sich, dass die Normalen im Vertex-Shader mit einer speziellen Matrix transformiert (bzw. multipliziert) werden m├╝ssen? Verwenden Sie diese Matrix nun (siehe Cheat Sheet → GLSL)! Dadurch werden die Normalen in Augenkoordinaten transformiert, so dass sie anschlie├čend im Fragment-Shader auch in Augenkoordinaten vorliegen. Renormalisieren Sie die transformierten Normalen au├čerdem im Fragment-Shader (GLSL: normalize)!

Achtung: F├╝r die Transformation der Normalen casten wir zuerst die Matrix auf 3×3 und multiplizieren dann mit dem Normalenvektor!


Ambiente Beleuchtung

Diffuse Beleuchtung

Aufgabe 8.8: GLSL Shading

Zum Abschlu├č k├Ânnen wir die Normalen nun f├╝r die Beleuchtung der Objekte verwenden. Die am einfachsten zu berechnende Beleuchtung ist die diffuse lokale Beleuchtung:

Die diffuse Beleuchtung verwendet den Cosinus des Zwischenwinkels von Lichtvektor $\vec{l}$ und Normale $\vec{n}$ als Beleuchtungsintensit├Ąt bzw Helligkeit. Dazu muss das Skalarprodukt der Normalen mit dem Lichtvektor berechnet werden. Als Lichtvektor $\vec{l}$ w├Ąhlen wir einen beliebigen konstanten 3D Vektor als Beleuchtungsrichtung f├╝r eine rein direktionale Lichtquelle. Diesen Vektor definieren wir als normalisierten Lichtrichtungsvektor im Fragment-Shader. Da die Normalen $\vec{n}$ im Fragment-Shader in Augenkoordinaten vorliegen, wird auch der Lichtrichtungsvektor $\vec{l}$ in Augenkoordinaten angegeben.

Das Ergebnis der obigen Beleuchtungsberechung ist die Reflektanz $r_d = \vec{n} \cdot \vec{l}$ des diffusen Anteils des lokalen Blinn-Phong Beleuchtungsmodells mit der Beleuchtungsintensit├Ąt $I_d = r_dI_L$ ($I_L$ wird zu 1 angenommen). Mit dieser Reflektanz modulieren (d.h. multiplizieren) wir nun die bisherige Vertex-Farbe (color). Achtung: $r_d$ sollte nicht negativ werden (GLSL: max)!


Ambiente & Diffuse Beleuchtung

Verwenden Sie au├čerdem einen ambienten bzw. konstanten Beleuchtungsanteil $I_a = const$. F├╝r einen warmen Beleuchtungston, wie er z.B. durch die Sonne erzeugt wird, verwendet man $I_a$ = (0.2, 0.1, 0). Dieser ambiente Anteil wird zum diffusen Beleuchtungsanteil hinzuaddiert.

Aufgabe 8.9: Toon Shading (Optional, Bonuspunkte ++)

Anstelle einer physikalisch motivierten, diffusen Beleuchtung erzeugen wir jetzt einen Comic-Look.

Beim so genannten Toon Shading (Cell bzw. Tone Shading oder eben auch Comic Shading) werden nur wenige Farben einer festen Farbpalette benutzt, um die Beleuchtungsintensit├Ąt darzustellen.


Toon Shading

Benutzen Sie jetzt das Skalarprodukt (die vorher berechnete Reflektanz $r_d$), um anhand einer Fallunterscheidung (if-else-Kaskade) folgende Farben zu verwenden:

  1. (1.0 1.0 1.0) im Bereich [0.98 .. 1.0]
  2. (0.7 0.3 0.3) im Bereich [0.7 .. 0.98]
  3. (0.5 0.15 0.15) im Bereich [0.4 .. 0.7]
  4. (0.3 0.1 0.1) im Bereich [0 .. 0.4]
  5. (0.1 0.1 0.1) im Bereich [−1 .. 0]

Am Roboter ist das eventuell nicht so sch├Ân zu erkennen. Stellen Sie daher neben den Roboter noch einen Teapot (lglTeapot).


Hausaufgaben bis zum neunten Praktikum


1. BRDF:
Zeichnen Sie die BRDF von einer Spiegelkugel, einer Plastikkugel und einer schwarzen und einer wei├čen matten Kugel (analog zu den Illustrationen in der Vorlesung)!

2. Lokale Beleuchtung:

  1. Zeichnen Sie den Lichtweg f├╝r die lokale Beleuchtung mittels Blinn-Phong. Tragen Sie alle relevanten Vektoren bzw. Gr├Â├čen ein und beschreiben Sie diese stichwortartig.
  2. Was ist der Unterschied zwischen Gouraud, Phong und Blinn-Phong?
  3. Beim W├╝rfel werden die einzelnen Seiten ja flach dargestellt. K├Ânnen Sie die Seiten auch einfach mit Hilfe von Flat Shading korrekt wiedergeben? Warum? Und unter welchen Bedingungen?
  4. Wir haben als Abschw├Ąchungsfunktion f├╝r eine positionale Lichtquelle das Quadrat des Abstandes kennen gelernt, da physikalisch gesehen das die einzige richtige Abschw├Ąchung darstellt. D.h., als Faktor f├╝r den Einfluss des Lichts ergibt sich etwas wie $\frac{1}{c+|\vec{l}|^2}$ mit $c = const$, $\vec{l}$ = Lichtvektor. In der Praxis m├Âchte man sicher gehen, dass eine Lichtquelle keinen Einfluss auf Geometrie jenseits eines gewissen Abstandes $maxdist$ hat. Wie kann z.B. eine lineare Abschw├Ąchungsfunktion aussehen, die am Ort der Lichtquelle den Einflu├č 1 hat, der bis zum Abstand $maxdist$ auf 0 abf├Ąllt, und dann auch nicht negativ wird? Zeichnen Sie erst diese Funktion schematisch und ├╝berlegen Sie Sich anhand der Zeichnung die passende Formel bzw. Geradengleichung.

3. Globale Beleuchtung:
Zeichnen Sie beispielhaft die Lichtwege, die f├╝r die folgenden globalen Beleuchtungsmodelle relevant sind:

  • Raytracing
  • Radiosity
  • Ambient Occlusion
  • Monte-Carlo

Nehmen Sie an, dass Ihre Szene aus einem Raum mit zwei Plastikw├╝rfeln besteht und zeichnen Sie jeweils die entsprechenden Lichtpfade. Zeichnen Sie der Einfachheit halber nicht dreidimensional.

4. Optional: Kaustik
Ganz dumm gefragt: Wieso lassen sich Kaustiken weder mit Raytracing noch mit Radiosity darstellen? Was ist ├╝berhaupt eine Kaustik? Begr├╝nden Sie Ihre Antwort zeichnerisch anhand der auftretenden Lichtpfade!

Options: