Rendern aus Vertex- und Indexpuffern (Direct3D 9)

Sowohl indizierte als auch nicht indizierte Zeichnungsmethoden werden von Direct3D unterstützt. Die indizierten Methoden verwenden einen einzelnen Satz von Indizes für alle Vertexkomponenten. Vertexdaten werden in Vertexpuffern und Indexdaten in Indexpuffern gespeichert. Nachfolgend sind einige gängige Szenarien für das Zeichnen von Grundtypen mit Vertex- und Indexpuffern aufgeführt.

In diesen Beispielen wird die Verwendung von IDirect3DDevice9::D rawPrimitive und IDirect3DDevice9::D rawIndexedPrimitive verglichen.

Szenario 1: Zeichnen von zwei Dreiecken ohne Indizierung

Angenommen, Sie möchten das Quad zeichnen, das in der folgenden Abbildung dargestellt ist.

Abbildung eines Quadrats, das aus zwei Dreiecken besteht

Wenn Sie den primitiven Typ Dreiecksliste verwenden, um die beiden Dreiecke zu rendern, wird jedes Dreieck als drei einzelne Scheitelpunkte gespeichert, was zu einem ähnlichen Vertexpuffer wie in der folgenden Abbildung führt.

Diagramm eines Vertexpuffers, der drei Scheitelpunkte für zwei Dreiecke definiert

Der Zeichnungsaufruf ist sehr einfach; Zeichnen Sie ab Position 0 innerhalb des Vertexpuffers zwei Dreiecke. Wenn Culling aktiviert ist, ist die Reihenfolge der Scheitelpunkte wichtig. In diesem Beispiel wird der Standardmäßige Kullingzustand gegen den Uhrzeigersinn angenommen, sodass sichtbare Dreiecke im Uhrzeigersinn gezeichnet werden müssen. Der primitive Typ Dreiecksliste liest einfach drei Scheitelpunkte in linearer Reihenfolge aus dem Puffer für jedes Dreieck, sodass dieser Aufruf Dreiecke (0, 1, 2) und (3, 4, 5) zeichnet:

DrawPrimitive( D3DPT_TRIANGLELIST, // PrimitiveType
               0,                  // StartVertex
               2 );                // PrimitiveCount

Szenario 2: Zeichnen von zwei Dreiecken mit Indizierung

Wie Sie sehen werden, enthält der Vertexpuffer doppelte Daten an den Standorten 0 und 4, 2 und 5. Dies ist sinnvoll, da die beiden Dreiecke zwei gemeinsame Scheitelpunkte haben. Diese doppelten Daten sind verschwenderisch, und der Vertexpuffer kann mithilfe eines Indexpuffers komprimiert werden. Ein kleinerer Vertexpuffer reduziert die Menge der Vertexdaten, die an die Grafikkarte gesendet werden müssen. Noch wichtiger ist, dass die Verwendung eines Indexpuffers es dem Adapter ermöglicht, Scheitelpunkte in einem Vertexcache zu speichern. Wenn der gezeichnete Grundtyp einen kürzlich verwendeten Scheitelpunkt enthält, kann dieser Scheitelpunkt aus dem Cache abgerufen werden, anstatt ihn aus dem Vertexpuffer zu lesen, was zu einer großen Leistungssteigerung führt.

Ein Indexpuffer "indiziert" in den Vertexpuffer, sodass jeder eindeutige Scheitelpunkt nur einmal im Vertexpuffer gespeichert werden muss. Das folgende Diagramm zeigt einen indizierten Ansatz für das frühere Zeichnungsszenario.

Diagramm eines Indexpuffers für den früheren Vertexpuffer

Der Indexpuffer speichert VB-Indexwerte, die auf einen bestimmten Scheitelpunkt innerhalb des Vertexpuffers verweisen. Ein Vertexpuffer kann als Array von Scheitelpunkten betrachtet werden, sodass der VB-Index einfach der Index in den Vertexpuffer für den Zielvertex ist. Ebenso ist ein IB-Index ein Index in den Indexpuffer. Dies kann sehr schnell sehr verwirrend werden, wenn Sie nicht vorsichtig sind. Stellen Sie daher sicher, dass Sie das verwendete Vokabular im Klaren sind: VB-Indexwerte in den Vertexpuffer, IB-Indexwerte indexieren in den Indexpuffer, und der Indexpuffer selbst speichert VB-Indexwerte.

Der Zeichnungsaufruf ist unten dargestellt. Die Bedeutungen aller Argumente werden für das nächste Zeichnungsszenario ausführlich erläutert. Beachten Sie zunächst, dass dieser Aufruf Direct3D erneut anweist, eine Dreiecksliste mit zwei Dreiecken zu rendern, beginnend bei Position 0 innerhalb des Indexpuffers. Bei diesem Aufruf werden die gleichen beiden Dreiecke in exakt derselben Reihenfolge wie zuvor gezeichnet, wodurch eine korrekte Ausrichtung im Uhrzeigersinn sichergestellt wird:

   
DrawIndexedPrimitive( D3DPT_TRIANGLELIST, // PrimitiveType
                    0,                  // BaseVertexIndex
                    0,                  // MinIndex
                    4,                  // NumVertices
                    0,                  // StartIndex
                    2 );                // PrimitiveCount

Szenario 3: Zeichnen eines Dreiecks mit Indizierung

Geben Sie jetzt an, dass Sie nur das zweite Dreieck zeichnen möchten, aber den gleichen Vertexpuffer und Indexpuffer verwenden möchten, die beim Zeichnen des gesamten Quads verwendet werden, wie im folgenden Diagramm dargestellt.

Diagramm des Indexpuffers und des Vertexpuffers für das zweite Dreieck

Für diesen Zeichnungsaufruf wird der erste IB-Index verwendet 3; Dieser Wert wird als StartIndex bezeichnet. Der niedrigste verwendete VB-Index ist 0; Dieser Wert wird als MinIndex bezeichnet. Obwohl zum Zeichnen des Dreiecks nur drei Scheitelpunkte erforderlich sind, werden diese drei Scheitelpunkte auf vier benachbarte Stellen im Vertexpuffer verteilt; Die Anzahl der Speicherorte innerhalb des zusammenhängenden Blocks des Vertexpufferspeichers, der für den Zeichnungsaufruf erforderlich ist, wird als NumVertices bezeichnet und in diesem Aufruf auf 4 festgelegt. Die Werte MinIndex und NumVertices sind wirklich nur Hinweise, um Direct3D bei der Optimierung des Speicherzugriffs während der Softwarevertexverarbeitung zu unterstützen, und könnten einfach so festgelegt werden, dass der gesamte Vertexpuffer zum Preis der Leistung eingeschlossen wird.

Hier ist der Zeichnungsaufruf für den einzelnen Dreiecks-Case; Die Bedeutung des BaseVertexIndex-Arguments wird als Nächstes erläutert:

   
DrawIndexedPrimitive( D3DPT_TRIANGLELIST, // PrimitiveType
                    0,                  // BaseVertexIndex
                    0,                  // MinIndex
                    4,                  // NumVertices
                    3,                  // StartIndex
                    1 );                // PrimitiveCount

Szenario 4: Zeichnen eines Dreiecks mit Offsetindizierung

BaseVertexIndex ist ein Wert, der effektiv jedem im Indexpuffer gespeicherten VB-Index hinzugefügt wird. Wenn wir beispielsweise während des vorherigen Aufrufs den Wert 50 für BaseVertexIndex übergeben hätten, wäre dies funktional identisch mit der Verwendung des Indexpuffers im folgenden Diagramm für die Dauer des DrawIndexedPrimitive-Aufrufs:

Diagramm eines Indexpuffers mit dem Wert 50 für Basevertexindex

Dieser Wert wird selten auf einen anderen Wert als 0 festgelegt, kann aber nützlich sein, wenn Sie den Indexpuffer vom Vertexpuffer entkoppeln möchten: Wenn beim Ausfüllen des Indexpuffers für ein bestimmtes Gitter die Position des Gitters innerhalb des Vertexpuffers noch nicht bekannt ist, können Sie einfach so tun, als würden sich die Gittervertices am Anfang des Vertexpuffers befinden; Wenn es an der Zeit ist, den Draw-Aufruf zu tätigen, übergeben Sie einfach die tatsächliche Startposition als BaseVertexIndex.

Dieses Verfahren kann auch verwendet werden, wenn mehrere Instanzen eines Gitters mit einem einzelnen Indexpuffer gezeichnet werden. Wenn der Vertexpuffer beispielsweise zwei Gitter mit identischer Zeichnungsreihenfolge, aber leicht unterschiedlichen Scheitelpunkten (möglicherweise unterschiedliche diffuse Farben oder Texturkoordinaten) enthielt, könnten beide Gitter mit unterschiedlichen Werten für BaseVertexIndex gezeichnet werden. Wenn Sie dieses Konzept noch einen Schritt weitergehen, können Sie einen Indexpuffer verwenden, um mehrere Instanzen eines Gitters zu zeichnen, die jeweils in einem anderen Vertexpuffer enthalten sind, indem Sie einfach den aktiven Vertexpuffer durchlaufen und den BaseVertexIndex nach Bedarf anpassen. Beachten Sie, dass der BaseVertexIndex-Wert auch automatisch dem MinIndex-Argument hinzugefügt wird. Dies ist sinnvoll, wenn Sie sehen, wie er verwendet wird:

Tun Sie jetzt so, als möchten wir wieder nur das zweite Dreieck des Quads mit demselben Indexpuffer wie zuvor zeichnen; Es wird jedoch ein anderer Vertexpuffer verwendet, in dem sich das Quad bei VB Index 50 befindet. Die relative Reihenfolge der Viereckpunkte bleibt unverändert, nur die Startposition innerhalb des Vertexpuffers ist unterschiedlich. Der Indexpuffer und der Vertexpuffer würden wie im folgenden Diagramm aussehen.

Diagramm des Indexpuffers und des Vertexpuffers mit einem VB-Index von 50

Hier ist der geeignete Draw-Aufruf; Beachten Sie, dass BaseVertexIndex der einzige Wert ist, der sich gegenüber dem vorherigen Szenario geändert hat:

   
DrawIndexedPrimitive( D3DPT_TRIANGLELIST, // PrimitiveType
                    50,                 // BaseVertexIndex
                    0,                  // MinIndex
                    4,                  // NumVertices
                    3,                  // StartIndex
                    1 );                // PrimitiveCount

Rendern von Grundtypen