Assignment Chef icon Assignment Chef
All German tutorials

Programming lesson

Normal Mapping und Shadow Mapping in der Computergrafik: Ein Tutorial für CS334/Ece30834

Lerne die Konzepte von Normal Mapping und Shadow Mapping in der Computergrafik anhand eines praxisnahen Assignments. Schritt-für-Schritt-Anleitung zur Implementierung von TBN-Matrizen, Tangentenraum-Beleuchtung und Schattenkarten mit PCF.

Normal Mapping Tutorial Shadow Mapping Implementierung Tangentenraum Shader TBN Matrix berechnen Schattenkarte OpenGL Percentage Closer Filtering Computergrafik Assignment Shader Programmierung lernen Echtzeit Schatten Bump Mapping vs Normal Mapping CS334 Ece30834 Spieleentwicklung Grafik 3D Rendering Techniken OpenGL Schatten Normalmap Shader Code PCF Schatten weichzeichnen

Einführung in Normal Mapping und Shadow Mapping

In der modernen Computergrafik sorgen Normal Mapping und Shadow Mapping für realistischere Oberflächen und Schatten. Dieses Tutorial basiert auf dem Assignment CS334/Ece30834 Assignment #3 – Map It! und erklärt die Implementierung beider Techniken. Du lernst, wie du mit Tangentenraum-Beleuchtung und Schattenkarten die visuelle Qualität deiner 3D-Szenen steigerst – ein Muss für jeden, der sich mit Computergrafik, Shader-Programmierung oder Spieleentwicklung beschäftigt.

Grundlagen: Normal Mapping vs. Bump Mapping

Normal Mapping (auch Bump Mapping) täuscht feine Oberflächendetails vor, indem es die Normalenvektoren pro Pixel manipuliert. Statt jedes Polygon zu unterteilen, speichert eine Normalmap die Abweichungen der Oberflächennormalen im Tangentenraum (TBN-Raum). Dies ermöglicht Echtzeitgrafik mit geringem Speicheraufwand – ähnlich wie bei aktuellen Spielen wie Cyberpunk 2077 oder Fortnite, wo Normalmaps für realistische Texturen sorgen.

Tangentenraum und TBN-Matrix

Der Tangentenraum besteht aus drei Vektoren: Normal (N), Tangente (T) und Bitangente (B). Diese bilden die TBN-Matrix, die Licht- und Blickvektoren vom Weltraum in den lokalen Raum der Oberfläche transformiert. In der Shader-Programmierung (Vertex-Shader) berechnest du diese Matrix pro Vertex:

// Pseudocode für TBN-Matrix im Vertex-Shader
vec3 T = normalize(tangent);
vec3 B = normalize(cross(N, T));
vec3 N = normalize(normal);
mat3 TBN = mat3(T, B, N);
// Transformiere Licht- und Blickvektor in Tangentenraum
lightDir = TBN * lightDir;
viewDir = TBN * viewDir;

Beleuchtungsberechnung im Fragment-Shader

Im Fragment-Shader liest du die Normale aus der Normalmap (texCubeNorm) und kombinierst sie mit den transformierten Vektoren. Das Phong-Beleuchtungsmodell oder Blinn-Phong wird dann im Tangentenraum berechnet. Achte darauf, dass die Normalmap-Werte von [0,1] nach [-1,1] skaliert werden müssen.

Praxistipp: Vergiss nicht, die Tangente und Bitangente aus dem OBJ-Modell zu laden oder zu berechnen. Viele Engines wie Unity oder Unreal erledigen das automatisch, aber im Rahmen dieses Assignments musst du es selbst implementieren.

Shadow Mapping: Schatten aus Lichtperspektive

Shadow Mapping erzeugt Schatten, indem die Szene aus Sicht der Lichtquelle gerendert wird. Die Tiefenwerte werden in einer Schattenkarte (Shadow Map) gespeichert. Im zweiten Pass vergleichst du die Tiefe jedes Fragments mit dem gespeicherten Wert – ist der Fragment tiefer, liegt es im Schatten.

Erster Pass: Tiefenkarte erzeugen

Rendere die Szene mit einem einfachen Tiefenshader (depth_v.glsl und depth_f.glsl). Die Lichtquelle wird als Kamera betrachtet. Die resultierende Tiefenkarte ist eine Textur, die im zweiten Pass verwendet wird.

Zweiter Pass: Schattentest im Fragment-Shader

Implementiere die Funktion calculateShadow:

float calculateShadow(vec4 lightSpaceFragPos, sampler2D shadowMap) {
    // Perspektivische Division
    vec3 projCoords = lightSpaceFragPos.xyz / lightSpaceFragPos.w;
    // Mapping nach [0,1]
    projCoords = projCoords * 0.5 + 0.5;
    // Tiefe des aktuellen Fragments
    float currentDepth = projCoords.z;
    // Nächste Tiefe aus der Schattenkarte
    float closestDepth = texture(shadowMap, projCoords.xy).r;
    // Bias gegen Shadow Acne
    float bias = 0.005;
    return currentDepth - bias > closestDepth ? 1.0 : 0.0;
}

Percentage-Closer Filtering (PCF) für weiche Schatten

PCF glättet harte Schattenkanten, indem mehrere Samples um den Texturkoordinaten gemittelt werden. Ein 3×3-Gitter reduziert Treppeneffekte und sorgt für weiche Schatten – ähnlich wie bei aktuellen Raytracing-Implementierungen in Spielen wie Battlefield 2042.

float pcfShadow(vec4 lightSpaceFragPos, sampler2D shadowMap) {
    vec3 projCoords = ...;
    float shadow = 0.0;
    vec2 texelSize = 1.0 / textureSize(shadowMap, 0);
    for(int x = -1; x <= 1; ++x) {
        for(int y = -1; y <= 1; ++y) {
            vec2 offset = vec2(x, y) * texelSize;
            float pcfDepth = texture(shadowMap, projCoords.xy + offset).r;
            shadow += projCoords.z - bias > pcfDepth ? 1.0 : 0.0;
        }
    }
    return shadow / 9.0;
}

Integration in das Assignment

Das Assignment stellt dir eine Szene mit Kugeln auf einer Ebene bereit. Die Konfigurationsdatei config.txt enthält Rotationsmatrizen und Translationsvektoren. Du musst die TODO-Markierungen im Code ausfüllen. Typische Fehler sind falsche Tangentenberechnung oder fehlende Bias-Werte – teste mit verschiedenen Lichtpositionen (Tastatur/Maus).

Ein aktueller Trend: Neural Rendering und AI-gestützte Texturen nutzen ähnliche Konzepte. In Tools wie Adobe Substance 3D oder NVIDIA Canvas werden Normalmaps automatisch generiert – das Verständnis der zugrunde liegenden Mathematik ist jedoch entscheidend für Spieleentwicklung und VR/AR-Anwendungen.

Häufige Probleme und Lösungen

  • Shadow Acne: Verwende einen Bias-Wert (z.B. 0.005) oder einen adaptiven Bias basierend auf dem Neigungswinkel.
  • Peter Panning: Zu hoher Bias lässt Schatten schweben; reduziere den Bias oder verwende Front-Face-Culling.
  • Normalmap invertiert: Überprüfe die Texturkoordinaten und die Ausrichtung der Tangente.
  • Leistung: PCF mit 3×3 ist akzeptabel; größere Kernel verlangsamen den Shader – verwende optimierte Filter wie Poisson-Disk.

Fazit

Mit Normal Mapping und Shadow Mapping hebst du deine Computergrafik-Projekte auf ein neues Niveau. Dieses Tutorial hat dir die Schritte zur Implementierung im Rahmen des CS334/Ece30834 Assignments gezeigt. Übe mit verschiedenen Szenen und Lichtquellen, um ein Gefühl für die Parameter zu bekommen. Viel Erfolg beim Shader-Programmieren – und denk dran: Nicht bis zur letzten Minute warten!