Kapitel 8: Rendering-Pipeline
Die vollständige Kette von 3D-Geometrie zum fertigen 2D-Bild: die Shaderstufen, die Koordinatenräume (Local -> World -> Camera -> Clip -> NDC), die Viewport-Transformation in Fensterkoordinaten, die Rasterisierung mit (bi)linearer Attribut-Interpolation und der Tiefentest (Painter's Algorithm vs. z-Buffer).
Überblick
Die Rendering-Pipeline (rendering pipeline) ist die feste Abfolge von Verarbeitungsschritten, die aus Rohdaten (Vertices und Primitive) ein 2D-Array von Farbwerten auf dem Bildschirm erzeugt. Die Hauptblöcke in Reihenfolge:
Roter Faden:
- Manche Schritte laufen automatisch auf der GPU, andere programmiert man selbst (Vertex Shader, Fragment Shader) oder aktiviert sie per OpenGL-Befehl.
- Die Geometrie durchläuft eine Kette von Koordinatenräumen, jeweils per Matrixmultiplikation.
- Bis zum Normalized Device Space ist alles auflösungsunabhängig; erst die Viewport-Transformation macht die Ausgabe geräteabhängig.
- Der z-Buffer löst die Sichtbarkeit korrekt und ohne globales Sortieren; er ist dem reihenfolgeabhängigen Painter's Algorithm überlegen.
Shaderpipeline (Wiederholung)
Shader (shader) sind kleine Programmfragmente, die direkt auf der GPU ausgeführt werden und die hochgeladenen Daten vor bzw. während des Renderns manipulieren. Geschrieben werden sie in GLSL (OpenGL Shading Language). Alternativen: HLSL (nur Windows/DirectX) und Cg (von NVidia, 2012 eingestellt).
Die vier Shaderstufen in Reihe:
- Vertex Shader (vertex shader) - Pflicht, selbst implementierbar. Berechnet die finale Position jedes Vertex im Ausgabebild (Model/View/Projection-Transformationen, optional die Homogenisierung).
- Tesselation Shader (tessellation shader) - optional. Zerlegt Objekte in feinere Dreiecksnetze (Verfeinerung der Geometrie).
- Geometry Shader (geometry shader) - optional. Verändert die Geometrie-Daten zur Laufzeit.
- Fragment Shader (fragment shader) - Pflicht, selbst implementierbar. Berechnet die Farbe jedes Fragments (genauer: jedes Fragments, nicht direkt jedes Pixels).
Merke: Der Einsatz der optionalen Shader ist ein Tradeoff Speicherplatz versus Performance (trade-off storage vs. performance). Pflicht sind nur Vertex und Fragment Shader.
Fragment vs. Pixel
Ein Pixel (pixel) ist ein Bildschirmelement. Ein Fragment (fragment) ist die Vorstufe eines Pixels. Diese Unterscheidung ist prüfungsrelevant:
- Mehrere Fragmente können ein Pixel ergeben (Transparenz).
- Nicht alle Fragmente werden zu einem Pixel: der Tiefentest (z-Buffer) verwirft verdeckte Fragmente.
Koordinatenräume
Die Vertexdaten durchlaufen eine Kette von Koordinatensystemen (KS = Koordinatensystem), jeweils durch eine Matrixmultiplikation überführt:
| Raum | Beschreibung | Übergang per |
|---|---|---|
| Local Space | Vertexdaten im lokalen KS des jeweiligen Objektes | ModelMatrix |
| World Space | Alle Objekte in einem globalen KS platziert | ViewMatrix |
| Camera Space (View Space) | Kamera im Ursprung, Blick entlang der negativen z-Achse | ProjectionMatrix |
| Clipping Space | Perspektivische oder orthographische Projektion in den Würfel | Homogenisierung |
| Normalized Device Space (NDC) | Normalisierte Darstellung im Würfel | Viewport Transformation |
Die Homogenisierung (homogenization / perspective divide) ist die Division durch die homogene Koordinate , die den Würfel auf den Würfel abbildet.
Wichtig (Grenze der Berechnung): Local -> Clipping Space werden selbst im Vertex Shader berechnet; ab der Homogenisierung/NDC laufen die Berechnungen automatisch auf der Grafikkarte. Die Homogenisierung kann aber auch manuell im Vertex Shader vorgenommen werden.
Viewport-Transformation
Die Viewport-Transformation (viewport transformation) bildet den Normalized Device Space auf den Screen Space (Fensterkoordinaten) ab. Sie hängt von der Bildschirmauflösung (Breite , Höhe ) ab und ist damit nicht auflösungsunabhängig (abhängig vom Ausgabemedium: Laptop, HD Monitor, Smartphone).
Für einen NDC-Punkt in ergibt sich der Bildpunkt :
Die - und -Formeln skalieren und verschieben vom Bereich nach bzw. ; der Tiefenwert wird auf abgebildet.
Hinweis (mögliche Unstimmigkeit): Die Folie nennt zusätzlich Pixelmittelpunkte wie unten links und oben rechts. Die oben angegebene Grundformel liefert an den NDC-Rändern exakt bzw. (nicht bzw. ); die Pixelmittelpunkt-Angaben beschreiben die diskreten Rasterzentren, nicht das Ergebnis der reinen Skalierungsformel. Beide Darstellungen stehen auf der Folie nebeneinander; in einer Klausur genügt in der Regel die Grundformel.
Rasterisierung
Die Rasterisierung (rasterization) wandelt die Vektorgeometrie (Dreiecke) in diskrete Fragmente auf dem Pixelraster um. Sie läuft automatisch auf der Grafikkarte in drei Schritten:
- Bestimmung der Pixel-Koordinaten der Vertices (V1, V2, V3).
- Interpolation der Vertex-Attribute entlang der Dreieckskanten. Die Strecke zwischen zwei Vertices wird zunächst normiert; ein Parameter läuft von bis entlang der normierten Strecke, danach erfolgt die lineare Interpolation abhängig von .
- Interpolation der Pixelwerte entlang der einzelnen Rasterzeilen (Scanlines): horizontales Ausfüllen zwischen den interpolierten Kantenwerten.
Interpolation der Vertex-Attribute
Attribute (Farbe, Normalen, Texturkoordinaten etc.), die an den Vertices definiert sind, werden während der Rasterisierung pro Fragment interpoliert. Konkret werden die Ausgaben des Vertex Shaders zu Eingaben des Fragment Shaders, wobei die Werte automatisch interpoliert übergeben werden:
// Vertex Shader
out vec3 color; // Ausgabe pro Vertex
// Fragment Shader
in vec3 color; // kommt automatisch interpoliert an
(Bi)lineare Farbinterpolation
- Lineare Interpolation (linear interpolation): Verfahren für Linien (1D entlang einer Strecke).
- Bilineare Interpolation (bilinear interpolation): Verfahren für Flächen (2D, z.B. innerhalb eines Dreiecks).
Die lineare Farbinterpolation mit Parameter :
Komponentenweise für eine RGB-Farbe:
Merke: Der Gewichtungsfaktor von ist , nicht . Bei ergibt sich , bei ergibt sich .
Worked Example: Lineare Farbinterpolation bei
Gegeben (rot) und (blau), Parameter :
Ergebnis: eine überwiegend rötliche Mischfarbe mit leichtem Blauanteil. Auf genau diese Weise werden alle Vertex-Attribute interpoliert (Vertex-Shader out wird interpoliert zu Fragment-Shader in).
Tiefentest
Der Tiefentest (depth test) bestimmt die Sichtbarkeit der Fragmente. Ziel: eine explizite globale Sortierung der Fragmente nach Tiefenwert soll vermieden werden.
Painter's Algorithm
Der Painter's Algorithm (painter's algorithm / Maleralgorithmus) ist das naive Verfahren: später gezeichnete Objekte überdecken frühere, wie Farbe auf einer Leinwand. Damit hängt die Darstellung von der Reihenfolge der Renderbefehle ab.
- Nachteil: falsche Verdeckung bei ungünstiger Reihenfolge.
- Kann sich schneidende Polygone nicht korrekt darstellen.
- Wird verwendet, wenn der Tiefentest nicht aktiviert ist.
Besser: Verdeckung abhängig vom Abstand des jeweiligen Pixels zur Kamera.
z-Buffer
Beim z-Buffer (z-buffer / Tiefenpuffer) wird pro Fragment die Entfernung zur Kamera (= z-Koordinate, der Tiefenwert) gespeichert. Ein Pixel wird nur aktualisiert, wenn der neue Tiefenwert geringer ist (näher an der Kamera). Das löst das Sichtbarkeitsproblem korrekt und ohne globales Sortieren, auch bei sich schneidenden Polygonen.
Worked Example: z-Buffer bei sich schneidenden Polygonen
Ausgangslage: der Puffer ist überall auf unendlich () gesetzt.
- Das erste (graue) Polygon schreibt in seine Fragmente konstant den Tiefenwert .
- Das zweite (gelbe) Polygon hat Verlaufstiefen von bis . In jeder Zelle wird der kleinere Wert übernommen:
- Wo Gelb oder hat, bleibt der Wert (Grau ist näher).
- Wo Gelb , oder hat, wird der Wert übernommen (Gelb ist näher).
So entsteht die korrekte Durchdringung der beiden Flächen ohne globale Sortierung; pro Zelle gewinnt der minimale z-Wert und bestimmt die Farbe.
Tiefentest in OpenGL
Der Tiefentest wird per glEnable(GL_DEPTH_TEST) aktiviert; die Vergleichsfunktion wählt man mit glDepthFunc(function). Getestet werden immer die z-Werte der Fragmente.
glEnable(GL_DEPTH_TEST); // Tiefentest aktivieren
glDepthFunc(GL_LESS); // Standard: naeherer (kleinerer) z-Wert gewinnt
Mögliche Funktionen: GL_LESS (Standard), GL_GREATER, GL_NEVER, GL_ALWAYS, GL_EQUAL.
Hinweis (Optimierung): Als Optimierung kann der Tiefentest u.U. vor dem Fragment Shader durchgeführt werden (Early-Z), um verdeckte Fragmente gar nicht erst einzufärben.
Prüfungsrelevanz
- Reihenfolge und Rolle der Pipeline-Stufen benennen: Vertex Processor -> Rasterizer -> Fragment Processor -> Output Merging.
- Vier Shaderstufen: welche Pflicht (Vertex, Fragment), welche optional (Tesselation, Geometry), und deren Aufgaben.
- Koordinatenräume + zugehörige Matrizen in richtiger Reihenfolge (ModelMatrix, ViewMatrix, ProjectionMatrix, Homogenisierung) und die Grenze zwischen "im Vertex Shader" und "automatisch auf der GPU".
- Viewport-Transformationsformeln anwenden (); Bewusstsein für Auflösungsabhängigkeit.
- Lineare Farbinterpolation berechnen (Formel + RGB-Vektor, wie im Beispiel ).
- Painter's Algorithm vs. z-Buffer: Nachteil des Painter's Algorithm (reihenfolgeabhängig, versagt bei sich schneidenden Polygonen); z-Buffer-Regel (Update, wenn neuer Tiefenwert geringer).
- OpenGL-Befehle:
glEnable(GL_DEPTH_TEST)undglDepthFunc(...)mit den möglichen Funktionen; StandardGL_LESS.
Typische Fehler
- Fragment und Pixel verwechseln. Ein Fragment ist die Vorstufe eines Pixels; nicht jedes Fragment wird zum Pixel (z-Buffer verwirft), und mehrere Fragmente können zu einem Pixel beitragen (Transparenz).
- Kamerablickrichtung falsch angeben. Die Kamera schaut im Camera Space entlang der negativen z-Achse.
- z-Buffer-Vergleich falsch herum. Standardmäßig gewinnt der kleinere (nähere) z-Wert (
GL_LESS); das Pixel wird aktualisiert, wenn der neue Tiefenwert geringer ist. glEnable(GL_DEPTH_TEST)vergessen. Ohne diese Zeile wird der Painter's Algorithm verwendet (falsche Verdeckung).- Auflösungsunabhängigkeit falsch zuordnen. Die Szene ist bis zum NDC/Einheitswürfel auflösungsunabhängig; erst die Viewport-Transformation macht sie geräteabhängig.
- Lineare vs. bilineare Interpolation vertauschen. Linear = Linien (1D), bilinear = Flächen (2D).
- Homogenisierung als reinen GPU-Schritt ansehen. Sie kann auch manuell im Vertex Shader durchgeführt werden.
- Interpolationsvorzeichen. In ist der Gewichtungsfaktor von gleich , nicht .
Glossar
| Deutsch | English |
|---|---|
| Rendering-Pipeline | rendering pipeline |
| Shader | shader (GLSL; Alternativen HLSL, Cg) |
| Vertex Shader | vertex shader (berechnet finale Vertex-Position) |
| Tesselation Shader | tessellation shader (optional) |
| Geometry Shader | geometry shader (optional) |
| Fragment Shader | fragment shader (berechnet Fragmentfarbe) |
| Fragment | fragment (Vorstufe eines Pixels) |
| Pixel | pixel (Bildschirmelement) |
| Vertex Processor / Rasterizer / Fragment Processor / Output Merging | Hauptblöcke der Pipeline |
| Koordinatensystem (KS) | coordinate system |
| Local Space | local space (lokales Objekt-KS) |
| World Space | world space (globales KS) |
| Camera Space / View Space | camera space (Kamera im Ursprung, Blick entlang -z) |
| Clipping Space | clip space ( nach Projektion) |
| Normalized Device Space (NDC) | normalized device space () |
| Screen Space | screen space () |
| ModelMatrix | model matrix (Local -> World) |
| ViewMatrix | view matrix (World -> Camera) |
| ProjectionMatrix | projection matrix (Camera -> Clipping) |
| Homogenisierung | homogenization / perspective divide |
| Viewport-Transformation | viewport transformation (NDC -> Screen Space) |
| Rasterisierung | rasterization (Dreiecke -> Fragmente) |
| Interpolation der Vertex-Attribute | vertex attribute interpolation |
| Lineare Interpolation | linear interpolation (Linien) |
| Bilineare Interpolation | bilinear interpolation (Flächen) |
| Rasterzeile | scanline |
| Tiefentest | depth test |
| Painter's Algorithm | painter's algorithm / Maleralgorithmus |
| Z-Buffer / Tiefenpuffer | z-buffer / depth buffer |
| Tiefenwert | depth value (z-Koordinate, Abstand zur Kamera) |
| Bildebene | image plane |
| Early-Z | Tiefentest u.U. vor dem Fragment Shader |
| Auflösungsunabhängig | resolution-independent (gilt bis NDC/Einheitswürfel) |