Übungen: Beleuchtung & Texturierung
Übungen zum Phong'schen Reflexionsmodell (ambient + diffus + spekular), zum Lambertschen Kosinusgesetz, zu den vier Vektoren N/L/R/V, den Shading-Verfahren (Flat/Gouraud/Phong) sowie zur Texturierung (UV-Koordinaten, Wrapping, Filterung, Mipmaps). Alle trigonometrischen Ergebnisse sind mit sauberen Winkeln gewählt und nachgerechnet. Lösungen mit vollständigen Schritten, Kapitelverweisen und Fehler-/Tipp-Hinweisen sind einklappbar. Quellen: Kapitel 9: Visualisierungstechniken und Kapitel 9b: Texturierung.
Beginner
Aufgabe 1 (Beginner) - Die drei Komponenten benennen
Das Phong'sche Reflexionsmodell (Phong reflection model) setzt die in Richtung des Betrachters abgegebene Lichtintensität aus einer Summe von drei Grundkomponenten zusammen (plus einem optionalen vierten Term). Nenne die drei Komponenten, ordne jeder in einem Satz ihre Rolle zu, und nenne den optionalen vierten Term.
Lösung anzeigen
Die drei Komponenten:
- Ambiente Beleuchtung (ambient lighting): imitiert grob die indirekte Beleuchtung (Licht von anderen Oberflächen). Ohne Ambient bleiben nicht direkt beleuchtete Bereiche komplett schwarz. Richtungsunabhängig (kein Kosinus-Faktor).
- Diffuse Reflexion (diffuse reflection): matte, gleichmäßige Rückstrahlung. Hängt vom Winkel zwischen Normale und Lichtvektor ab (Lambert-Faktor ).
- Spekulare / spiegelnde Reflexion (specular reflection): erzeugt das Glanzlicht (specular highlight). Hängt vom Winkel zwischen Sichtvektor und reflektiertem Lichtvektor ab, plus Shininess .
Optionaler vierter Term: Emission (self-emission, ) für selbstleuchtende Oberflächen, unabhängig von externen Lichtquellen.
Vollständige Formel:
Tipp: Der ambiente Term steht außerhalb der Summe (wird einmal global addiert), die diffusen und spekularen Terme stehen innerhalb der Summe über alle Lichtquellen.
Aufgabe 2 (Beginner) - N, L, R, V identifizieren
Für die Beleuchtungsberechnung an einem Oberflächenpunkt braucht man vier Einheitsvektoren: , , , . Beschreibe für jeden, wohin er zeigt bzw. wie er entsteht. Ordne außerdem den beiden zentralen Winkeln und jeweils das Vektorpaar und die Reflexionskomponente zu.
Lösung anzeigen
Die vier Vektoren (alle als Einheitsvektoren / normiert):
- : Oberflächennormale (surface normal), steht senkrecht auf der Fläche.
- : Lichtvektor, zeigt vom Oberflächenpunkt zur Lichtquelle hin.
- : reflektierter Lichtvektor, die Spiegelung von an .
- : Sichtvektor (view direction), zeigt vom Oberflächenpunkt zur Kamera hin.
Die zwei zentralen Winkel:
- = Winkel zwischen und steuert die diffuse Reflexion, mit .
- = Winkel zwischen und steuert die spekulare Reflexion, mit .
Typischer Fehler: und zeigen vom Oberflächenpunkt weg (zur Lichtquelle bzw. zur Kamera), nicht umgekehrt. Wer die Richtung von vertauscht, bekommt beim Skalarprodukt das falsche Vorzeichen. Im GLSL-Builtin
reflectmuss man deshalb den einfallenden Vektor übergeben:reflect(-L, N).
Aufgabe 3 (Beginner) - cos alpha über das Skalarprodukt
Ein Oberflächenpunkt hat die Normale . Die Lichtquelle liegt so, dass der (noch nicht normierte) Lichtvektor ist. In einem zweiten Fall ist .
(a) Warum müssen und vor dem dot-Aufruf normalisiert werden?
(b) Berechne und den Winkel für beide Fälle.
Lösung anzeigen
(a) Das Skalarprodukt lautet allgemein
Nur wenn beide Vektoren normiert sind (), vereinfacht sich das zu . Andernfalls steckt in dot(N,L) noch der Faktor der Längen und man erhält nicht direkt den Kosinus.
(b) Fall 1: ist bereits normiert, ebenfalls (). Also .
Licht fällt senkrecht ein, maximale diffuse Helligkeit.
Fall 2: hat die Länge . Normiert:
Tipp: Deshalb steht im Fragment-Shader
float cosa = max(0.0, dot(N,L));mit vorherigemnormalize(). Dasmax(0.0, ...)schneidet negative Werte ab, damit Licht von der Rückseite nicht "negativ" beleuchtet.
Fortgeschritten
Aufgabe 4 (Fortgeschritten) - Diffusen Term numerisch berechnen
Gegeben: diffuse Materialfarbe , eine Lichtquelle mit Lichtfarbe , Winkel zwischen und . Es gibt nur diese eine Lichtquelle; Ambient und Spekular werden hier ignoriert.
Berechne den diffusen Farbbeitrag komponentenweise. Was passiert mit dem Ergebnis, wenn wäre?
Lösung anzeigen
Der diffuse Term lautet (komponentenweise, Hadamard-Produkt).
Schritt 1 - Lambert-Faktor: (sauberer Standardwinkel, nachgerechnet).
Schritt 2 - komponentenweise multiplizieren:
Der Punkt zwischen den Farbvektoren ist die komponentenweise Multiplikation, kein Skalarprodukt.
Bei ist , also : Das Licht streift die Fläche tangential, es kommt kein diffuser Beitrag an.
Typischer Fehler: Material und Licht werden komponentenweise multipliziert (Hadamard-Produkt), nicht als Skalarprodukt. Ein Skalarprodukt würde einen einzelnen Zahlwert liefern und die Farbinformation zerstören.
Aufgabe 5 (Fortgeschritten) - Spekularen Term und Wirkung von k
Gegeben: spekulare Materialfarbe (weiß), Lichtfarbe , Winkel zwischen und .
(a) Berechne den spekularen Faktor für , und . (b) Erkläre in einem Satz, wie sich das Glanzlicht (specular highlight) mit steigendem verändert.
Lösung anzeigen
Der spekulare Term lautet .
Basis: (sauberer Standardwinkel).
(a) Potenzieren mit :
- : .
- : (exakt, sauber nachgerechnet).
- : (exakt).
Da und beide sind, ist der spekulare Farbbeitrag jeweils , bzw. .
(b) Höhere Shininess macht das Highlight kleiner und härter (Eindruck eines "harten" Materials); niedrige Shininess ergibt ein großes, weiches Highlight. Man sieht das oben: bei festem Winkel sinkt der Faktor mit steigendem (0,71 -> 0,50 -> 0,25), abseits des Reflexionswinkels fällt er also schneller ab.
Tipp: Die spekulare Materialfarbe ist oft weiß und kann von der diffusen Materialfarbe abweichen. In der Praxis sind Reihen wie üblich: großes weiches, kleines hartes Highlight.
Aufgabe 6 (Fortgeschritten) - Wrapping: CLAMP_TO_EDGE vs REPEAT
Eine Textur wird mit Texturkoordinaten außerhalb des Bereichs abgetastet, konkret mit und .
(a) Was liefert GL_REPEAT für diese beiden Werte, was GL_CLAMP_TO_EDGE?
(b) Wofür stehen die Suffixe bei GL_TEXTURE_WRAP_S usw., und kann man sie unterschiedlich setzen?
Lösung anzeigen
Wrapping regelt, was passiert, wenn Texturkoordinaten außerhalb liegen.
(a)
GL_REPEAT: Es wird nur der Nachkommaanteil verwendet, die Textur wird gekachelt / wiederholt.
- effektiv .
- effektiv (Nachkommaanteil im wiederholten Muster; landet bei ).
GL_CLAMP_TO_EDGE: Werte oder werden auf den Rand geklemmt; der Randtexel wird wiederholt / gestreckt.
- geklemmt auf .
- geklemmt auf .
(b) sind die drei Texturkoordinaten-Richtungen (analog zu ; nur bei 3D-Texturen relevant). Wrapping wird pro Richtung gesetzt und kann unterschiedlich sein, z.B. GL_REPEAT für und GL_CLAMP_TO_EDGE für : dann wird horizontal gekachelt, vertikal geklemmt.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Typischer Fehler: Bei Texturen nutzt man nicht , sondern (oder ), normiert auf unabhängig von der Pixelauflösung.
Siehe Kapitel 9b: Texturierung.
Anspruchsvoll
Aufgabe 7 (Anspruchsvoll) - Flat vs Gouraud vs Phong Shading
(a) Ordne den drei Shading-Verfahren die jeweilige Granularität der Normalen zu (Flächen-/Ecken-/Pixelnormalen) und beschreibe, wo die Beleuchtung berechnet wird. (b) Ein kleines, scharfes Glanzlicht trifft genau die Mitte eines großen Dreiecks. Welches Verfahren zeigt es korrekt, welches verliert es und warum? Wie kann man das bei Gouraud dennoch retten? (c) Warum gilt: Phong Shading ist nicht dasselbe wie Phong's Reflexionsmodell?
Lösung anzeigen
(a) Die Verfahren unterscheiden sich in der Granularität der Normalen:
| Verfahren | Normale | Beleuchtung berechnet | Interpolation |
|---|---|---|---|
| Flat Shading | eine Flächennormale (face normal) pro Fläche | pro Fläche | keine (eine Farbe je Facette) |
| Gouraud Shading | Eckennormalen (vertex normals) | pro Eckpunkt (Vertex) | Farben werden über das Dreieck interpoliert (bilinear) |
| Phong Shading | Pixel-/Fragmentnormalen (per-pixel) | pro Pixel/Fragment | Normalen werden interpoliert, dann pro Pixel beleuchtet |
(b) Phong Shading zeigt das Glanzlicht korrekt, weil die Beleuchtung pro Pixel berechnet wird, also auch in der Dreiecksmitte. Gouraud Shading verliert das Highlight: die Beleuchtung wird nur an den drei Ecken berechnet, und wenn die Highlight-Spitze zwischen den Ecken liegt, taucht sie in keiner Eckfarbe auf und wird durch die Interpolation "weggemittelt". Rettung bei Gouraud nur durch feinere Tessellierung (tessellation), z.B. die Reihe 2 -> 8 -> 18 -> 50 -> 200 -> 3200 Dreiecke, bis eine Ecke nahe genug an der Highlight-Stelle liegt. Das ist teuer (Kosten-/Qualitätsabwägung).
(c) Das Reflexionsmodell beschreibt, was berechnet wird (ambient + diffus + spekular). Shading beschreibt, wo bzw. wie oft ausgewertet wird (pro Fläche / pro Vertex / pro Pixel). Man kann das Phong-Reflexionsmodell mit Flat-, Gouraud- oder Phong-Shading auswerten. Die beiden Begriffe sind orthogonal.
Typischer Fehler: Phong Shading mit Phong's Reflexionsmodell verwechseln. Das ist eine klassische Prüfungsfalle. Merksatz: Modell = was, Shading = wo/wie oft.
Tipp: Eckennormalen stehen senkrecht auf der Tangentialebene (nicht auf der Fläche selbst) und werden durch Interpolation der Flächennormalen der benachbarten Flächen gewonnen.
Aufgabe 8 (Anspruchsvoll) - NEAREST, LINEAR, trilinear und die MAG-Falle
(a) Erkläre den Unterschied zwischen GL_NEAREST und GL_LINEAR. Wie viele Texel gehen jeweils ein?
(b) Was bedeutet der Filtermodus GL_LINEAR_MIPMAP_LINEAR? Zerlege das Namensschema GL_{...}_MIPMAP_{...} in seine zwei Interpolationsstufen und gib an, wie dieser Modus üblich heißt.
(c) Warum darf man einen Mipmap-Filtermodus nicht für GL_TEXTURE_MAG_FILTER setzen?
Lösung anzeigen
(a)
GL_NEAREST(Point Filter): verwendet den einen nächstgelegenen Texel. Bei Magnification blockig/pixelig, bei Minification Aliasing/Flimmern.GL_LINEAR: linearer Durchschnitt der vier nächstgelegenen Texel = bilineare Interpolation. Weichere Übergänge.
(b) Das Namensschema ist GL_{NEAREST,LINEAR}_MIPMAP_{NEAREST,LINEAR}:
- 1. Teil (vor
MIPMAP): Interpolation innerhalb einer Mipmap-Stufe.LINEAR= bilinear innerhalb der Stufe. - 2. Teil (nach
MIPMAP): Interpolation zwischen zwei benachbarten Stufen.LINEAR= linear zwischen den Stufen.
GL_LINEAR_MIPMAP_LINEAR = bilinear innerhalb einer Stufe plus linear zwischen zwei Stufen = trilineare Interpolation (trilinear interpolation). Der Anti-Aliasing-Vorteil ist am deutlichsten bei Bewegung sichtbar (weniger Flimmern).
(c) Nur der MIN_FILTER (Minification) kennt Mipmap-Filtermodi, weil Mipmaps nur beim Verkleinern (viele Texel auf ein Pixel) sinnvoll sind. Der MAG_FILTER (Magnification) akzeptiert ausschließlich GL_NEAREST oder GL_LINEAR, keine Mipmap-Modi. Beim Vergrößern gibt es keine kleinere Stufe, aus der man mitteln könnte.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // ok
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // ok (trilinear)
Typischer Fehler: Einen Mipmap-Modus für
GL_TEXTURE_MAG_FILTERangeben. Häufiger Fehler, führt zu ungültiger Konfiguration.
Siehe Kapitel 9b: Texturierung.
Aufgabe 9 (Anspruchsvoll) - Reflektierten Vektor R berechnen und cos beta bestimmen
Gegeben (bereits normiert): Normale und Lichtvektor (Licht kommt von schräg oben links, zeigt zur Lichtquelle).
(a) Berechne den reflektierten Lichtvektor (Spiegelung von an ) mit . (b) Der Sichtvektor sei . Berechne und den Winkel . Was bedeutet das für das Glanzlicht?
Lösung anzeigen
(a) Zuerst :
Dann :
Komponentenweise:
Kontrolle: (Einheitsvektor, korrekt). zeigt nach schräg oben rechts, also gespiegelt zu an der senkrechten Normale. Plausibel.
(b)
und sind identisch, der Betrachter blickt genau in die Reflexionsrichtung. Das Glanzlicht ist maximal: für jedes . Der Betrachter sieht das hellste mögliche Highlight.
Typischer Fehler: Vorzeichen bei
reflect. Das GLSL-Builtinreflecterwartet den einfallenden Vektor. Da zur Lichtquelle zeigt, gehört in den Shaderreflect(-L, N). Die Handrechnung hier nutzt direkt die Spiegelformel und liefert dasselbe Ergebnis.
Klausur-Niveau
Aufgabe 10 (Klausur-Niveau) - Vollständiges Phong-Modell durchrechnen
Ein Oberflächenpunkt wird von genau einer Punktlichtquelle beleuchtet. Gegeben:
- diffuses Material
- spekulares Material
- Shininess
- Lichtfarbe
- globale ambiente Lichtfarbe
- Winkel (zwischen und ), Winkel (zwischen und )
Berechne die resultierende Farbe komponentenweise nach der vollständigen Phong-Formel. Zeige die drei Teilbeiträge (ambient, diffus, spekular) getrennt.
Lösung anzeigen
Vollständige Formel (eine Lichtquelle, ):
Faktoren (nachgerechnet): ; .
Ambienter Term (komponentenweise):
Diffuser Term (vor Multiplikation mit ):
Spekularer Term :
Diffus + spekular, mal :
Summe mit dem ambienten Term:
Ergebnis: , ein helles, fast weißes Grau (das weiße Glanzlicht dominiert).
Typischer Fehler: Den ambienten Term in die Summe ziehen oder ihm einen Kosinus-Faktor geben. Ambient ist richtungsunabhängig (, kein Winkel) und wird einmal global addiert, nicht pro Lichtquelle.
Tipp: Ohne den Ambient-Term wäre eine unbeleuchtete Schattenseite (, auf 0 abgeschnitten) komplett schwarz. Der Ambient hellt sie gleichmäßig auf.
Aufgabe 11 (Klausur-Niveau) - Texture Unit vs Textur-ID bei glUniform1i
Ein Shader nutzt zwei Texturen. Der C++-Code lautet:
GLuint texA, texB;
glGenTextures(1, &texA); // liefert z.B. ID 7
glGenTextures(1, &texB); // liefert z.B. ID 8
// ... beide mit glTexImage2D befuellt ...
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texA);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texB);
glUniform1i(glGetUniformLocation(programID, "tex0"), /* ??? */);
glUniform1i(glGetUniformLocation(programID, "tex1"), /* ??? */);
(a) Welche Werte gehören in die beiden /* ??? */-Stellen und warum?
(b) Ein Kommilitone schreibt dort 7 und 8. Was ist der Fehler und was passiert dadurch?
(c) In welcher Reihenfolge müssen glActiveTexture und glBindTexture aufgerufen werden?
Lösung anzeigen
(a) Es gehören die Texture Units hinein: 0 für tex0 und 1 für tex1.
glUniform1i(glGetUniformLocation(programID, "tex0"), 0); // Sampler tex0 -> Unit 0
glUniform1i(glGetUniformLocation(programID, "tex1"), 1); // Sampler tex1 -> Unit 1
Das zweite Argument von glUniform1i ist die Texture Unit, an die die Textur zuvor mit glActiveTexture + glBindTexture gebunden wurde. Der Sampler zeigt auf die Unit, nicht auf die Textur selbst. Im Beispiel wurde texA an Unit 0 (GL_TEXTURE0) und texB an Unit 1 (GL_TEXTURE1) gebunden.
(b) 7 und 8 sind die Textur-IDs (die von glGenTextures zurückgegebenen Namen), nicht die Texture Units. Damit würden die Sampler auf die Units 7 und 8 zeigen, an die hier gar nichts gebunden ist. Ergebnis: falsche oder leere Texturen. Das ist die häufigste Fehlerquelle bei Multitexturing und eine explizit hervorgehobene Prüfungsfalle.
(c) Erst die Unit wählen (glActiveTexture(GL_TEXTUREi)), dann die Textur binden (glBindTexture). Die GL_TEXTUREi sind fortlaufend, glActiveTexture(GL_TEXTURE0 + 1) ist äquivalent zu glActiveTexture(GL_TEXTURE1).
Typischer Fehler: Texture Unit und Textur-ID verwechseln. Merksatz:
glUniform1ibekommt die Unit (0, 1, 2, ...), niemals die ID vonglGenTextures.
Siehe Kapitel 9b: Texturierung.
Aufgabe 12 (Klausur-Niveau) - Multiple Choice gemischt
Kreuze für jede Aussage an, ob sie wahr oder falsch ist, und begründe kurz.
- Der ambiente Term im Phong-Modell wird mit skaliert.
- Bei einer -Textur benötigt die vollständige Mipmap-Pyramide etwa ein Drittel (ca. +33%) zusätzlichen Speicher gegenüber dem Original.
GL_LINEARmittelt die vier nächstgelegenen Texel (bilineare Interpolation).- Die interpolierten Normalen zwischen zwei Vertices sind automatisch Einheitsvektoren, daher genügt
normalize()im Vertex-Shader. - Nach einer nicht-uniformen Skalierung transformiert man die Normale korrekt mit (Normalmatrix).
Lösung anzeigen
-
Falsch. Ambient ist richtungsunabhängig und hat keinen Kosinus-Faktor: . Nur der diffuse Term nutzt , der spekulare .
-
Wahr. Die Reihe bedeutet ca. +1/3 (33%) zusätzlichen Speicher. Jede Stufe hat halbe Kantenlänge, also ein Viertel der Texel der vorigen Stufe. (Mipmaps werden aus -Texturen erzeugt; passt.)
-
Wahr.
GL_LINEARist der lineare Durchschnitt der vier nächstgelegenen Texel = bilineare Interpolation.GL_NEARESTnimmt dagegen nur den einen nächsten Texel. -
Falsch. Die zwischen den Vertices interpolierten Vektoren sind nicht mehr normiert. Deshalb muss
normalize()erneut im Fragment-Shader aufgerufen werden, nicht (nur) im Vertex-Shader. -
Wahr. Die naive Transformation mit der Modellmatrix macht die Normale bei nicht-uniformer Skalierung falsch (sie steht nicht mehr senkrecht auf der Fläche). Korrekt ist die Normalmatrix , im Shader
transpose(inverse(viewMatrix * modelMatrix)).
Hinweis: Die vollständige Herleitung der Normalmatrix ist laut Vorlesung ausdrücklich als "Nicht klausurrelevant!" markiert. Die praktische Anwendung (
transpose(inverse(...))im Shader) ist dagegen klausurrelevant. Für die Klausur genügt es, das Ergebnis und seine Anwendung zu kennen.
Tipp: Aussagen 4 und 5 sind klassische Shader-Fallen. Merke: normalisieren gehört in den Fragment-Shader (wegen Interpolation), und Normalen brauchen die inverse transponierte Matrix.
Siehe Kapitel 9: Visualisierungstechniken und Kapitel 9b: Texturierung.