Programmieren eines oder mehrerer Streams (Direct3D 9)

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

Verwenden von Streams

DirectX 8 hat den Begriff eines Datenstroms eingeführt, um Daten zur Verwendung durch Shader an Eingaberegister zu binden. Ein Stream 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. Streams ermöglichen Grafikchips, einen direkten Speicherzugriff aus mehreren Scheitelpunktpuffern parallel durchzuführen und eine natürlichere Zuordnung aus Anwendungsdaten bereitzustellen. Sie ermöglichen auch triviale Mehrtexte im Vergleich zu Multipass. Stellen Sie sich dies wie folgt vor:

  • Ein Scheitelpunkt besteht aus n Streams.
  • Ein Stream besteht aus m-Elementen.
  • Ein Element ist [Position, Farbe, Normalkoordinate].

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

Die Zuordnung der Eingabevertexelemente zu den Vertexeingaberegistern für programmierbare Vertex-Shader ist in der Shaderdeklaration definiert, aber die Eingabevertexelemente verfügen nicht über eine bestimmte Semantik für ihre 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 mithilfe von Anweisungen in der Shaderfunktion explizit in geschrieben.

In dieser Diskussion befassen Sie sich jedoch weniger mit der semantischen Zuordnung von Elementen zu Registern als mit dem Grund für die Verwendung von Streams und dem Problem, das durch die Verwendung von Streams gelöst wird. Der Standard Vorteil von Streams besteht darin, dass sie die vertex-Datenkosten entfernen, die zuvor mit Multitexturing verbunden waren. Vor Streams musste ein Benutzer entweder Vertex-Datasets duplizieren, um den Einzel- und Multitexturfall ohne ungenutzte Datenelemente zu behandeln, oder Datenelemente tragen, die außer im Multitexture-Fall nicht verwendet würden.

Hier sehen Sie ein Beispiel für die Verwendung von zwei Sätzen von Vertexdaten, einen für eine einzelne Textur und einen 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 verwenden, 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
};

Mit diesen Scheitelpunktdaten wird nur eine Kopie der Positions- und Farbdaten im Arbeitsspeicher übertragen, was zu Lasten der Übertragung beider Texturkoordinatensätze für das Rendern auch im einzelnen Texturfall erfolgt.

Jetzt, da der Kompromiss klar ist, bieten Streams eine elegante Lösung für dieses Dilemma. Hier sehen Sie eine Reihe von Scheitelpunktdefinitionen, die drei Datenströme unterstützen: einen mit Position und Farbe, einen mit dem ersten Satz von Texturkoordinaten und einen 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 folgt fest:

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

m_pd3dDevice->SetVertexDeclaration(m_pVertexDeclaration);

Beispiele für Kombinationen

Ein Stream Diffuse Farbe

Die Vertexdeklaration und die Streameinstellungen für diffuses 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 Streams mit Farbe und Textur

Die Vertexdeklaration und die Streameinstellungen für das Rendering einer einzelnen Textur 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 Streams mit Farbe und zwei Texturen

Die Vertexdeklaration und die Streameinstellungen für das Rendering mit mehreren Texturen mit zwei 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 reicht der folgende IDirect3DDevice9::D rawPrimitive-Aufruf aus.

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

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

Vertexdeklaration