Was ist die beste Methode, um Shader-Uniformen zu aktualisieren?

10

Was ist der am meisten akzeptierte Weg, um die Matrizen eines Shaders auf dem neuesten Stand zu halten, und warum?

Im Moment habe ich zum Beispiel eine ShaderKlasse, die die Handles im GLSL-Shader-Programm und in den Uniformen speichert. Jedes Mal, wenn ich die Kamera bewege, muss ich die neue Ansichtsmatrix an den Shader übergeben, und jedes andere Weltobjekt muss ich seine Modellmatrix an den Shader übergeben.

Dies schränkt mich stark ein, da ich nichts tun kann, ohne Zugriff auf dieses Shader-Objekt zu haben.

Ich dachte daran, eine Singleton- ShaderManagerKlasse zu erstellen , die für das Halten aller aktiven Shader verantwortlich ist. Ich kann dann von überall darauf zugreifen und ein Weltobjekt müsste nicht wissen, welche Shader aktiv sind, nur dass es ShaderManagerdie gewünschten Matrizen wissen lassen muss, aber ich bin nicht sicher, ob dies der beste Weg ist und es gibt wahrscheinlich einige Probleme, die auftreten wird sich aus diesem Ansatz ergeben.

Lerp
quelle
Warum nicht die Daten an die Shader weitergeben, wenn es Zeit zum Rendern ist?
Nick Caplinger
Das habe ich vor, aber wie? Wenn ein Objekt einen Shader erstellt, wie sollen dann Weltobjekte über die Existenz dieses Shaders Bescheid wissen und ihm die erforderlichen Matrizen übergeben?
Lerp

Antworten:

11

Verwenden Sie einheitliche Puffer (dh konstante Puffer im D3D-Jargon).

Solange sich alle Ihre Shader auf das Layout und den Bindungspunkt jedes solchen Puffers einigen, wird das Aktualisieren zum Kinderspiel. Modelle müssen überhaupt nichts über Shader wissen. Sie müssen nur die Modellansichtsmatrix in ihrem konstanten Puffer aktualisieren, und die Rendering-Pipeline verwendet sie automatisch.

Zumindest würde ich argumentieren, dass Sie zwei solche Puffer haben sollten. Die erste sollte Ihre Projektionsmatrix, Kameramatrix, die verkettete Kamera-Projektionsmatrix, Ansichtsfensterinformationen, Frustrumdetails und Matrixumkehrungen speichern. Sie müssen diesen Puffer nur einmal pro Szene aktualisieren.

Geben Sie dann jedem Modell einen weiteren Puffer, um seine Modellansichtsmatrix, Normalmatrix, Inversen und Materialeigenschaften zu speichern. Dies wird für jedes Modell einmal aktualisiert und kann in einem anderen Aktualisierungsdurchlauf durchgeführt werden (falls zutreffend). Die Materialinformationen können / sollten in einen dritten materialspezifischen Puffer verschoben werden, wenn Sie die Möglichkeit haben, Materialien für mehrere Objekte freizugeben.

In einem Vorwärtsschattierungsaufbau ist es sinnvoll, alle Lichter in einen anderen Puffer zu legen, und in der verzögerten Schattierung ist es ebenfalls sinnvoll, einen Lichtpuffer für den Lichtdurchgang zu verwenden (anstelle eines Modell- / Materialpuffers, der in der Geometriepass).

Beachten Sie, dass Sie eine mäßig aktuelle Version von GL benötigen, um überhaupt einheitliche Puffer zu verwenden (3.1 oder eine Erweiterung; heute üblich genug, außer bei einigen älteren, aber noch in Betrieb befindlichen Laptops), und dass Sie eine relativ aktuelle Version benötigen Sie können einheitliche Puffer innerhalb des Shader-Codes an bestimmte Bindungsstellen binden (4.2; immer noch ungewöhnlich, aber immer besser). Andernfalls müssen Sie mehr Arbeit in Ihrem CPU-seitigen Code leisten, um die Einstellungen vorzunehmen (Ihr CPU-Code muss die richtige Bindung kennen Punkte sowieso, so ist es eher ein API-Geruch als ein ernstes Problem). OpenGL | ES hat bis 3.0 keine einheitlichen Puffer hinzugefügt, was auf den meisten gängigen mobilen Plattformen leider immer noch nicht unterstützt wird.

Wenn Puffer keine Option sind, benötigen Sie einen globalen Speicherort für Indexpositionen für den aktiven Shader. Sie können glGetUniformLocationnach dem Laden Ihres Shaders die Indizes für bekannte Namen (wie ModelViewMatrixoder ähnliche) suchen und diese Indizes speichern. Ihr Rendering kann Enum-Werte wie MODEL_VIEWan eine SetUniformWrapper-Funktion übergeben zuordnen, um in den gebundenen Shader zu schauen, den Index zu finden und glUniformordnungsgemäß aufzurufen . Es ist keine besonders große Änderung in der Verwendung von Client-Code aufgrund von Puffern, außer dass jede Uniform einzeln festgelegt werden muss, wenn alles gut verpackt ist.

Siehe GLSL-Schnittstellenblöcke und einheitliche Pufferobjekte .

Sean Middleditch
quelle
Ich wusste, dass dies ein eingebauter Weg sein würde. Vielen Dank!
Lerp
4

Der einfachste Weg, dies zu tun, besteht darin, einheitliche Namen zwischen Ihren Shadern zu standardisieren und alle Daten direkt vor dem Zeichnen zu senden. Der Overhead ist nicht verrückt, aber Sie können ihn später optimieren, um nicht immer weniger aktualisierte Uniformen zu senden.

Die Art und Weise, wie ich das in meinem Spiel mache, ist, dass ich eine abstrakte MaterialKlasse habe. Materialien fungieren als Brücke zwischen dem Spiel und den Shadern. Jedes Materialhat eine Shaderund verschiedene Eigenschaften, die eingestellt werden können, einschließlich Texturen. Wenn es Zeit ist, ein Objekt zu zeichnen, Materialist es gebunden. Die BindMethode verfügt über einen GraphicsStateParameter, der alle aktuellen Grafikzustände enthält - Matrizen, Lichter usw. Die BindMethode bindet den Shader und die Texturen und legt alle Uniformen fest, wobei alles verwendet wird, was sie von der benötigt GraphicsState.

Robert Rouhani
quelle