Tutorial 28 – Deferred Shadow Calculations

Das Shadow Mapping gilt heutzutage als Standardverfahren für die Simulation von Schattenwürfen in einer 3D-Szene. Unabhängig von den Fortschritten, die man bei der Darstellung von Echtzeitschatten erzielt hat, ist die den Schattenberechnungen zugrunde liegende Idee denkbar einfach – man überprüft, ob ein Szenenpixel von einer Lichtquelle aus gesehen sichtbar ist (kein Schatten) oder nicht (Schatten). Damit sich dieser Test durchführen lässt, muss zunächst – im Verlauf eines zusätzlichen Render-Durchgangs – das Tiefenabbild der 3D-Szene aus der Blickrichtung der betreffenden Lichtquelle (Lichtquellen-Perspektive) in einer separaten Textur – der sogenannten Shadow Map – zwischengespeichert werden.

Im Zuge des Deferred Renderings (Lightings) bietet sich jedoch eine modifizierte Form der Schattenberechnung an. Für die Durchführung der Beleuchtungsberechnungen ist es hierbei notwendig, sowohl die Kameraraum-Normalen wie auch die Kameraraum-Positionen der sichtbaren Szenenpixel in separaten Render-Targets (Texturen) zwischenzuspeichern. Speichert man darüber hinaus zusätzlich die Kameraraum-Positionen der Schattenspender-Pixel aus der Blickrichtung einer Lichtquelle in einer Shadow Map, lassen sich die Schattenwürfe wie folgt berechnen:

  • Transformation der Position eines sichtbaren Szenenpixels aus der Kameraperspektive in die Lichtquellen-Perspektive. Als Ergebnis erhält man die Texturkoordinaten, an denen das transformierte Szenenpixel in der Shadow Map zu finden wäre, sofern es nicht durch einen Schattenspender-Pixel verdeckt wird.
  • Die Kameraraum-Position, die bei den zuvor berechneten Texturkoordinaten in der Shadow Map gespeichert ist, dient als Ursprung einer virtuellen Punktlichtquelle, welche das potenziell verdeckte Szenenpixel mit einem sogenannten Negativ-Licht (Antiradiance) beleuchtet (abdunkelt).

Hinweis: Das Negativ-Licht wirkt als Schattenspender. Mit zunehmender Distanz zwischen dem transformierten Szenenpixel und dem Schattenspender-Pixel schwächt sich der Abdunklungseffekt (die Schattenintensität) mehr und mehr ab. Für den Fall, dass ein transformiertes Szenenpixel auch von der Lichtquelle aus gesehen sichtbar ist, wird kein Schatten berechnet.

Im Rahmen des heutigen Tutorials wird die Schattenberechnung sowohl für
direktionale Lichtquellen, wie auch für Spotlights oder omnidirektionale Lichtquellen (Punktlichter) demonstriert.
.
Mithilfe der hier vorgestellten Technik lassen sich die Schatten einer 3D-Szene schrittweise berechnen. Falls gewünscht, kann der Schattenwurf jedes einzelnen 3D-Objekts separat berechnet werden. Für direktionale Lichtquellen wie Sonnenlicht lassen sich auf diese Weise für alle 3D-Objekte hochaufgelöste Schatten berechnen. Für weit von der Kamera entfernt liegende 3D-Objekte können Low-Resolution-Schatten berechnet werden, indem man anstelle eines einzelnen 3D-Objekts mehrere benachbarte 3D-Objekte in eine Shadow Map rendert. Ferner kann man als Entwickler festlegen, ob einfache Schlagschatten dargestellt werden sollen oder rechenintensivere Soft-Shadows.

Aufgaben der CLightViewPositionRendering-Klasse:

  • Handling zweier Shadow Maps zum Speichern der Kameraraum-Positionen der Schattenspender-Pixel aus der Blickrichtung einer Lichtquelle samt der zugehörigen Frame- und Renderbuffer-Objekte. Textur 2 kommt in Verbindung mit omnidirektionalen Lichtquellen (Punktlichtern) zum Einsatz, zum Speichern eines 360°-Szenenabbilds (dual paraboloide Projektion).
  • Festlegung aller Eigenschaften einer Lichtquelle (Set_SpotLightProperties(), Set_OmniDirectionalLightProperties(), Set_DirectionalLightProperties()). Unterstützt werden direktionale Lichtquellen, Spotlights sowie Punktlichter.
  • Methoden zum Setzen der einzelnen Render Targets (Begin_LightViewPositionRendering_Texture1(), Continue_LightViewPositionRendering_Texture1(), Begin_LightViewPositionRendering_Texture2(), Continue_LightViewPositionRendering_Texture2() sowie Stop_LightViewPositionRendering()).


Für die Berechnung der Schatten stellt die CPostProcessingEffects-Klasse die folgenden zwei Methoden zur Verfügung: Calculate_Shadows() sowie Calculate_DualParaboloidShadows(). Beide Methoden erwarten als Parameter die gültige Adresse einer CLightViewPositionRendering-Instanz, die maximale Reichweite des Schattenwurfs, die Intensität des Schattens sowie einen Offset-Wert zur Minimierung von möglichen Schattenartefakten. Das Ergebnis der Berechnungen wird in einer Schattentextur zwischengespeichert.

Nachdem alle zu berücksichtigenden Schatten (welche Objekte einen Schatten werfen sollen, liegt im Ermessen des Entwicklers) in der Schattentextur zwischengespeichert worden sind, muss die CPostProcessingEffects-Methode Finish_ShadowCalculations() aufgerufen werden. Hierbei erfolgt die Verrechnung der Schattentextur mit der Szenentextur sowie falls gewünscht die Soft-Shadow-Berechnung.



Hinweise zum Erstellen eines neuen Projekts:

  • Kopieren Sie den Ordner GraphicsAndPhysicsFrameworkImports ins Projektverzeichnis
  • Kopieren sie alle dll-Dateien sowie die Konfigurationsdatei ResolutionAndRendering.txt aus besagtem Ordner ins gleiche Verzeichnis, in dem sich auch die exe-Datei befindet (in unseren Programmbeispielen ist dies das Bin-Verzeichnis)
  • Binden Sie die folgenden Dateien in Ihr Projekt ein: GraphicsAndPhysics_Framework_Imports.h, GraphicsAndPhysics_Framework_Imports.lib, glew32.lib, glew32s.lib, glut32.lib. Die Glew- und Glut-Bibliotheken ermöglichen die Nutzung der aktuellen OpenGL-Spezifikationen unabhängig vom Framework.

GraphicsAndPhysicsFrameworkDemo28.zip