Freigeben über


Programmieren eines oder mehrerer Streams (Direct3D 9)

In diesem Abschnitt werden Shader beschrieben, die für das programmierbare Datenstrommodell verwendet werden können.

Verwenden von Streams

DirectX 8 hat den Begriff eines Datenstroms zum Binden von Daten an Eingaberegister für die Verwendung durch Shader eingeführt. Ein Datenstrom ist ein einheitliches Array von Komponentendaten, wobei jede Komponente aus einem oder mehreren Elementen besteht, die eine einzelne Entität darstellen, z. B. Position, Normal, Farbe usw. Datenströme ermöglichen Grafikchips, einen direkten Speicherzugriff von mehreren Vertexpuffern parallel durchzuführen und eine natürlichere Zuordnung von Anwendungsdaten bereitzustellen. Sie ermöglichen auch triviale Multitextur im Vergleich zu Multipass. Stellen Sie sich folgendes vor:

  • Ein Scheitelpunkt besteht aus n Datenströmen.
  • Ein Datenstrom besteht aus m-Elementen.
  • Ein Element ist [Position, Farbe, normal, Texturkoordinate].

Die IDirect3DDevice9::SetStreamSource-Methode bindet einen Vertexpuffer an einen Gerätedatenstrom, wodurch eine Zuordnung zwischen den Vertexdaten und einem von mehreren Datenstromports erstellt wird, die die primitiven Verarbeitungsfunktionen enthalten. Die tatsächlichen Verweise auf die Datenstromdaten treten erst auf, wenn eine Zeichnungsmethode wie IDirect3DDevice9::D rawPrimitiveaufgerufen wird.

Die Zuordnung der Eingabevertexelemente zu den Vertexeingaberegistern für programmierbare Vertex-Shader wird in der Shaderdeklaration definiert, aber die Eingabevertexelemente haben keine spezifische Semantik zur Verwendung. Die Interpretation der Eingabevertexelemente wird mithilfe der Shaderanweisungen programmiert. Die Vertex-Shaderfunktion wird durch ein Array von Anweisungen definiert, die auf jeden Scheitelpunkt angewendet werden. Die Vertexausgaberegister werden explizit in die Shaderfunktion geschrieben.

Für diese Diskussion geht es jedoch weniger um die semantische Zuordnung von Elementen, um sich zu registrieren und mehr mit dem Grund für die Verwendung von Datenströmen und dem Problem zu befassen, das durch die Verwendung von Datenströmen gelöst wird. Der Hauptvorteil von Datenströmen besteht darin, dass sie die Vertexdatenkosten entfernen, die zuvor mit multitexturing verbunden waren. Vor Datenströmen musste ein Benutzer entweder Vertexdatensätze duplizieren, um den Einzel- und Multitexture-Fall ohne nicht verwendete Datenelemente zu verarbeiten oder Datenelemente zu übertragen, die außer im Multitexture-Fall nicht verwendet würden.

Hier ist ein Beispiel für die Verwendung von zwei Sätzen von Vertexdaten, einer für eine einzelne Textur und eine für multitexturing.

struct CUSTOMVERTEX_TEX1
{
    FLOAT x, y, z;      // The untransformed position for the vertex
    DWORD diffColor;    // The vertex diffuse color    
    DWORD specColor;    // The vertex specular color
    float tu_1, tv_1;   // Texture coordinates for a single texture
};
 
struct CUSTOMVERTEX_TEX2
{
    FLOAT x, y, z;      // The untransformed position for the vertex
    DWORD diffColor;    // The vertex diffuse color    
    DWORD specColor;    // The vertex specular color
    float tu_2, tv_2;   // Texture coordinates for multitexturing
};

Die Alternative bestand darin, ein einzelnes Vertexelement zu haben, das beide Sätze von Texturkoordinaten enthielt.

struct CUSTOMVERTEX_TEX2
{
    FLOAT x, y, z;      // The untransformed position for the vertex
    DWORD diffColor;    // The vertex diffuse color    
    DWORD specColor;    // The vertex specular color
    float tu_1, tv_1;   // Texture coordinates for a single texture
    float tu_2, tv_2;   // Texture coordinates for multitexturing
};

Bei diesen Vertexdaten werden nur eine Kopie der Positions- und Farbdaten im Arbeitsspeicher übertragen, auf Kosten der Übertragung beider Texturkoordinatensätze für das Rendern sogar im einzelnen Texturfall.

Nun, da der Kompromiss klar ist, bieten Streams einen eleganten Fix für dieses Dilemma. Hier ist eine Reihe von Vertexdefinitionen zur Unterstützung von drei Datenströmen: eine mit Position und Farbe, eine mit dem ersten Satz von Texturkoordinaten und eine mit dem zweiten Satz von Texturkoordinaten.

// Multistream vertex
// Stream 0, pos, diffuse, specular
struct POSCOLORVERTEX
{
    FLOAT x, y, z;
    DWORD diffColor, specColor;
};
#define D3DFVF_POSCOLORVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_SPECULAR)
// Stream 1, tex coord 0
struct TEXC0VERTEX
{
    FLOAT tu1, tv1;
};
#define D3DFVF_TEXC0VERTEX (D3DFVF_TEX1)

// Stream 2, tex coord 1
struct TEXC1VERTEX
{
    FLOAT tu2, tv2;
};
#define D3DFVF_TEXC1VERTEX (D3DFVF_TEX0)

Die Vertexdeklaration lautet wie folgt:

// Multitexture - multistream

D3DVERTEXELEMENT9 dwDecl3[] = 
{
    {0, 0,  D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_POSITION, 0},
    {0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_COLOR, 0},
    {0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_COLOR, 1},
    {1,  0, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_TEXCOORD, 0},
    {2,  0, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_TEXCOORD, 1},
    D3DDECL_END()
};

Erstellen Sie nun das Vertexdeklarationsobjekt, und legen Sie es wie gezeigt fest:

LPDIRECT3DVERTEXDECLARATION9 m_pVertexDeclaration;
g_d3dDevice->CreateVertexDeclaration(dwDecl3, &m_pVertexDeclaration);

m_pd3dDevice->SetVertexDeclaration(m_pVertexDeclaration);

Beispiele für Kombinationen

Eine Stream diffuse Farbe

Die Vertexdeklarations- und Datenstromeinstellungen für das diffuse Farbrendering würden wie folgt aussehen:

D3DVERTEXELEMENT9 dwDecl3[] = 
{
    {0,  0,  D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_POSITION, 0},
    {0, 12,  D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_COLOR, 0 ,
    {0, 16,  D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_COLOR, 1},
    D3DDECL_END()
};
   m_pd3dDevice->SetStreamSource(0, m_pVBVertexShader0, 0,
                                      sizeof(CUSTOMVERTEX));
   m_pd3dDevice->SetStreamSource(1, NULL, 0, 0);
   m_pd3dDevice->SetStreamSource(2, NULL, 0, 0);

Zwei Datenströme mit Farbe und Textur

Die Vertexdeklarations- und Datenstromeinstellungen für einzelnes Texturrendering würden wie folgt aussehen:

D3DVERTEXELEMENT9 dwDecl3[] = 
{
    {0, 0,  D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_POSITION, 0},
    {0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_COLOR, 0},
    {0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_COLOR, 1},

    {1,  0, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_TEXCOORD, 0},

    D3DDECL_END()
};
   m_pd3dDevice->SetStreamSource(0, m_pVBPosColor, 0,
                                           sizeof(POSCOLORVERTEX));
   m_pd3dDevice->SetStreamSource(1, m_pVBTexC0, 0,
                                           sizeof(TEXC0VERTEX));
   m_pd3dDevice->SetStreamSource(2, NULL, 0, 0);

Zwei Datenströme mit Farbe und zwei Texturen

Die Vertexdeklarations- und Datenstromeinstellungen für das Rendern mit zwei Texturen für mehrere Texturen würden wie folgt aussehen:

D3DVERTEXELEMENT9 dwDecl3[] = 
{
    {0, 0,  D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_POSITION, 0},
    {0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_COLOR, 0},
    {0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_COLOR, 1},

    {1,  0, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_TEXCOORD, 0},

    {2,  0, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_TEXCOORD, 1},
    
    D3DDECL_END()
};
 
m_pd3dDevice->SetStreamSource(0, m_pVBPosColor, 0, 
                                          sizeof(POSCOLORVERTEX));
m_pd3dDevice->SetStreamSource(1, m_pVBTexC0, 0, 
                                          sizeof(TEXC0VERTEX));
m_pd3dDevice->SetStreamSource(2, m_pVBTexC1, 0, 
                                          sizeof(TEXC1VERTEX));

In jedem Fall genügt der folgende IDirect3DDevice9::D rawPrimitive Aufruf.

       m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, NUM_TRIS);

Dies zeigt die Flexibilität von Datenströmen bei der Lösung des Problems der Datenduplizierung/redundanten Datenübertragung über den gesamten Bus hinweg (d. a. von Lässigkeit der Bandbreite).

Vertexdeklaration