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 Shader
Klasse, 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- ShaderManager
Klasse 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 ShaderManager
die 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.
Antworten:
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
glGetUniformLocation
nach dem Laden Ihres Shaders die Indizes für bekannte Namen (wieModelViewMatrix
oder ähnliche) suchen und diese Indizes speichern. Ihr Rendering kann Enum-Werte wieMODEL_VIEW
an eineSetUniform
Wrapper-Funktion übergeben zuordnen, um in den gebundenen Shader zu schauen, den Index zu finden undglUniform
ordnungsgemäß 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 .
quelle
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
Material
Klasse habe. Materialien fungieren als Brücke zwischen dem Spiel und den Shadern. JedesMaterial
hat eineShader
und verschiedene Eigenschaften, die eingestellt werden können, einschließlich Texturen. Wenn es Zeit ist, ein Objekt zu zeichnen,Material
ist es gebunden. DieBind
Methode verfügt über einenGraphicsState
Parameter, der alle aktuellen Grafikzustände enthält - Matrizen, Lichter usw. DieBind
Methode bindet den Shader und die Texturen und legt alle Uniformen fest, wobei alles verwendet wird, was sie von der benötigtGraphicsState
.quelle