Übungen: Rendering-Pipeline & Interpolation

Übungsaufgaben zu Kapitel 8: Koordinatenräume (coordinate spaces) und Viewport-Transformation (viewport transformation), lineare und baryzentrische Farbinterpolation (linear / barycentric color interpolation) im Dreieck, Tiefentest (depth test) mit z-Buffer sowie der Painter's Algorithm. Rechne jede Aufgabe erst selbst, bevor du die Lösung aufklappst. Alle Farbwerte sind in normierten RGB-Kanälen [0,1][0,1] angegeben.


Beginner

Aufgabe 1 (Beginner) - Koordinatenräume ordnen

Bringe die folgenden Koordinatenräume (coordinate spaces) in die korrekte Reihenfolge, die ein Vertex von den Rohdaten bis zur normierten Darstellung durchläuft, und benenne jeweils die Matrix bzw. den Schritt, der den Übergang bewirkt:

Camera Space, NDC, World Space, Clipping Space, Local Space.

Lösung anzeigen

Korrekte Kette:

LocalModelMatrixWorldViewMatrixCameraProjectionMatrixClippingHomogenisierungNDC\text{Local} \xrightarrow{\text{ModelMatrix}} \text{World} \xrightarrow{\text{ViewMatrix}} \text{Camera} \xrightarrow{\text{ProjectionMatrix}} \text{Clipping} \xrightarrow{\text{Homogenisierung}} \text{NDC}
Von -> NachÜbergang per
Local Space -> World SpaceModelMatrix (model matrix)
World Space -> Camera SpaceViewMatrix (view matrix)
Camera Space -> Clipping SpaceProjectionMatrix (projection matrix)
Clipping Space -> NDCHomogenisierung (perspective divide)

Merke:

  • Bis zum NDC ([1,1]3[-1,1]^3) ist alles auflösungsunabhängig (resolution-independent). Erst die anschließende Viewport-Transformation macht die Ausgabe geräteabhängig.
  • Local -> Clipping Space werden selbst im Vertex Shader berechnet; ab der Homogenisierung/NDC läuft es automatisch auf der GPU (die Homogenisierung kann aber auch manuell im Vertex Shader erfolgen).

Typischer Fehler: Die Kamerablickrichtung falsch angeben. Im Camera Space (view space) sitzt die Kamera im Ursprung und blickt entlang der negativen z-Achse.

Siehe Kapitel 8: Rendering-Pipeline.


Aufgabe 2 (Beginner) - Lineare Farbinterpolation bei t=1/4t = 1/4

Entlang einer Dreieckskante wird die Farbe linear interpoliert (linear interpolation). Am Startpunkt gilt F(0)=(1,0,0)F(0) = (1, 0, 0) (rot), am Endpunkt F(1)=(0,0,1)F(1) = (0, 0, 1) (blau). Berechne die Farbe F ⁣(14)F\!\left(\tfrac{1}{4}\right).

Lösung anzeigen

Formel der linearen Farbinterpolation:

F(t)=(1t)F(0)+tF(1)F(t) = (1 - t)\cdot F(0) + t\cdot F(1)

Einsetzen von t=14t = \tfrac{1}{4}, also (1t)=34(1-t) = \tfrac{3}{4}:

F ⁣(14)=34(100)+14(001)=(3/401/4)F\!\left(\tfrac{1}{4}\right) = \tfrac{3}{4}\begin{pmatrix} 1 \\ 0 \\ 0 \end{pmatrix} + \tfrac{1}{4}\begin{pmatrix} 0 \\ 0 \\ 1 \end{pmatrix} = \begin{pmatrix} 3/4 \\ 0 \\ 1/4 \end{pmatrix}

Ergebnis: (0,75, 0, 0,25)(0{,}75,\ 0,\ 0{,}25), eine überwiegend rötliche Mischfarbe mit leichtem Blauanteil. Kontrolle: Bei t=0t=0 käme F(0)F(0) heraus, bei t=1t=1 käme F(1)F(1) heraus.

Typischer Fehler: Interpolationsvorzeichen vertauschen. Der Gewichtungsfaktor von F(0)F(0) ist (1t)(1-t), nicht tt. Wer tF(0)+(1t)F(1)t\cdot F(0) + (1-t)\cdot F(1) rechnet, erhält fälschlich (1/4,0,3/4)(1/4, 0, 3/4).

Siehe Kapitel 8: Rendering-Pipeline.


Aufgabe 3 (Beginner) - Fragment oder Pixel?

Beantworte kurz: Was ist der Unterschied zwischen einem Fragment (fragment) und einem Pixel (pixel)? Warum wird nicht jedes Fragment zu einem Pixel?

Lösung anzeigen
  • Ein Pixel ist ein Bildschirmelement (das finale Bildpunkt-Ergebnis).
  • Ein Fragment ist die Vorstufe eines Pixels, erzeugt von der Rasterisierung (rasterization).

Beziehung:

  • Mehrere Fragmente können zu einem Pixel beitragen (z.B. bei Transparenz).
  • Nicht jedes Fragment wird zu einem Pixel: Der Tiefentest (z-Buffer) verwirft verdeckte Fragmente, bevor sie das Bild beeinflussen.

Tipp: Diese Unterscheidung ist prüfungsrelevant. Der Fragment Shader (fragment shader) berechnet die Farbe pro Fragment, nicht direkt pro Pixel.

Siehe Kapitel 8: Rendering-Pipeline.


Fortgeschritten

Aufgabe 4 (Fortgeschritten) - Viewport-Transformation

Ein Punkt liegt im Normalized Device Space (NDC) bei p=(0,5, 0,5, 0)p = (0{,}5,\ -0{,}5,\ 0). Die Bildschirmauflösung beträgt w=800w = 800 (Breite) und h=600h = 600 (Höhe). Berechne den zugehörigen Bildpunkt p=(px,py,pz)p' = (p'_x, p'_y, p'_z) im Screen Space.

Lösung anzeigen

Formeln der Viewport-Transformation (viewport transformation):

px=12(px+1)w,py=12(py+1)h,pz=12(pz+1)p'_x = \tfrac{1}{2}\,(p_x + 1)\cdot w,\qquad p'_y = \tfrac{1}{2}\,(p_y + 1)\cdot h,\qquad p'_z = \tfrac{1}{2}\,(p_z + 1)

Einsetzen:

px=12(0,5+1)800=121,5800=600p'_x = \tfrac{1}{2}\,(0{,}5 + 1)\cdot 800 = \tfrac{1}{2}\cdot 1{,}5 \cdot 800 = 600 py=12(0,5+1)600=120,5600=150p'_y = \tfrac{1}{2}\,(-0{,}5 + 1)\cdot 600 = \tfrac{1}{2}\cdot 0{,}5 \cdot 600 = 150 pz=12(0+1)=0,5p'_z = \tfrac{1}{2}\,(0 + 1) = 0{,}5

Ergebnis: p=(600, 150, 0,5)p' = (600,\ 150,\ 0{,}5).

Tipp: Der Tiefenwert zz wird von [1,1][-1,1] auf [0,1][0,1] abgebildet und bleibt auflösungsunabhängig; nur xx und yy hängen von ww und hh ab. Genau deshalb ist erst die Viewport-Transformation geräteabhängig.

Hinweis: Die Folie nennt zusätzlich Pixelmittelpunkte wie (0,5,0,5)(0{,}5, 0{,}5) unten links und (w0,5,h0,5)(w-0{,}5, h-0{,}5) oben rechts. Die reine Skalierungsformel oben liefert an den NDC-Rändern exakt 00 bzw. ww (nicht 0,50{,}5 bzw. w0,5w-0{,}5). Für Klausuren genügt in der Regel die Grundformel; die Mittelpunkt-Angaben beschreiben die diskreten Rasterzentren.

Siehe Kapitel 8: Rendering-Pipeline.


Aufgabe 5 (Fortgeschritten) - Baryzentrische Farbinterpolation im Dreieck

Ein Dreieck hat die folgenden Vertices mit zugeordneten Vertex-Farben:

VertexPosition (x,y)(x, y)Farbe (RGB)
V1V_1(0,0)(0, 0)(1,0,0)(1, 0, 0) rot
V2V_2(4,0)(4, 0)(0,1,0)(0, 1, 0) grün
V3V_3(0,4)(0, 4)(0,0,1)(0, 0, 1) blau

Bestimme die interpolierte Farbe im inneren Punkt P=(1,2)P = (1, 2). Berechne dazu zuerst die baryzentrischen Gewichte (barycentric weights) λ1,λ2,λ3\lambda_1, \lambda_2, \lambda_3 und prüfe, dass sie sich zu 11 summieren.

Lösung anzeigen

Schritt 1: Baryzentrische Gewichte. Da V1V_1 im Ursprung liegt und V2,V3V_2, V_3 auf den Achsen bei 44 liegen, gilt P=λ2V2+λ3V3=(4λ2, 4λ3)P = \lambda_2 V_2 + \lambda_3 V_3 = (4\lambda_2,\ 4\lambda_3). Aus P=(1,2)P = (1, 2):

λ2=x4=14,λ3=y4=24=12,λ1=1λ2λ3=11412=14\lambda_2 = \frac{x}{4} = \frac{1}{4},\qquad \lambda_3 = \frac{y}{4} = \frac{2}{4} = \frac{1}{2},\qquad \lambda_1 = 1 - \lambda_2 - \lambda_3 = 1 - \tfrac{1}{4} - \tfrac{1}{2} = \tfrac{1}{4}

Kontrolle der Summe: λ1+λ2+λ3=14+14+12=1\lambda_1 + \lambda_2 + \lambda_3 = \tfrac{1}{4} + \tfrac{1}{4} + \tfrac{1}{2} = 1. Alle Gewichte liegen in [0,1][0,1], also liegt PP tatsächlich im Dreieck.

Schritt 2: Farbe interpolieren. Die Farbe ist die gewichtete Summe der Vertex-Farben:

F(P)=λ1(100)+λ2(010)+λ3(001)=(λ1λ2λ3)=(1/41/41/2)F(P) = \lambda_1 \begin{pmatrix} 1 \\ 0 \\ 0 \end{pmatrix} + \lambda_2 \begin{pmatrix} 0 \\ 1 \\ 0 \end{pmatrix} + \lambda_3 \begin{pmatrix} 0 \\ 0 \\ 1 \end{pmatrix} = \begin{pmatrix} \lambda_1 \\ \lambda_2 \\ \lambda_3 \end{pmatrix} = \begin{pmatrix} 1/4 \\ 1/4 \\ 1/2 \end{pmatrix}

Ergebnis: F(P)=(0,25, 0,25, 0,5)F(P) = (0{,}25,\ 0{,}25,\ 0{,}5), ein blaustichiges Violett (blau dominiert, weil PP näher an V3V_3 liegt).

Tipp: Weil hier jede Vertex-Farbe genau ein reiner RGB-Kanal ist, entsprechen die Farbkanäle direkt den Gewichten. Das ist ein guter Selbstcheck: Die drei Kanäle müssen sich ebenfalls zu 11 summieren.

Typischer Fehler: Gewichte verwenden, die sich nicht zu 11 summieren. Immer λ1=1λ2λ3\lambda_1 = 1 - \lambda_2 - \lambda_3 setzen und gegenprüfen.

Siehe Kapitel 8: Rendering-Pipeline.


Aufgabe 6 (Fortgeschritten) - z-Buffer mit GL_LESS

Der Tiefentest ist mit glEnable(GL_DEPTH_TEST) aktiviert und glDepthFunc(GL_LESS) gesetzt. Der z-Buffer ist an einer bestimmten Pixelzelle bereits mit dem Tiefenwert 55 belegt. Nacheinander treffen an dieser Zelle Fragmente mit den Tiefenwerten 77, dann 33, dann 44 ein. Welchen Wert enthält der z-Buffer am Ende, und welche Farbe ist sichtbar?

Lösung anzeigen

Regel bei GL_LESS: Ein Fragment wird nur übernommen (Buffer aktualisiert, Farbe geschrieben), wenn sein neuer Tiefenwert kleiner ist als der gespeicherte (näher an der Kamera).

Startwert im Buffer: 55.

  1. Fragment z=7z = 7: 7<57 < 5? Nein -> verworfen. Buffer bleibt 55.
  2. Fragment z=3z = 3: 3<53 < 5? Ja -> übernommen. Buffer wird 33, dessen Farbe ist jetzt sichtbar.
  3. Fragment z=4z = 4: 4<34 < 3? Nein -> verworfen. Buffer bleibt 33.

Endstand: z-Buffer =3= 3; sichtbar ist die Farbe des Fragments mit z=3z = 3 (das kameranächste).

Typischer Fehler: z-Vergleich falsch herum. Standardmäßig (GL_LESS) gewinnt der kleinere, nähere z-Wert. Der z-Buffer löst die Sichtbarkeit korrekt und ohne globales Sortieren, unabhängig von der Ankunftsreihenfolge der Fragmente.

Siehe Kapitel 8: Rendering-Pipeline.


Anspruchsvoll

Aufgabe 7 (Anspruchsvoll) - Baryzentrische Interpolation mit gegebenen Gewichten

Für einen Fragmentpunkt im Dreieck sind die baryzentrischen Gewichte λ1=0,2\lambda_1 = 0{,}2, λ2=0,3\lambda_2 = 0{,}3, λ3=0,5\lambda_3 = 0{,}5 bereits bekannt. Die Vertex-Farben sind:

VertexFarbe (RGB)
V1V_1(0,8, 0,4, 0,0)(0{,}8,\ 0{,}4,\ 0{,}0)
V2V_2(0,0, 0,6, 0,2)(0{,}0,\ 0{,}6,\ 0{,}2)
V3V_3(0,4, 0,4, 1,0)(0{,}4,\ 0{,}4,\ 1{,}0)

Berechne die interpolierte RGB-Farbe am Fragmentpunkt.

Lösung anzeigen

Zuerst prüfen: λ1+λ2+λ3=0,2+0,3+0,5=1\lambda_1 + \lambda_2 + \lambda_3 = 0{,}2 + 0{,}3 + 0{,}5 = 1. Gültig.

Die Interpolation erfolgt kanalweise: C=λ1C1+λ2C2+λ3C3C = \lambda_1 C_1 + \lambda_2 C_2 + \lambda_3 C_3.

Rot:

R=0,20,8+0,30,0+0,50,4=0,16+0+0,20=0,36R = 0{,}2\cdot 0{,}8 + 0{,}3\cdot 0{,}0 + 0{,}5\cdot 0{,}4 = 0{,}16 + 0 + 0{,}20 = 0{,}36

Grün:

G=0,20,4+0,30,6+0,50,4=0,08+0,18+0,20=0,46G = 0{,}2\cdot 0{,}4 + 0{,}3\cdot 0{,}6 + 0{,}5\cdot 0{,}4 = 0{,}08 + 0{,}18 + 0{,}20 = 0{,}46

Blau:

B=0,20,0+0,30,2+0,51,0=0+0,06+0,50=0,56B = 0{,}2\cdot 0{,}0 + 0{,}3\cdot 0{,}2 + 0{,}5\cdot 1{,}0 = 0 + 0{,}06 + 0{,}50 = 0{,}56

Ergebnis: C=(0,36, 0,46, 0,56)C = (0{,}36,\ 0{,}46,\ 0{,}56).

Tipp: Jeder interpolierte Kanal muss zwischen dem kleinsten und größten Vertex-Wert dieses Kanals liegen (Konvexkombination). Beispiel Blau: Alle Ergebniskanäle bleiben im Intervall [0,1][0,1], hier liegt B=0,56B = 0{,}56 zwischen 0,00{,}0 und 1,01{,}0. So lassen sich Rechenfehler schnell entdecken.

Typischer Fehler: Die interpolierte Farbe am Ende zusätzlich durch die Summe der Gewichte teilen. Das ist nur nötig, wenn die Gewichte nicht auf 11 normiert sind; hier summieren sie bereits zu 11.

Siehe Kapitel 8: Rendering-Pipeline.


Aufgabe 8 (Anspruchsvoll) - Grenze des Painter's Algorithm

Beim Painter's Algorithm (painter's algorithm / Maleralgorithmus) werden die Polygone in einer bestimmten Reihenfolge gezeichnet, spätere überdecken frühere. Nenne zwei konkrete Situationen, in denen dieses Verfahren ein falsches Bild liefert, und erkläre, warum der z-Buffer sie korrekt löst.

Lösung anzeigen

Situation 1: Ungünstige Renderreihenfolge. Wird ein weit entferntes Polygon zufällig nach einem nahen gezeichnet, überdeckt es dieses fälschlich. Die Darstellung hängt allein von der Reihenfolge der Renderbefehle ab, nicht vom tatsächlichen Abstand zur Kamera.

Situation 2: Sich schneidende Polygone. Zwei Flächen, die sich gegenseitig durchdringen, lassen sich mit einer einzigen Zeichenreihenfolge grundsätzlich nicht korrekt darstellen, weil kein Polygon durchgehend vor dem anderen liegt.

Warum der z-Buffer korrekt löst: Der z-Buffer arbeitet pro Fragment. Für jede Pixelzelle wird der Tiefenwert gespeichert, und ein Fragment gewinnt nur, wenn sein z-Wert kleiner (näher) ist. Dadurch entscheidet der Abstand jedes einzelnen Pixels zur Kamera, nicht die Reihenfolge der Objekte. So entsteht die korrekte Durchdringung auch bei sich schneidenden Polygonen, ganz ohne globales Sortieren.

Tipp: Der Painter's Algorithm greift immer dann, wenn der Tiefentest nicht aktiviert ist (glEnable(GL_DEPTH_TEST) vergessen). Ziel des z-Buffers ist gerade, eine explizite globale Sortierung der Fragmente zu vermeiden.

Siehe Kapitel 8: Rendering-Pipeline.


Klausur-Niveau

Aufgabe 9 (Klausur-Niveau) - Multiple Choice, gemischt

Genau eine Antwort pro Teilfrage ist korrekt.

(a) Welche Shaderstufen sind Pflicht?

  1. Vertex Shader und Geometry Shader
  2. Vertex Shader und Fragment Shader
  3. Tesselation Shader und Fragment Shader
  4. Alle vier Stufen

(b) Bis zu welchem Raum ist die Darstellung auflösungsunabhängig?

  1. Bis World Space
  2. Bis Camera Space
  3. Bis NDC (Einheitswürfel)
  4. Bis Screen Space

(c) Was bewirkt glDepthFunc(GL_LESS)?

  1. Das Fragment mit dem größeren z-Wert gewinnt.
  2. Das Fragment mit dem kleineren (näheren) z-Wert gewinnt.
  3. Der Tiefentest wird deaktiviert.
  4. Alle Fragmente werden immer gezeichnet.

(d) Was ist die Homogenisierung (perspective divide)?

  1. Die Multiplikation mit der ModelMatrix.
  2. Die Division durch die homogene Koordinate ww, die [w,w]3[-w,w]^3 auf [1,1]3[-1,1]^3 abbildet.
  3. Die Abbildung des NDC auf den Screen Space.
  4. Die Interpolation der Vertex-Attribute.
Lösung anzeigen
  • (a) -> 2. Pflicht sind Vertex Shader und Fragment Shader. Tesselation und Geometry Shader sind optional (Tradeoff Speicherplatz versus Performance).
  • (b) -> 3. Bis zum NDC ([1,1]3[-1,1]^3) ist alles auflösungsunabhängig; erst die Viewport-Transformation macht die Ausgabe geräteabhängig.
  • (c) -> 2. GL_LESS (Standard): Der kleinere, nähere z-Wert gewinnt; das Pixel wird nur bei geringerem Tiefenwert aktualisiert.
  • (d) -> 2. Die Homogenisierung ist die Division durch ww und bildet den [w,w]3[-w,w]^3 Würfel auf den [1,1]3[-1,1]^3 Würfel ab. Sie kann automatisch auf der GPU oder manuell im Vertex Shader erfolgen.

Typischer Fehler: Auflösungsunabhängigkeit falsch zuordnen (Antwort b) oder den z-Vergleich umdrehen (Antwort c).

Siehe Kapitel 8: Rendering-Pipeline.


Aufgabe 10 (Klausur-Niveau) - Kante interpolieren und Tiefentest kombinieren

Ein Dreieck hat unter anderem die Kantenendpunkte:

VertexFarbe (RGB)Tiefenwert zz
AA(1,1,0)(1, 1, 0) gelb22
BB(0,1,1)(0, 1, 1) cyan66

(a) Berechne die entlang der Kante ABA \to B linear interpolierte Farbe am Parameter t=12t = \tfrac{1}{2} (also am Mittelpunkt der normierten Strecke).

(b) Ein zweites Objekt liefert an genau dieser Fragmentzelle den Tiefenwert 55. Der Tiefentest läuft mit GL_LESS, der Buffer ist an der Zelle noch auf \infty (unendlich). Beide Objekte, das Kantenfragment aus (a) und das zweite Objekt, treffen ein. Welche Farbe ist am Ende sichtbar?

Hinweis zu (b): Der Tiefenwert des Kantenfragments bei t=12t = \tfrac{1}{2} wird auf gleiche Weise linear interpoliert.

Lösung anzeigen

(a) Lineare Farbinterpolation. Mit F(t)=(1t)F(A)+tF(B)F(t) = (1-t)\,F(A) + t\,F(B) und t=12t = \tfrac{1}{2}:

F ⁣(12)=12(110)+12(011)=(1/211/2)F\!\left(\tfrac{1}{2}\right) = \tfrac{1}{2}\begin{pmatrix} 1 \\ 1 \\ 0 \end{pmatrix} + \tfrac{1}{2}\begin{pmatrix} 0 \\ 1 \\ 1 \end{pmatrix} = \begin{pmatrix} 1/2 \\ 1 \\ 1/2 \end{pmatrix}

Ergebnis: (0,5, 1, 0,5)(0{,}5,\ 1,\ 0{,}5), ein helles Grün.

(b) Tiefentest. Der interpolierte Tiefenwert des Kantenfragments bei t=12t = \tfrac{1}{2}:

z=(112)2+126=1+3=4z = (1-\tfrac{1}{2})\cdot 2 + \tfrac{1}{2}\cdot 6 = 1 + 3 = 4

Ablauf mit GL_LESS, Buffer startet bei \infty:

  • Kantenfragment z=4z = 4: 4<4 < \infty? Ja -> übernommen. Buffer wird 44, sichtbare Farbe ist das helle Grün (0,5,1,0,5)(0{,}5, 1, 0{,}5).
  • Zweites Objekt z=5z = 5: 5<45 < 4? Nein -> verworfen.

(Bei umgekehrter Ankunftsreihenfolge ergäbe sich dasselbe Endbild: Zuerst 5<5 < \infty -> Buffer 55; dann 4<54 < 5 -> Buffer 44. Der z-Buffer ist reihenfolgeunabhängig.)

Endergebnis: Sichtbar ist die interpolierte Kantenfarbe (0,5, 1, 0,5)(0{,}5,\ 1,\ 0{,}5) mit Tiefenwert 44.

Tipp: Farbe, Tiefe und alle weiteren Vertex-Attribute werden mit demselben Interpolationsverfahren behandelt. Der z-Wert entscheidet über Sichtbarkeit, die interpolierte Farbe über das Aussehen des Gewinner-Fragments.

Siehe Kapitel 8: Rendering-Pipeline.