Freigeben über


Porten der Vertexpuffer und -daten

Wichtige APIs

In diesem Schritt definieren Sie die Vertexpuffer, die Ihre Gitter und die Indexpuffer enthalten, mit denen die Shader die Scheitelpunkte in einer angegebenen Reihenfolge durchlaufen können.

An diesem Punkt untersuchen wir das hartcodierte Modell für das Würfelgitter, das wir verwenden. Beide Darstellungen weisen die Scheitelpunkte als Dreiecksliste auf (im Gegensatz zu einem Streifen oder einem anderen effizienteren Dreieckslayout). Alle Scheitelpunkte in beiden Darstellungen weisen auch Indizes und Farbwerte auf. Ein Großteil des Direct3D-Codes in diesem Thema bezieht sich auf Variablen und Objekte, die im Direct3D-Projekt definiert sind.

Hier ist der Cube für die Verarbeitung durch OpenGL ES 2.0. In der Beispielimplementierung ist jeder Scheitelpunkt 7 Float-Werte: 3 Positionskoordinaten gefolgt von 4 RGBA-Farbwerten.

#define CUBE_INDICES 36
#define CUBE_VERTICES 8

GLfloat cubeVertsAndColors[] = 
{
  -0.5f, -0.5f,  0.5f, 0.0f, 0.0f, 1.0f, 1.0f,
  -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f,
  -0.5f,  0.5f,  0.5f, 0.0f, 1.0f, 1.0f, 1.0f,
  -0.5f,  0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f,
  0.5f, -0.5f,  0.5f, 1.0f, 0.0f, 1.0f, 1.0f,
  0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f,  
  0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f,
  0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 1.0f
};

GLuint cubeIndices[] = 
{
  0, 1, 2, // -x
  1, 3, 2,

  4, 6, 5, // +x
  6, 7, 5,

  0, 5, 1, // -y
  5, 6, 1,

  2, 6, 3, // +y
  6, 7, 3,

  0, 4, 2, // +z
  4, 6, 2,

  1, 7, 3, // -z
  5, 7, 1
};

Und hier ist derselbe Cube für die Verarbeitung durch Direct3D 11.

VertexPositionColor cubeVerticesAndColors[] = 
// struct format is position, color
{
  {XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, 0.0f)},
  {XMFLOAT3(-0.5f, -0.5f,  0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f)},
  {XMFLOAT3(-0.5f,  0.5f, -0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f)},
  {XMFLOAT3(-0.5f,  0.5f,  0.5f), XMFLOAT3(0.0f, 1.0f, 1.0f)},
  {XMFLOAT3( 0.5f, -0.5f, -0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f)},
  {XMFLOAT3( 0.5f, -0.5f,  0.5f), XMFLOAT3(1.0f, 0.0f, 1.0f)},
  {XMFLOAT3( 0.5f,  0.5f, -0.5f), XMFLOAT3(1.0f, 1.0f, 0.0f)},
  {XMFLOAT3( 0.5f,  0.5f,  0.5f), XMFLOAT3(1.0f, 1.0f, 1.0f)},
};
        
unsigned short cubeIndices[] = 
{
  0, 2, 1, // -x
  1, 2, 3,

  4, 5, 6, // +x
  5, 7, 6,

  0, 1, 5, // -y
  0, 5, 4,

  2, 6, 7, // +y
  2, 7, 3,

  0, 4, 6, // -z
  0, 6, 2,

  1, 3, 7, // +z
  1, 7, 5
};

Wenn Sie diesen Code überprüfen, stellen Sie fest, dass der Cube im OpenGL ES 2.0-Code in einem rechtshändigen Koordinatensystem dargestellt wird, während der Würfel im Direct3D-spezifischen Code in einem linken Koordinatensystem dargestellt wird. Beim Importieren eigener Gitterdaten müssen Sie die Z-Achsenkoordinaten für Ihr Modell umkehren und die Indizes für jedes Gitter entsprechend ändern, um die Dreiecke entsprechend der Änderung im Koordinatensystem zu durchlaufen.

Wenn wir das Würfelgitter erfolgreich aus dem rechtshändigen OpenGL ES 2.0-Koordinatensystem in das linkshändige Direct3D-Koordinatensystem verschoben haben, sehen wir uns an, wie die Cubedaten für die Verarbeitung in beiden Modellen geladen werden.

Anweisungen

Schritt 1: Erstellen eines Eingabelayouts

In OpenGL ES 2.0 werden Die Vertexdaten als Attribute bereitgestellt, die von den Shaderobjekten bereitgestellt und gelesen werden. In der Regel stellen Sie eine Zeichenfolge bereit, die den Attributnamen enthält, der im GLSL des Shaders für das Shaderprogrammobjekt verwendet wird, und erhalten einen Speicherspeicherort zurück, den Sie für den Shader bereitstellen können. In diesem Beispiel enthält ein Vertexpufferobjekt eine Liste von benutzerdefinierten Vertexstrukturen, die wie folgt definiert und formatiert sind:

OpenGL ES 2.0: Konfigurieren Sie die Attribute, die die Informationen pro Scheitelpunkt enthalten.

typedef struct 
{
  GLfloat pos[3];        
  GLfloat rgba[4];
} Vertex;

In OpenGL ES 2.0 sind Eingabelayouts implizit; Sie nehmen eine allgemeine GL_ELEMENT_ARRAY_BUFFER und geben die Stride und den Offset so an, dass der Vertex-Shader die Daten nach dem Hochladen interpretieren kann. Sie informieren den Shader vor dem Rendern, welche Attribute den Teilen der einzelnen Vertexdatenblocks mit glVertexAttribPointer zugeordnet sind.

In Direct3D müssen Sie ein Eingabelayout bereitstellen, um die Struktur der Vertexdaten im Vertexpuffer zu beschreiben, wenn Sie den Puffer erstellen, anstatt die Geometrie zu zeichnen. Dazu verwenden Sie ein Eingabelayout, das dem Layout der Daten für unsere einzelnen Scheitelpunkte im Speicher entspricht. Es ist sehr wichtig, dies genau anzugeben!

Hier erstellen Sie eine Eingabebeschreibung als Array von D3D11_INPUT_ELEMENT_DESC Strukturen.

Direct3D: Definieren einer Eingabelayoutbeschreibung.

struct VertexPositionColor
{
  DirectX::XMFLOAT3 pos;
  DirectX::XMFLOAT3 color;
};

// ...

const D3D11_INPUT_ELEMENT_DESC vertexDesc[] = 
{
  { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,  D3D11_INPUT_PER_VERTEX_DATA, 0 },
  { "COLOR",    0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};

Diese Eingabebeschreibung definiert einen Scheitelpunkt als Paar von 2 3-Koordinatenvektoren: einen 3D-Vektor zum Speichern der Position des Scheitelpunkts in Modellkoordinaten und einen anderen 3D-Vektor zum Speichern des RGB-Farbwerts, der dem Vertex zugeordnet ist. In diesem Fall verwenden Sie das 3x32-Bit-Gleitkommaformat, dessen Elemente wir im Code darstellen als XMFLOAT3(X.Xf, X.Xf, X.Xf). Sie sollten Typen aus der DirectXMath-Bibliothek verwenden, wenn Sie Daten verarbeiten, die von einem Shader verwendet werden, da sie die richtige Verpackung und Ausrichtung dieser Daten sicherstellen. (Verwenden Sie z. B. XMFLOAT3 oder XMFLOAT4 für Vektordaten und XMFLOAT4X4 für Matrizen.)

Eine Liste aller möglichen Formattypen finden Sie unter DXGI_FORMAT.

Mit dem definierten Eingabelayout pro Scheitelpunkt erstellen Sie das Layoutobjekt. Im folgenden Code schreiben Sie sie in m_inputLayout, eine Variable vom Typ ComPtr (die auf ein Objekt vom Typ ID3D11InputLayout verweist). fileData enthält das kompilierte Vertex-Shaderobjekt aus dem vorherigen Schritt, Portieren der Shader.

Direct3D: Erstellen Sie das eingabelayout, das vom Vertexpuffer verwendet wird.

Microsoft::WRL::ComPtr<ID3D11InputLayout>      m_inputLayout;

// ...

m_d3dDevice->CreateInputLayout(
  vertexDesc,
  ARRAYSIZE(vertexDesc),
  fileData->Data,
  fileShaderData->Length,
  &m_inputLayout
);

Wir haben das Eingabelayout definiert. Als Nächstes erstellen wir einen Puffer, der dieses Layout verwendet und mit den Würfelgitterdaten geladen wird.

Schritt 2: Erstellen und Laden der Vertexpuffer

In OpenGL ES 2.0 erstellen Sie ein Pufferpaar, eines für die Positionsdaten und eines für die Farbdaten. (Sie können auch eine Struktur erstellen, die sowohl einen als auch einen einzelnen Puffer enthält.) Sie binden jeden Puffer und schreiben Positions- und Farbdaten in diese. Binden Sie die Puffer später während der Renderfunktion erneut, und stellen Sie den Shader mit dem Format der Daten im Puffer bereit, damit er sie richtig interpretieren kann.

OpenGL ES 2.0: Binden der Vertexpuffer

// upload the data for the vertex position buffer
glGenBuffers(1, &renderer->vertexBuffer);    
glBindBuffer(GL_ARRAY_BUFFER, renderer->vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(VERTEX) * CUBE_VERTICES, renderer->vertices, GL_STATIC_DRAW);   

In Direct3D werden shaderzugriffe Puffer als D3D11_SUBRESOURCE_DATA Strukturen dargestellt. Um die Position dieses Puffers an shaderobjekt zu binden, müssen Sie eine CD3D11_BUFFER_DESC Struktur für jeden Puffer mit ID3DDevice::CreateBuffer erstellen und dann den Puffer des Direct3D-Gerätekontexts festlegen, indem Sie eine set-Methode aufrufen, die für den Puffertyp spezifisch ist, z. B. ID3DDeviceContext::IASetVertexBuffers.

Wenn Sie den Puffer festlegen, müssen Sie den Abstand (die Größe des Datenelements für einen einzelnen Scheitelpunkt) sowie den Offset (wobei das Vertexdatenarray tatsächlich beginnt) vom Anfang des Puffers festlegen.

Beachten Sie, dass wir den Zeiger dem VertexIndices-Array dem pSysMem-Feld der D3D11_SUBRESOURCE_DATA Struktur zuweisen. Wenn dies nicht korrekt ist, ist Ihr Gitter beschädigt oder leer!

Direct3D: Erstellen und Festlegen des Vertexpuffers

D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
vertexBufferData.pSysMem = cubeVertices;
vertexBufferData.SysMemPitch = 0;
vertexBufferData.SysMemSlicePitch = 0;
CD3D11_BUFFER_DESC vertexBufferDesc(sizeof(cubeVertices), D3D11_BIND_VERTEX_BUFFER);
        
m_d3dDevice->CreateBuffer(
  &vertexBufferDesc,
  &vertexBufferData,
  &m_vertexBuffer);
        
// ...

UINT stride = sizeof(VertexPositionColor);
UINT offset = 0;
m_d3dContext->IASetVertexBuffers(
  0,
  1,
  m_vertexBuffer.GetAddressOf(),
  &stride,
  &offset);

Schritt 3: Erstellen und Laden des Indexpuffers

Indexpuffer sind eine effiziente Möglichkeit, damit der Vertex-Shader einzelne Scheitelpunkte nachschlagen kann. Obwohl sie nicht erforderlich sind, verwenden wir sie in diesem Beispielrenderer. Wie bei Vertexpuffern in OpenGL ES 2.0 wird ein Indexpuffer erstellt und als allgemeiner Puffer gebunden, und die zuvor erstellten Vertexindizes werden in ihn kopiert.

Wenn Sie zum Zeichnen bereit sind, binden Sie sowohl den Vertex als auch den Indexpuffer erneut, und rufen Sie glDrawElements auf.

OpenGL ES 2.0: Senden Sie die Indexreihenfolge an den Draw-Aufruf.

GLuint indexBuffer;

// ...

glGenBuffers(1, &renderer->indexBuffer);    
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->indexBuffer);   
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 
  sizeof(GLuint) * CUBE_INDICES, 
  renderer->vertexIndices, 
  GL_STATIC_DRAW);

// ...
// Drawing function

// Bind the index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->indexBuffer);
glDrawElements (GL_TRIANGLES, renderer->numIndices, GL_UNSIGNED_INT, 0);

Bei Direct3D ist es ein bisschen ähnlich, wenn auch etwas didaktischer. Geben Sie den Indexpuffer als Direct3D-Unterressource an den ID3D11DeviceContext an, den Sie beim Konfigurieren von Direct3D erstellt haben. Dazu rufen Sie ID3D11DeviceContext::IASetIndexBuffer mit der konfigurierten Unterressource für das Indexarray wie folgt auf. (Beachten Sie erneut, dass Sie den Zeiger dem CubeIndices-Array dem pSysMem-Feld der D3D11_SUBRESOURCE_DATA Struktur zuweisen.)

Direct3D: Erstellen Sie den Indexpuffer.

m_indexCount = ARRAYSIZE(cubeIndices);

D3D11_SUBRESOURCE_DATA indexBufferData = {0};
indexBufferData.pSysMem = cubeIndices;
indexBufferData.SysMemPitch = 0;
indexBufferData.SysMemSlicePitch = 0;
CD3D11_BUFFER_DESC indexBufferDesc(sizeof(cubeIndices), D3D11_BIND_INDEX_BUFFER);

m_d3dDevice->CreateBuffer(
  &indexBufferDesc,
  &indexBufferData,
  &m_indexBuffer);

// ...

m_d3dContext->IASetIndexBuffer(
  m_indexBuffer.Get(),
  DXGI_FORMAT_R16_UINT,
  0);

Später zeichnen Sie die Dreiecke mit einem Aufruf von ID3D11DeviceContext::D rawIndexed (oder ID3D11DeviceContext::D raw für nicht indizierte Scheitelpunkte) wie folgt. (Für weitere Details springen Sie zu Zeichnen auf dem Bildschirm.)

Direct3D: Zeichnen Sie die indizierten Scheitelpunkte.

m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_d3dContext->IASetInputLayout(m_inputLayout.Get());

// ...

m_d3dContext->DrawIndexed(
  m_indexCount,
  0,
  0);

Vorheriger Schritt

Porten der Shaderobjekte

Nächster Schritt

Port the GLSL

Hinweise

Trennen Sie beim Strukturieren Von Direct3D den Code, der Methoden für ID3D11Device aufruft, in eine Methode, die aufgerufen wird, wenn die Geräteressourcen neu erstellt werden müssen. (In der Direct3D-Projektvorlage befindet sich dieser Code im Rendererobjekt. CreateDeviceResource-Methoden . Der Code, der den Gerätekontext aktualisiert (ID3D11DeviceContext), wird dagegen in der Rendermethode platziert, da hier tatsächlich die Shaderphasen erstellt und die Daten gebunden werden.