Volumetrische Stencilschatten

Aus DGL Wiki
Wechseln zu: Navigation, Suche
Hinweis: Dieser Artikel wurde von den Benutzern des Wikis zum exzellenten Artikel berufen!
(weitere exzellente Artikel)
Excelent.jpg

Konzept

Bei dieser Technik nutzt man den (seit ~2000 von fast allen Grafikkarten angebotenen) Stencilpuffer um dort die im Schatten liegenden Bereiche der Szene abzulegen. Später werden dann mit einem passenden Stencilvergleich die Schatten dargestellt.

Dazu wird für jedes Objekt ausgehend von seiner Silhouette (es entsteht immer dort eine Silhouetten-Kante, wo ein dem Licht abgewandtes und ein dem Licht zugewandtes Dreieck sich treffen) durch extrudieren ihrer Eckpunkte (im Idealfall in die Unendlichkeit, am einfachsten lösbar über die W-Koordinate) ein Volumen erstellt das den Bereich des Objektes darstellt den es schattiert.

Geschichte

1977 - Erfunden von Crow : Software-Renderer
1984 - Brotman und Badler : Software-Tiefenpuffer und viele Punktlichter für weiche Schatten
1985 - Fuchs und Co. : Erste Hardwareimplementation und Volumentechnik statt Raytracing
1986 - Bergeron : Behandlung offener Modelle, Nicht-Planare Polygone
1988 - Forunier & Fussell : Theorie zum Zählen der Schattenvolumen in einem Puffer
1991 - Heidmann : Implementation in IRIS GL über Stencilpuffer
1992 - Akeley & Foren : Der für IRIS GL entwickelte Stencilpuffer wird patentiert und in OpenGL 1.0 implementiert
1996 - Deifenbach : Multi-Pass Shattenvolumen
1999 - Dietrich : ZFail-Methode vorgestellt
1999 - Kilgard : Invertierter Algorithmus für planare Ausschnitte
2000 - Carmack : Erste detaillierte Diskussion zur Gleichheit von ZPass und ZFail
2001 - Kilgard : ZPass mit Capping vorgestellt
2002 - Everitt & Kilgard : Mehrere Techniken für eine robuste Lösung

(Teilweise entnommen aus dem Dokument "Optimized Stencil Shadow Volumes" von Cass Everitt & Mark J. Kilgard)

Zukunft

Zumindest für die in der Spieleindustrie treibenden "Kraft" in Sachen OpenGL, John Carmack (id Software), gehört die Zukunft klar den Schatten Volumen. Zumal die zwei frappierendsten aktuellen Nachteile (Ermitteln der Silhouette und Extrudieren eben dieser für das Volumen, sowie die hohe Anforderung an die Füllrate) dank schneller werdender Grafikkarten und CPUs immer weiter in den Hintergrund rücken. So kann man inzwischen dank Vertexprogrammen alle Eckpunkte der Silhouette einfach auf der GPU extrudieren. Auch die Tatsache, dass diese Technik nur sehr scharfkantige Schatten wirft, lässt sich via Jittering (mehrfachem, versetzem Rendern der Schattenvolumen mit unterschiedlicher Helligkeit) spätestens dann realisieren, wenn Grafikkarten genug Füllrate anbieten, und eben genau diese steigt von Generation zu Generation recht stetig an.

Grundlegende Funktionsweise

Um eine Szene mit volumetrischen Stencilschatten zu rendern sieht der Renderablauf im Normalfall wie folgt aus :

  1. Szene nur mit ambientem Lichtanteil in den Farbpuffer rendern
  2. Silhouette zwischen Objekten und Lichtquellen errechnen
  3. Silhouetten extrudieren (und je nach Technik noch Deckel für das Volumen erstellen)
  4. Stenciltest aktivieren und erstellte Schattenvolumen dort hinein rendern (mit unterschiedlichen Stenciloperationen für Vorder- und Rückseite)
  5. Szene mit diffusem Lichtanteil rendern (da Schatten im Stencilpuffer liegen wird die Szene jetzt schattiert)

Methoden

zPass

Bei dieser (zuerst entwickelten) Methode inkrementieren nach vorne zeigende Dreiecke den Wert im Stencilpuffer, wenn diese den Test passieren; und nach hinten zeigende Dreiecke dekrementieren diesen Wert. Nachteil dieser Methode ist jedoch die Tatsache das es zu Fehlern beim Zählen der Schattenwerte im Stencilpuffer kommt, sobald der Betrachter in ein Schattenvolumen eintritt. Um dies zu lösen gibt es jedoch die zFail-Methode.

 glCullFace(GL_FRONT);
 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP);
 RenderShadowVolumes;

 glCullFace(GL_BACK);
 glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP);
 RenderShadowVolumes;

zFail

Die von John Carmack im Jahre 2000 "erfundene" Methode zählt die Schattenfragmente etwas anders : Für nach vorne zeigende Dreiecke wird der Wert im Stencilpuffer inkrementiert, wenn diese den Test nicht bestehen (daher der Name); und dekrementiert für nach hinten zeigende Dreiecke. Weiterhin muss man bei dieser Methode (damit die Fragmente korrekt gezählt werden) ein geschlossenes Schattenvolumen erstellen, das neben der extrudierten (meist in die Unendlichkeit) Silhouette des schattenwerfenden Objektes sowohl einen vorderen Deckel (Front-Cap) als auch einen abschliessenden (Back-Cap) hat. Der große Vorteil (auf den man in den seltensten Fällen verzichten) kann ist hier die Tatsache das zFail auch dann korrekte Schatten darstellt wenn der Betrachter in das Volumen eindringt. Nachteilig ist aber das man bedingt durch Deckel und Boden für die Schattenvolumen mehr Berechnungen und Pixeltests durchführen muss. Im Normalfall findet man deshalb eine Kombination aus beiden Techniken in einer Anwendung (zPass generell, wenn Betrachter in einem Volumen ist, dann zFail).

glCullFace(GL_FRONT);
glStencilOp(GL_KEEP, GL_INCR_WRAP, GL_KEEP);
RenderShadowVolumes;

glCullFace(GL_BACK);
glStencilOp(GL_KEEP, GL_DECR_WRAP, GL_KEEP);
RenderShadowVolumes;

Vor- und Nachteile

Positiv :

  • Sehr hohe Details (kein Aliasing)
  • Selbstschattierung für schattenwerfende Objekte

Negativ :

  • Benötigt Kenntnis über die zugrundeliegende Geometrie (für die Schattensilhouette)
  • Silhouette muss auf der CPU errechnet werden (->CPU-Lastig)
  • Verbraucht sehr viel Füllrate

Beispiel

Shadowvolume scene.jpg
Zu sehen ist hier eine Szene die mittels volumetrischer Stencilschatten schattiert wurde. Die roten Umrandungen stellen die Silhouette der Szene ggü. der Lichtquelle dar, während die blauen Linien die Schattenvolumen darstellen (extrudiert in die Unendlichkeit).

Zweiseitiger Stenciltest

Die Performance kann mithilfe eines zweiseitigen Stenciltests verbessert werden, welcher vor OpenGL 2.0 mit der Extension GL_EXT_stencil_two_side bzw. GL_ATI_separate_stencil zur Verfügung stand. Ab OpenGL 2.0 wurde GL_ATI_separate_stencil in den Kern übernommen und man kann von nun an mit glStencilOpSeparate für Front- und Backface-Polygone gleichzeitig verschiedene Operationen angeben. Somit lässt sich der Algorithmus in einem einzelnen Pass realisieren.

Beispiel glActiveStencilFaceEXT

Hier ein Beispiel, wie man Stencilschatten in einem Pass mithilfe der Extensions GL_EXT_stencil_two_side und GL_EXT_stencil_wrap umsetzt (zPass) :

glDepthMask(0);
glColorMask(0,0,0,0);
glDisable(GL_CULL_FACE);
glEnable(GL_STENCIL_TEST);
glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);

glActiveStencilFaceEXT(GL_BACK);
glStencilOp(GL_KEEP,            // stencil test fail
            GL_KEEP,            // depth test fail
            GL_DECR_WRAP_EXT);  // depth test pass
glStencilMask(~0);
glStencilFunc(GL_ALWAYS, 0, ~0);

glActiveStencilFaceEXT(GL_FRONT);
glStencilOp(GL_KEEP,            // stencil test fail
            GL_KEEP,            // depth test fail
            GL_INCR_WRAP_EXT);  // depth test pass
glStencilMask(~0);
glStencilFunc(GL_ALWAYS, 0, ~0);

renderShadowVolumePolygons();

Ressourcen