Scheitelpunktpufferbindungsindex und einheitliche Pufferbindungspunkte?
Ich lerne nur etwas über einheitliche Puffer und wie sie funktionieren. Es scheint, dass sie Ähnlichkeiten mit Vertex-Puffern hinsichtlich ihrer Attribut- und Pufferbindungen aufweisen.
Da ich die Pufferbindungspunkte nicht direkt in glBindBufferBaseoder fest codieren möchte glUniformBlockBindingund für die zukünftige automatische Shader-Komposition versuche ich, Vertex-Shader-Puffer und einheitliche Puffer in einem einzigen Konzept für eine Pufferklasse in C ++ zu kombinieren.
Hier ist mein mentales Modell dafür:
Ist diese Ansicht von beiden korrekt:
Attributpositionen, Scheitelpunktpuffer und Bindungsindex
&
einheitliche Positionen, einheitliche Puffer und Bindungspunkte
?
Antworten
Die grundlegenden Fakten Ihrer Analogie sind weitgehend zutreffend (vorausgesetzt, wir verwenden die Vertex-Attributbindung). Ein Vertex-Attribut in einer GLSL hat eine Position, die letztendlich einem Pufferbindungsindex zugeordnet ist, an den ein Pufferobjekt gebunden ist. Ein einheitlicher Puffer in GLSL hat einen Blockindex, der letztendlich einem Pufferbindungsindex zugeordnet ist, an den ein Pufferobjekt gebunden ist.
Es gibt jedoch zu viele Unterschiede in den Details dieser Dinge, um zu versuchen, irgendeine strukturelle Grundlage für die gleiche Behandlung dieser Konstrukte in einem formalisierten Objekt zu bilden.
Betrachten Sie die Unterscheidung zwischen einem Scheitelpunktattributindex und einem Blockindex. Ja, diese sind in mancher Hinsicht ähnlich. Die Art und Weise, wie Sie mit ihnen interagieren, ist jedoch sehr unterschiedlich. Blockindizes werden vom GLSL-Compiler zugewiesen. Attributindizes werden vom Benutzer im Code entweder über layout(location)im Shader oder über glBindAttribLocationvor dem Kompilieren des Shaders zugewiesen .
Einfach ausgedrückt, fragen Sie Blockindizes ab. Sie weisen Scheitelpunktattributpositionen zu.
Wenn Sie nun technische Informationen benötigen, weist der GLSL-Compiler jedem Attribut beim Kompilieren des Shaders einen eindeutigen Speicherort zu, sofern Sie diese Speicherorte nicht auf andere Weise zugewiesen haben. Und so, Sie können Standorte abfragen.
Aber das bringt uns zu einem weiteren Unterschied in der Art und Weise, wie Sie mit ihnen interagieren. Blockindizes bedeuten nichts ; Sie sind lediglich eine numerische Kennung für einen bestimmten einheitlichen Block innerhalb eines Shaders. Ein Blockindex ist nur in Bezug auf einen bestimmten Shader nützlich .
Dies ist bei einem Attributspeicherort nicht der Fall. Die Zuordnung zwischen Attributpositionen und den Puffern, aus denen sie abgerufen werden, ist in einem vom eigentlichen Shader getrennten Objekt enthalten : einem Vertex-Array-Objekt. Dies bedeutet, dass ein von einem Shader angegebener Attributspeicherort auch mit dem in einem VAO angegebenen Attributformat übereinstimmen muss, damit alles funktioniert.
Der Bereich einer Attributposition ist also nicht nur an einen Shader gebunden. Es muss mit allen VAOs übereinstimmen, mit denen es verwendet werden soll. Oder was noch wichtiger ist, die Attributpositionen eines bestimmten VAO müssen mit allen Shadern übereinstimmen, mit denen dieses VAO verwendet wird.
Das Ändern von Scheitelpunktformaten ist in vielen GPUs ein ziemlich teurer Vorgang. Es wäre also eine gute Sache, das gleiche Scheitelpunktformat beizubehalten (einschließlich der Zuordnung von Ort zu Bindung). Dies bedeutet, dass es sinnvoll ist, mehrere Shader zu haben, die alle dieselbe VAO verwenden. Sie würden ändern, welche Puffer gebunden sind, um verschiedene Objekte zu rendern (was eine schnellere Statusänderung als das Format darstellt), aber Sie würden nicht glBindVertexArrayzwischen solchen Objekten aufrufen .
Damit dies funktioniert, müssen alle Shader für diese Objekte dieselben Speicherorte für ihre Attribute verwenden. Aber sie würden nicht unbedingt dieselben Blockindizes haben, selbst wenn sie dieselben einheitlichen Blockdefinitionen verwenden.
Einheitliche Blockindexwerte sind beliebig; Standorte sind nicht . Aus diesem Grund können Sie Standorte zuweisen, aber keine Indizes blockieren.
Aus diesem Grund können Sie im Shader (via ) auch UBO- Bindungsindizes zuweisen , im Shader layout(binding=#)jedoch keine Attributbindungsindizes. Der Shader steuert den Bindungsindex nicht. die VAO tut.
Tatsächlich ermöglicht die Fähigkeit, einem UBO in einem Shader einen Bindungsindex zuzuweisen, es, den Blockindex im Wesentlichen von der Betrachtung auszuschließen. Sie können einen bekannten Satz von Bindungsindizes entwickeln, die eine genau definierte Bedeutung haben. Index 0 kann für Szenendaten (Kamera, perspektivische Matrizen usw.) sein, Index 1 kann für Objektdaten sein, Index 2 kann für Beleuchtung sein, Index 3 für Arrays von Knochenmatrizen usw.
Eine ähnliche Heuristik für Attribute verwendet jedoch Positionen und keine Scheitelpunktpuffer-Bindungsindizes. Position 0 kann für Positionen sein, Position 1 für Normalen, Position 2 für Farben usw.
Aus dieser Perspektive können Attributpositionen aus der Perspektive des GLSL-Codes, mit dem wir mit ihnen sprechen, eher als UBO-Bindungsindizes angesehen werden. Dies trotz der Tatsache, dass Sie keine Puffer an Attributpositionen binden.
Sehen Sie sich auch den Unterschied an, wie Sie zur Bindungszeit mit Puffern interagieren. UBO-Pufferbindungen sind explizit angeordnet. Sie können verwenden glBindBufferBase, um den gesamten Puffer zu binden, dies ist jedoch nicht die erwartete Verwendung. Ein paar kleine Pufferobjekte zu haben, ist im Allgemeinen keine gute Idee. Wenn Sie UBOs zum Speichern von Daten pro Objekt verwenden, möchten Sie wahrscheinlich nur einen einzelnen Puffer zuordnen, alle Objektdaten auf einmal übertragen und dann verwenden, anstatt immer wieder verschiedene Puffer binden zu müssen.
Die normale Verwendung der UBO-Bindungs-API besteht also darin, geeignete Unterbereiche eines kleinen Satzes von Puffern an einen bestimmten Bindungspunkt zu binden.
Im Gegensatz dazu ist die Vertex-Array-Pufferbindung nicht gebunden. Sie geben einen Startversatz an, aber es gibt keine Obergrenze für den Bereich. Ein Rendering-Aufruf kann aus jedem Byte im Speicher des Puffers (nach dem Offset) abgerufen werden.
Dies ist wichtig, da Sie mit Funktionen wie Instanzen und Rendering von Basisscheitelpunkten mehrere Objekte im selben Puffer speichern können, ohne neue Puffer binden zu müssen, um ein anderes Objekt zu rendern. Vertex-Pufferbindung ist zwar nicht besonders teuer, aber nicht die billigste Sache der Welt. Wenn es sinnvoll ist, sie zu vermeiden, sollten Sie dies tun.
Hier ist ein weiterer Unterschied. Das Format der in einem UBO gespeicherten Daten wird letztendlich vom Shader selbst definiert. Ihr C ++ - Code muss Daten bereitstellen, die genau dem Layout entsprechen, das in den UBO-Blockdefinitionen im GLSL-Shader-Code definiert ist.
Im Gegensatz dazu wird das Format der in einem Scheitelpunktpuffer gespeicherten Daten weitgehend vom VAO und nicht vom Shader definiert. Ein Attribut vom Typ vec4könnte sein, dass seine Daten aus Gruppen von 4 Floats stammen, aus 4 normalisierten vorzeichenlosen Bytes, die in Floats konvertiert werden, aus 4 nicht normalisierten signierten Shorts, die numerisch in Floats konvertiert werden, oder aus vielen anderen Alternativen.
Auch wenn ähnliche Konzepte verwendet werden, ist die Interaktion mit ihnen sehr unterschiedlich. Der Versuch, ein System aufzubauen, in dem diese beiden Mechanismen durch die Struktur Ihres Codes ähnlich gemacht werden, wäre daher unangemessen.