Flat Shadows mit DirectX 8

Von Christian Rogge

Allgemein

Flat-Shadows sind eine Methode um sehr schnellen, dynamischen, aber nicht sehr schönen Schatten zu erzeugen.

Vorteile

  • Sehr schnell
  • Recht einfach zu verstehen und zu implementieren
  • Meistens recht einfach in eine bestehende Engine zu integrieren
  • Braucht keine besondere Hardware (z.B. Stencil-Buffer o. ä.)
  • Braucht keine komplizierten Objekt-Objekt-Beziehungen
  • Gut in der Performance skalierbar.

Nachteile

  • Keine Soft-Shadows (hat also "harte" Kanten)
  • Funktioniert nur auf einer Ebene, passt sich also keiner komplizierten Geometrie an
  • Der Schatten befindet sich immer nur auf einer Ebene

Das Prinzip

Begriffe

Zuerst einige Begriffe (in Klammern das Kürzel für den Begriff):

  • Occluder (OC): Ein Objekt, das den Schatten verursacht. Es befindet sich zwischen einem Licht und dem Receiver.
  • Receiver (RE): bekommt den Schatten. Der RE beim Flat-Shading ist immer eine Ebene.
  • Shadow-Caster (SC): Ein Objekt, das einem Licht entspricht. In einer Engine könnte der SC z. B. die Position eines PointLights repräsentieren. Ich schreibe mit Absicht Shadow-Caster und nicht Light, da es in der Praxis Sinn macht, diese beiden Objekte zu trennen.

So geht's

Situation:

  • 1 SC, das ein Licht repräsentiert.
  • 1 Objekt, das als OC dient.
  • 1 Ebene, die als RE dient.
  1. Man definiert eine Ebene, auf der der Schatten liegen soll. Dies könnte z. B. der Boden bei einem Shooter sein.
  2. Man erstellt eine "ShadowMatrix". Diese Matrix wird die Geometrie des OC so verändern, dass sie in die Ebene "geflatted" wird. Die Matrix ist abhängig von der Position des SC und der Ebene.
  3. Man multipliziert die ShadowMatrix mit der Matrix des OC, also der Matrix, die seine Verschiebung und Verdrehung in der Welt beschreibt. Daraus bekommt man eine Matrix, die beim späteren Rendern gesetzt wird.
  4. Man rendert den Shadow. Dazu setzt man die erzeugte Matrix und rendert die Geometrie des OC. Damit die gerenderte Geometrie wie ein Schatten aussieht, benutzt man das folgende Vertex- Format: D3DFVF_XYZ | D3DFVF_DIFFUSE

Nachfolgend etwas Code zur Verdeutlichung. Den Code habe ich auf die Schnelle aus meiner Engine extrahiert und leicht geändert. Er soll in erster Linie das Prinzip verdeutlichen.

[Quellcode 1]

Tips und Ideen zur Verbesserung

Im Sample wird der Schatten in einen dynamischen Vertexbuffer gerendert. Für nicht animierte Meshes kann man stattdessen einen eigenen statischen VB für die Shadow-Vertices erstellen und diesen rendern.

Man kann auch reduzierte Geometrie für den Schatten nehmen. Es bietet sich ein Mesh in geringerer Auflösung oder auch die Kollisions-Geometrie an.

Wenn ein SC sich nahe an einem OC befindet, kann der Schatten sehr riesig werden. Man kann den Effekt abmildern, indem man "parallelen" Schatten berechnet. Man berechnet den Vektor vom Mittelpunkt des Objektes zur SC-Position und verschiebt den SC entlang dieses Vektors in eine sehr hohe Entfernung.

Beispiel einer angepassten create_shadow_matrix()- Funktion:

[Quellcode 2]