Es gibt im Allgemeinen zwei Methoden, um damit umzugehen. Heutzutage werden sie Forward-Rendering und Deferred-Rendering genannt. Es gibt eine Variation dieser beiden, die ich unten diskutieren werde.
Forward-Rendering
Rendern Sie jedes Objekt einmal für jedes Licht, das es beeinflusst. Dies schließt das Umgebungslicht ein. Wenn Sie einen additiven Mischmodus ( glBlendFunc(GL_ONE, GL_ONE)
) verwenden, werden die Beiträge der einzelnen Lichtquellen zueinander addiert. Da der Beitrag verschiedener Lichter additiv ist, erhält der Framebuffer schließlich den Wert
Sie können HDR erhalten, indem Sie in einen Gleitkomma-Framebuffer rendern. Anschließend gehen Sie die Szene abschließend durch, um die HDR-Beleuchtungswerte auf einen sichtbaren Bereich zu verkleinern. Hier können Sie auch Bloom und andere Post-Effekte implementieren.
Eine übliche Leistungsverbesserung für diese Technik (wenn die Szene viele Objekte enthält) ist die Verwendung eines "Pre-Pass", bei dem Sie alle Objekte rendern, ohne etwas in den Farbrahmenpuffer zu zeichnen ( glColorMask
zum Deaktivieren von Farbschreibvorgängen). Dies füllt nur den Tiefenpuffer. Auf diese Weise kann die GPU diese Fragmente schnell überspringen, wenn Sie ein Objekt rendern, das sich hinter einem anderen befindet. Der Vertex-Shader muss noch ausgeführt werden, die normalerweise teureren Fragment-Shader-Berechnungen können jedoch übersprungen werden.
Dies ist einfacher zu codieren und einfacher zu visualisieren. Bei einigen Hardwarekomponenten (hauptsächlich Mobil- und Embedded-GPUs) kann dies effizienter sein als die Alternative. Aber auf High-End-Hardware gewinnt die Alternative im Allgemeinen für Szenen mit viel Licht.
Verzögertes Rendern
Das verzögerte Rendern ist etwas komplizierter.
Die Beleuchtungsgleichung, mit der Sie das Licht für einen Punkt auf einer Oberfläche berechnen, verwendet die folgenden Oberflächenparameter:
- Oberflächenposition
- Oberflächennormalen
- Diffuse Oberflächenfarbe
- Spiegelnde Oberflächenfarbe
- Oberflächenglanz spiegelnd
- Möglicherweise andere Oberflächenparameter (abhängig von der Komplexität Ihrer Beleuchtungsgleichung)
Beim Forward-Rendering werden diese Parameter entweder direkt vom Vertex-Shader an die Beleuchtungsfunktion des Fragment-Shaders übergeben, aus Texturen gezogen (normalerweise über Texturkoordinaten, die vom Vertex-Shader übergeben wurden) oder aus dem gesamten Stoff im Fragment-Shader basierend auf generiert andere Parameter. Die diffuse Farbe kann berechnet werden, indem eine Per-Vertex-Farbe mit einer Textur kombiniert wird, wobei mehrere Texturen kombiniert werden, was auch immer.
Bei verzögerter Wiedergabe machen wir dies alles explizit. Im ersten Durchgang rendern wir alle Objekte. Wir rendern aber keine Farben . Stattdessen rendern wir Oberflächenparameter . So hat jedes Pixel auf dem Bildschirm eine Reihe von Oberflächenparametern. Dies erfolgt über das Rendern von Off-Screen-Texturen. Eine Textur würde die diffuse Farbe als RGB und möglicherweise den spiegelnden Glanz als Alpha speichern. Eine andere Textur würde die spiegelnde Farbe speichern. Ein Drittel würde das normale speichern. Und so weiter.
Die Position wird normalerweise nicht gespeichert. Es wird stattdessen im zweiten Durchgang durch Mathematik rekonstruiert, was zu komplex ist, um hier darauf einzugehen. Es genügt zu sagen, dass wir den Tiefenpuffer und die Fragmentposition des Bildschirmbereichs als Eingabe verwenden, um die Position des Kameraraums des Punkts auf einer Oberfläche zu ermitteln.
Jetzt, da diese Texturen im Wesentlichen alle Oberflächeninformationen für jedes sichtbare Pixel in der Szene enthalten, rendern wir Vollbild-Quads. Jedes Licht erhält einen Vollbild-Quad-Render. Wir probieren aus den Oberflächenparametertexturen (und stellen die Position wieder her) und verwenden sie dann nur, um den Beitrag dieses Lichts zu berechnen. Dies wird glBlendFunc(GL_ONE, GL_ONE)
dem Bild (erneut ) hinzugefügt . Wir machen das so lange, bis uns die Lichter ausgehen.
HDR ist wieder ein Nachbearbeitungsschritt.
Der größte Nachteil des verzögerten Renderns ist das Antialiasing. Es erfordert ein bisschen mehr Arbeit, um Antialias richtig auszuführen.
Der größte Vorteil ist die Leistung, wenn Ihre GPU über eine große Speicherbandbreite verfügt. Wir rendern die tatsächliche Geometrie nur einmal (oder 1 + 1 pro Licht mit Schatten, wenn wir eine Schattenzuordnung durchführen). Wir nie verbringen jederzeit auf versteckte Pixel oder Geometrie , die nicht sichtbar nach dieser ist. Die gesamte Beleuchtungsdauer wird für Dinge aufgewendet, die tatsächlich sichtbar sind.
Wenn Ihre GPU nicht über viel Speicherbandbreite verfügt, kann der Lichtdurchgang wirklich weh tun. Das Ziehen von 3-5 Texturen pro Bildschirmpixel macht keinen Spaß.
Leichter Pre-Pass
Dies ist eine Art Variation des verzögerten Renderns, die interessante Kompromisse aufweist.
Genau wie beim verzögerten Rendern rendern Sie Ihre Oberflächenparameter in einer Reihe von Puffern. Sie haben jedoch Oberflächendaten abgekürzt. Die einzigen Oberflächendaten, die Sie für diese Zeit interessieren, sind der Tiefenpufferwert (zum Rekonstruieren der Position), der Normalwert und der Glanz der Spiegel.
Dann berechnen Sie für jedes Licht nur die Beleuchtungsergebnisse. Keine Multiplikation mit Oberflächenfarben, nichts. Nur der Punkt (N, L) und der spiegelnde Term, ganz ohne die Oberflächenfarben. Die spiegelnden und diffusen Terme sollten in getrennten Puffern aufbewahrt werden. Die spiegelnden und diffusen Terme für jedes Licht werden in den beiden Puffern summiert.
Anschließend rendern Sie die Geometrie neu. Verwenden Sie dabei die Berechnungen für die gesamte spiegelnde und diffuse Beleuchtung, um die endgültige Kombination mit der Oberflächenfarbe zu erzielen und so den Gesamtreflexionsgrad zu erzielen.
Die Vorteile hier sind, dass Sie Multisampling zurückbekommen (zumindest einfacher als mit verzögert). Sie rendern weniger pro Objekt als vorwärts. Die Hauptsache ist jedoch, dass dies eine einfachere Zeit ist, um verschiedene Beleuchtungsgleichungen für verschiedene Oberflächen zu haben.
Beim verzögerten Rendern wird im Allgemeinen die gesamte Szene mit demselben Shader pro Licht gezeichnet. Daher muss jedes Objekt dieselben Materialparameter verwenden. Mit dem Lichtvorübergang können Sie jedem Objekt einen anderen Shader zuweisen, sodass der letzte Beleuchtungsschritt für sich alleine ausgeführt werden kann.
Dies bietet nicht so viel Freiheit wie der Forward-Rendering-Fall. Es ist aber immer noch schneller, wenn Sie die Texturbandbreite zur Verfügung haben.