Ü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:

  1. 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).
  2. Diffuse Reflexion (diffuse reflection): matte, gleichmäßige Rückstrahlung. Hängt vom Winkel α\alpha zwischen Normale NN und Lichtvektor LL ab (Lambert-Faktor cosα\cos\alpha).
  3. Spekulare / spiegelnde Reflexion (specular reflection): erzeugt das Glanzlicht (specular highlight). Hängt vom Winkel β\beta zwischen Sichtvektor VV und reflektiertem Lichtvektor RR ab, plus Shininess kk.

Optionaler vierter Term: Emission (self-emission, MeM_e) für selbstleuchtende Oberflächen, unabhängig von externen Lichtquellen.

Vollständige Formel:

Lo=MdLa+i=1n(Mdcosα+Mscoskβ)LiL_o = M_d \cdot L_a + \sum_{i=1}^{n} \left( M_d \cdot \cos\alpha + M_s \cdot \cos^{k}\beta \right) \cdot L_i

Tipp: Der ambiente Term MdLaM_d \cdot L_a steht außerhalb der Summe (wird einmal global addiert), die diffusen und spekularen Terme stehen innerhalb der Summe über alle nn Lichtquellen.

Siehe Kapitel 9: Visualisierungstechniken.

Aufgabe 2 (Beginner) - N, L, R, V identifizieren

Für die Beleuchtungsberechnung an einem Oberflächenpunkt braucht man vier Einheitsvektoren: NN, LL, RR, VV. Beschreibe für jeden, wohin er zeigt bzw. wie er entsteht. Ordne außerdem den beiden zentralen Winkeln α\alpha und β\beta jeweils das Vektorpaar und die Reflexionskomponente zu.

Lösung anzeigen

Die vier Vektoren (alle als Einheitsvektoren / normiert):

  • NN: Oberflächennormale (surface normal), steht senkrecht auf der Fläche.
  • LL: Lichtvektor, zeigt vom Oberflächenpunkt zur Lichtquelle hin.
  • RR: reflektierter Lichtvektor, die Spiegelung von LL an NN.
  • VV: Sichtvektor (view direction), zeigt vom Oberflächenpunkt zur Kamera hin.

Die zwei zentralen Winkel:

  • α\alpha = Winkel zwischen NN und LL steuert die diffuse Reflexion, mit cosα=NL\cos\alpha = N \circ L.
  • β\beta = Winkel zwischen RR und VV steuert die spekulare Reflexion, mit cosβ=RV\cos\beta = R \circ V.

Typischer Fehler: LL und VV zeigen vom Oberflächenpunkt weg (zur Lichtquelle bzw. zur Kamera), nicht umgekehrt. Wer die Richtung von LL vertauscht, bekommt beim Skalarprodukt das falsche Vorzeichen. Im GLSL-Builtin reflect muss man deshalb den einfallenden Vektor L-L übergeben: reflect(-L, N).

Siehe Kapitel 9: Visualisierungstechniken.

Aufgabe 3 (Beginner) - cos alpha über das Skalarprodukt

Ein Oberflächenpunkt hat die Normale N=(0,1,0)N = (0, 1, 0). Die Lichtquelle liegt so, dass der (noch nicht normierte) Lichtvektor l=(0,1,0)\vec{l} = (0, 1, 0) ist. In einem zweiten Fall ist l=(1,1,0)\vec{l} = (1, 1, 0).

(a) Warum müssen NN und LL vor dem dot-Aufruf normalisiert werden? (b) Berechne cosα\cos\alpha und den Winkel α\alpha für beide Fälle.

Lösung anzeigen

(a) Das Skalarprodukt lautet allgemein

ab=abcosα.\vec{a}\circ\vec{b} = |\vec{a}| \cdot |\vec{b}| \cdot \cos\alpha.

Nur wenn beide Vektoren normiert sind (a=b=1|\vec{a}| = |\vec{b}| = 1), vereinfacht sich das zu ab=cosα\vec{a}\circ\vec{b} = \cos\alpha. Andernfalls steckt in dot(N,L) noch der Faktor der Längen und man erhält nicht direkt den Kosinus.

(b) Fall 1: N=(0,1,0)N = (0,1,0) ist bereits normiert, l=(0,1,0)\vec{l} = (0,1,0) ebenfalls (l=1|\vec{l}| = 1). Also L=(0,1,0)L = (0,1,0).

cosα=NL=00+11+00=1    α=0.\cos\alpha = N \circ L = 0\cdot 0 + 1\cdot 1 + 0 \cdot 0 = 1 \;\Rightarrow\; \alpha = 0^\circ.

Licht fällt senkrecht ein, maximale diffuse Helligkeit.

Fall 2: l=(1,1,0)\vec{l} = (1,1,0) hat die Länge l=12+12+02=2|\vec{l}| = \sqrt{1^2 + 1^2 + 0^2} = \sqrt{2}. Normiert:

L=12(1,1,0).L = \tfrac{1}{\sqrt{2}}(1,1,0). cosα=NL=012+112+0=120,707    α=45.\cos\alpha = N \circ L = 0\cdot\tfrac{1}{\sqrt 2} + 1\cdot\tfrac{1}{\sqrt 2} + 0 = \tfrac{1}{\sqrt 2} \approx 0{,}707 \;\Rightarrow\; \alpha = 45^\circ.

Tipp: Deshalb steht im Fragment-Shader float cosa = max(0.0, dot(N,L)); mit vorherigem normalize(). Das max(0.0, ...) schneidet negative Werte ab, damit Licht von der Rückseite nicht "negativ" beleuchtet.

Siehe Kapitel 9: Visualisierungstechniken.


Fortgeschritten

Aufgabe 4 (Fortgeschritten) - Diffusen Term numerisch berechnen

Gegeben: diffuse Materialfarbe Md=(0,8, 0,2, 0,2)M_d = (0{,}8,\ 0{,}2,\ 0{,}2), eine Lichtquelle mit Lichtfarbe Li=(1,0, 1,0, 1,0)L_i = (1{,}0,\ 1{,}0,\ 1{,}0), Winkel α=60\alpha = 60^\circ zwischen NN und LL. Es gibt nur diese eine Lichtquelle; Ambient und Spekular werden hier ignoriert.

Berechne den diffusen Farbbeitrag komponentenweise. Was passiert mit dem Ergebnis, wenn α=90\alpha = 90^\circ wäre?

Lösung anzeigen

Der diffuse Term lautet Lo=MdLicosαL_o = M_d \cdot L_i \cdot \cos\alpha (komponentenweise, Hadamard-Produkt).

Schritt 1 - Lambert-Faktor: cos60=0,5\cos 60^\circ = 0{,}5 (sauberer Standardwinkel, nachgerechnet).

Schritt 2 - komponentenweise multiplizieren:

Lo=(0,80,20,2)(1,01,01,0)0,5=(0,81,00,50,21,00,50,21,00,5)=(0,40,10,1).L_o = \begin{pmatrix} 0{,}8 \\ 0{,}2 \\ 0{,}2 \end{pmatrix} \cdot \begin{pmatrix} 1{,}0 \\ 1{,}0 \\ 1{,}0 \end{pmatrix} \cdot 0{,}5 = \begin{pmatrix} 0{,}8 \cdot 1{,}0 \cdot 0{,}5 \\ 0{,}2 \cdot 1{,}0 \cdot 0{,}5 \\ 0{,}2 \cdot 1{,}0 \cdot 0{,}5 \end{pmatrix} = \begin{pmatrix} 0{,}4 \\ 0{,}1 \\ 0{,}1 \end{pmatrix}.

Der Punkt zwischen den Farbvektoren ist die komponentenweise Multiplikation, kein Skalarprodukt.

Bei α=90\alpha = 90^\circ ist cos90=0\cos 90^\circ = 0, also Lo=(0,0,0)L_o = (0,0,0): 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.

Siehe Kapitel 9: Visualisierungstechniken.

Aufgabe 5 (Fortgeschritten) - Spekularen Term und Wirkung von k

Gegeben: spekulare Materialfarbe Ms=(1,0, 1,0, 1,0)M_s = (1{,}0,\ 1{,}0,\ 1{,}0) (weiß), Lichtfarbe Li=(1,0, 1,0, 1,0)L_i = (1{,}0,\ 1{,}0,\ 1{,}0), Winkel β=45\beta = 45^\circ zwischen RR und VV.

(a) Berechne den spekularen Faktor coskβ\cos^{k}\beta für k=1k = 1, k=2k = 2 und k=4k = 4. (b) Erkläre in einem Satz, wie sich das Glanzlicht (specular highlight) mit steigendem kk verändert.

Lösung anzeigen

Der spekulare Term lautet MsLicoskβM_s \cdot L_i \cdot \cos^{k}\beta.

Basis: cos45=120,7071\cos 45^\circ = \tfrac{1}{\sqrt 2} \approx 0{,}7071 (sauberer Standardwinkel).

(a) Potenzieren mit kk:

  • k=1k = 1: cos145=0,7071\cos^1 45^\circ = 0{,}7071.
  • k=2k = 2: cos245=(12)2=12=0,5\cos^2 45^\circ = \left(\tfrac{1}{\sqrt 2}\right)^2 = \tfrac{1}{2} = 0{,}5 (exakt, sauber nachgerechnet).
  • k=4k = 4: cos445=(12)2=0,25\cos^4 45^\circ = \left(\tfrac{1}{2}\right)^2 = 0{,}25 (exakt).

Da MsM_s und LiL_i beide (1,1,1)(1,1,1) sind, ist der spekulare Farbbeitrag jeweils (0,7071, 0,7071, 0,7071)(0{,}7071,\ 0{,}7071,\ 0{,}7071), (0,5, 0,5, 0,5)(0{,}5,\ 0{,}5,\ 0{,}5) bzw. (0,25, 0,25, 0,25)(0{,}25,\ 0{,}25,\ 0{,}25).

(b) Höhere Shininess kk 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 kk (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 k{5,20,45,100}k \in \{5, 20, 45, 100\} üblich: k=5k=5 großes weiches, k=100k=100 kleines hartes Highlight.

Siehe Kapitel 9: Visualisierungstechniken.

Aufgabe 6 (Fortgeschritten) - Wrapping: CLAMP_TO_EDGE vs REPEAT

Eine Textur wird mit Texturkoordinaten außerhalb des Bereichs [0,1]×[0,1][0,1] \times [0,1] abgetastet, konkret mit s=2,3s = 2{,}3 und s=0,4s = -0{,}4.

(a) Was liefert GL_REPEAT für diese beiden Werte, was GL_CLAMP_TO_EDGE? (b) Wofür stehen die Suffixe S,T,RS, T, R bei GL_TEXTURE_WRAP_S usw., und kann man sie unterschiedlich setzen?

Lösung anzeigen

Wrapping regelt, was passiert, wenn Texturkoordinaten außerhalb [0,1][0,1] liegen.

(a)

GL_REPEAT: Es wird nur der Nachkommaanteil verwendet, die Textur wird gekachelt / wiederholt.

  • s=2,3s = 2{,}3 \to effektiv 0,30{,}3.
  • s=0,4s = -0{,}4 \to effektiv 0,60{,}6 (Nachkommaanteil im wiederholten Muster; 0,4-0{,}4 landet bei 10,4=0,61 - 0{,}4 = 0{,}6).

GL_CLAMP_TO_EDGE: Werte <0< 0 oder >1> 1 werden auf den Rand geklemmt; der Randtexel wird wiederholt / gestreckt.

  • s=2,3s = 2{,}3 \to geklemmt auf 1,01{,}0.
  • s=0,4s = -0{,}4 \to geklemmt auf 0,00{,}0.

(b) S,T,RS, T, R sind die drei Texturkoordinaten-Richtungen (analog zu s,t,rs, t, r; RR nur bei 3D-Texturen relevant). Wrapping wird pro Richtung gesetzt und kann unterschiedlich sein, z.B. GL_REPEAT für ss und GL_CLAMP_TO_EDGE für tt: 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 x,yx, y, sondern s,ts, t (oder u,vu, v), normiert auf [0,1][0,1] 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:

VerfahrenNormaleBeleuchtung berechnetInterpolation
Flat Shadingeine Flächennormale (face normal) pro Flächepro Flächekeine (eine Farbe je Facette)
Gouraud ShadingEckennormalen (vertex normals)pro Eckpunkt (Vertex)Farben werden über das Dreieck interpoliert (bilinear)
Phong ShadingPixel-/Fragmentnormalen (per-pixel)pro Pixel/FragmentNormalen 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.

Siehe Kapitel 9: Visualisierungstechniken.

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_FILTER angeben. 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 N=(0,1,0)N = (0, 1, 0) und Lichtvektor L=12(1,1,0)L = \tfrac{1}{\sqrt 2}(-1, 1, 0) (Licht kommt von schräg oben links, LL zeigt zur Lichtquelle).

(a) Berechne den reflektierten Lichtvektor RR (Spiegelung von LL an NN) mit R=2(NL)NLR = 2(N \circ L)\,N - L. (b) Der Sichtvektor sei V=12(1,1,0)V = \tfrac{1}{\sqrt 2}(1, 1, 0). Berechne cosβ=RV\cos\beta = R \circ V und den Winkel β\beta. Was bedeutet das für das Glanzlicht?

Lösung anzeigen

(a) Zuerst NLN \circ L:

NL=0(12)+112+0=12.N \circ L = 0\cdot\left(-\tfrac{1}{\sqrt 2}\right) + 1\cdot\tfrac{1}{\sqrt 2} + 0 = \tfrac{1}{\sqrt 2}.

Dann R=2(NL)NLR = 2(N \circ L)\,N - L:

R=212(0,1,0)12(1,1,0)=(0, 22, 0)(12, 12, 0).R = 2 \cdot \tfrac{1}{\sqrt 2} \cdot (0,1,0) - \tfrac{1}{\sqrt 2}(-1,1,0) = \left(0,\ \tfrac{2}{\sqrt 2},\ 0\right) - \left(-\tfrac{1}{\sqrt 2},\ \tfrac{1}{\sqrt 2},\ 0\right).

Komponentenweise:

R=(0(12), 2212, 0)=(12, 12, 0)=12(1,1,0).R = \left(0 - \left(-\tfrac{1}{\sqrt 2}\right),\ \tfrac{2}{\sqrt 2} - \tfrac{1}{\sqrt 2},\ 0\right) = \left(\tfrac{1}{\sqrt 2},\ \tfrac{1}{\sqrt 2},\ 0\right) = \tfrac{1}{\sqrt 2}(1,1,0).

Kontrolle: R=12+12=1|R| = \sqrt{\tfrac{1}{2} + \tfrac{1}{2}} = 1 (Einheitsvektor, korrekt). RR zeigt nach schräg oben rechts, also gespiegelt zu LL an der senkrechten Normale. Plausibel.

(b)

cosβ=RV=1212+1212+0=12+12=1    β=0.\cos\beta = R \circ V = \tfrac{1}{\sqrt 2}\cdot\tfrac{1}{\sqrt 2} + \tfrac{1}{\sqrt 2}\cdot\tfrac{1}{\sqrt 2} + 0 = \tfrac{1}{2} + \tfrac{1}{2} = 1 \;\Rightarrow\; \beta = 0^\circ.

RR und VV sind identisch, der Betrachter blickt genau in die Reflexionsrichtung. Das Glanzlicht ist maximal: coskβ=1k=1\cos^{k}\beta = 1^k = 1 für jedes kk. Der Betrachter sieht das hellste mögliche Highlight.

Typischer Fehler: Vorzeichen bei reflect. Das GLSL-Builtin reflect erwartet den einfallenden Vektor. Da LL zur Lichtquelle zeigt, gehört in den Shader reflect(-L, N). Die Handrechnung hier nutzt direkt die Spiegelformel R=2(NL)NLR = 2(N \circ L)N - L und liefert dasselbe Ergebnis.

Siehe Kapitel 9: Visualisierungstechniken.


Klausur-Niveau

Aufgabe 10 (Klausur-Niveau) - Vollständiges Phong-Modell durchrechnen

Ein Oberflächenpunkt wird von genau einer Punktlichtquelle beleuchtet. Gegeben:

  • diffuses Material Md=(0,6, 0,6, 0,6)M_d = (0{,}6,\ 0{,}6,\ 0{,}6)
  • spekulares Material Ms=(1,0, 1,0, 1,0)M_s = (1{,}0,\ 1{,}0,\ 1{,}0)
  • Shininess k=2k = 2
  • Lichtfarbe Li=(1,0, 1,0, 1,0)L_i = (1{,}0,\ 1{,}0,\ 1{,}0)
  • globale ambiente Lichtfarbe La=(0,2, 0,2, 0,2)L_a = (0{,}2,\ 0{,}2,\ 0{,}2)
  • Winkel α=60\alpha = 60^\circ (zwischen NN und LL), Winkel β=45\beta = 45^\circ (zwischen RR und VV)

Berechne die resultierende Farbe LoL_o komponentenweise nach der vollständigen Phong-Formel. Zeige die drei Teilbeiträge (ambient, diffus, spekular) getrennt.

Lösung anzeigen

Vollständige Formel (eine Lichtquelle, n=1n = 1):

Lo=MdLa+(Mdcosα+Mscoskβ)LiL_o = M_d \cdot L_a + \left( M_d \cdot \cos\alpha + M_s \cdot \cos^{k}\beta \right) \cdot L_i

Faktoren (nachgerechnet): cos60=0,5\cos 60^\circ = 0{,}5; cos245=(12)2=0,5\cos^2 45^\circ = \left(\tfrac{1}{\sqrt 2}\right)^2 = 0{,}5.

Ambienter Term MdLaM_d \cdot L_a (komponentenweise):

(0,6, 0,6, 0,6)(0,2, 0,2, 0,2)=(0,12, 0,12, 0,12).(0{,}6,\ 0{,}6,\ 0{,}6) \cdot (0{,}2,\ 0{,}2,\ 0{,}2) = (0{,}12,\ 0{,}12,\ 0{,}12).

Diffuser Term MdcosαM_d \cdot \cos\alpha (vor Multiplikation mit LiL_i):

(0,6, 0,6, 0,6)0,5=(0,3, 0,3, 0,3).(0{,}6,\ 0{,}6,\ 0{,}6)\cdot 0{,}5 = (0{,}3,\ 0{,}3,\ 0{,}3).

Spekularer Term MscoskβM_s \cdot \cos^{k}\beta:

(1,0, 1,0, 1,0)0,5=(0,5, 0,5, 0,5).(1{,}0,\ 1{,}0,\ 1{,}0)\cdot 0{,}5 = (0{,}5,\ 0{,}5,\ 0{,}5).

Diffus + spekular, mal Li=(1,1,1)L_i = (1,1,1):

((0,3, 0,3, 0,3)+(0,5, 0,5, 0,5))(1,1,1)=(0,8, 0,8, 0,8).\big((0{,}3,\ 0{,}3,\ 0{,}3) + (0{,}5,\ 0{,}5,\ 0{,}5)\big)\cdot(1,1,1) = (0{,}8,\ 0{,}8,\ 0{,}8).

Summe mit dem ambienten Term:

Lo=(0,12, 0,12, 0,12)+(0,8, 0,8, 0,8)=(0,92, 0,92, 0,92).L_o = (0{,}12,\ 0{,}12,\ 0{,}12) + (0{,}8,\ 0{,}8,\ 0{,}8) = (0{,}92,\ 0{,}92,\ 0{,}92).

Ergebnis: Lo=(0,92, 0,92, 0,92)L_o = (0{,}92,\ 0{,}92,\ 0{,}92), 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 (MdLaM_d \cdot L_a, kein Winkel) und wird einmal global addiert, nicht pro Lichtquelle.

Tipp: Ohne den Ambient-Term wäre eine unbeleuchtete Schattenseite (α90\alpha \geq 90^\circ, cosα\cos\alpha auf 0 abgeschnitten) komplett schwarz. Der Ambient hellt sie gleichmäßig auf.

Siehe Kapitel 9: Visualisierungstechniken.

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: glUniform1i bekommt die Unit (0, 1, 2, ...), niemals die ID von glGenTextures.

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.

  1. Der ambiente Term im Phong-Modell wird mit cosα\cos\alpha skaliert.
  2. Bei einer 512×512512 \times 512-Textur benötigt die vollständige Mipmap-Pyramide etwa ein Drittel (ca. +33%) zusätzlichen Speicher gegenüber dem Original.
  3. GL_LINEAR mittelt die vier nächstgelegenen Texel (bilineare Interpolation).
  4. Die interpolierten Normalen zwischen zwei Vertices sind automatisch Einheitsvektoren, daher genügt normalize() im Vertex-Shader.
  5. Nach einer nicht-uniformen Skalierung transformiert man die Normale korrekt mit (M1)(M^{-1})^\top (Normalmatrix).
Lösung anzeigen
  1. Falsch. Ambient ist richtungsunabhängig und hat keinen Kosinus-Faktor: MdLaM_d \cdot L_a. Nur der diffuse Term nutzt cosα\cos\alpha, der spekulare coskβ\cos^{k}\beta.

  2. Wahr. Die Reihe k=0(14)k=43\sum_{k=0}^{\infty}\left(\tfrac{1}{4}\right)^k = \tfrac{4}{3} 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 2n×2n2^n \times 2^n-Texturen erzeugt; 512=29512 = 2^9 passt.)

  3. Wahr. GL_LINEAR ist der lineare Durchschnitt der vier nächstgelegenen Texel = bilineare Interpolation. GL_NEAREST nimmt dagegen nur den einen nächsten Texel.

  4. 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.

  5. Wahr. Die naive Transformation mit der Modellmatrix MM macht die Normale bei nicht-uniformer Skalierung falsch (sie steht nicht mehr senkrecht auf der Fläche). Korrekt ist die Normalmatrix M=(M1)M' = (M^{-1})^\top, 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 (M1)(M^{-1})^\top 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.