Compartir a través de


Portabilidad de los búferes de vértices y los datos

API importantes

En este paso, definirá los búferes de vértices que contendrán las mallas y los búferes de índice que permiten a los sombreadores atravesar los vértices en un orden especificado.

En este punto, vamos a examinar el modelo codificado de forma codificada para la malla de cubo que estamos usando. Ambas representaciones tienen los vértices organizados como una lista de triángulos (en lugar de una franja u otro diseño de triángulo más eficaz). Todos los vértices de ambas representaciones también tienen índices asociados y valores de color. Gran parte del código de Direct3D de este tema hace referencia a variables y objetos definidos en el proyecto de Direct3D.

Este es el cubo para su procesamiento por OpenGL ES 2.0. En la implementación de ejemplo, cada vértice tiene 7 valores flotantes: 3 coordenadas de posición seguidas de 4 valores de color RGBA.

#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
};

Y este es el mismo cubo para su procesamiento por 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
};

Al revisar este código, observará que el cubo del código openGL ES 2.0 se representa en un sistema de coordenadas de la derecha, mientras que el cubo del código específico de Direct3D se representa en un sistema de coordenadas de la izquierda. Al importar sus propios datos de malla, debe invertir las coordenadas del eje Z para el modelo y cambiar los índices de cada malla en consecuencia para recorrer los triángulos según el cambio en el sistema de coordenadas.

Suponiendo que hemos movido correctamente la malla de cubo del sistema de coordenadas OpenGL ES 2.0 a la izquierda direct3D, veamos cómo cargar los datos del cubo para su procesamiento en ambos modelos.

Instrucciones

Paso 1: Crear un diseño de entrada

En OpenGL ES 2.0, los datos de vértices se proporcionan como atributos que los objetos de sombreador proporcionarán y leerán. Normalmente, se proporciona una cadena que contiene el nombre de atributo usado en el GLSL del sombreador al objeto de programa de sombreador y se devuelve una ubicación de memoria que se puede proporcionar al sombreador. En este ejemplo, un objeto de búfer de vértices contiene una lista de estructuras de vértices personalizadas, definidas y con formato como se indica a continuación:

OpenGL ES 2.0: configure los atributos que contienen la información por vértice.

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

En OpenGL ES 2.0, los diseños de entrada son implícitos; toma una GL_ELEMENT_ARRAY_BUFFER de uso general y proporciona el paso y el desplazamiento para que el sombreador de vértices pueda interpretar los datos después de cargarlos. Se informa al sombreador antes de representar qué atributos se asignan a qué partes de cada bloque de datos de vértices con glVertexAttribPointer.

En Direct3D, debe proporcionar un diseño de entrada para describir la estructura de los datos de vértices en el búfer de vértices al crear el búfer, en lugar de antes de dibujar la geometría. Para ello, se usa un diseño de entrada que corresponde al diseño de los datos de nuestros vértices individuales en memoria. ¡Es muy importante especificar esto con precisión!

Aquí, creará una descripción de entrada como una matriz de estructuras de D3D11_INPUT_ELEMENT_DESC .

Direct3D: defina una descripción del diseño de entrada.

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 },
};

Esta descripción de entrada define un vértice como un par de 2 vectores de coordenadas 3D: un vector 3D para almacenar la posición del vértice en coordenadas del modelo y otro vector 3D para almacenar el valor de color RGB asociado al vértice. En este caso, se usa el formato de punto flotante de 3x32 bits, elementos de los que representamos en el código como XMFLOAT3(X.Xf, X.Xf, X.Xf). Debe usar tipos de la biblioteca DirectXMath siempre que controle los datos que usará un sombreador, ya que garantiza el empaquetado y la alineación adecuados de esos datos. (Por ejemplo, use XMFLOAT3 o XMFLOAT4 para datos vectoriales y XMFLOAT4X4 para matrices).

Para obtener una lista de todos los tipos de formato posibles, consulte DXGI_FORMAT.

Con el diseño de entrada por vértice definido, se crea el objeto de diseño. En el código siguiente, se escribe en m_inputLayout, una variable de tipo ComPtr (que apunta a un objeto de tipo ID3D11InputLayout). fileData contiene el objeto sombreador de vértices compilado del paso anterior, Portar los sombreadores.

Direct3D: cree el diseño de entrada usado por el búfer de vértices.

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

// ...

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

Hemos definido el diseño de entrada. Ahora, vamos a crear un búfer que use este diseño y cárgelo con los datos de malla del cubo.

Paso 2: Crear y cargar los búferes de vértices

En OpenGL ES 2.0, se crea un par de búferes, uno para los datos de posición y otro para los datos de color. (También puede crear una estructura que contenga y un solo búfer). Puede enlazar cada búfer y escribir datos de posición y color en ellos. Más adelante, durante la función de representación, vuelva a enlazar los búferes y proporcione al sombreador el formato de los datos en el búfer para que pueda interpretarlos correctamente.

OpenGL ES 2.0: enlace de los búferes de vértices

// 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);   

En Direct3D, los búferes accesibles para sombreador se representan como estructuras D3D11_SUBRESOURCE_DATA. Para enlazar la ubicación de este búfer al objeto de sombreador, debe crear una estructura de CD3D11_BUFFER_DESC para cada búfer con ID3DDevice::CreateBuffer y, a continuación, establecer el búfer del contexto del dispositivo Direct3D llamando a un método set específico del tipo de búfer, como ID3DDeviceContext::IASetVertexBuffers.

Al establecer el búfer, debe establecer el intervalo (el tamaño del elemento de datos para un vértice individual) así como el desplazamiento (donde se inicia realmente la matriz de datos de vértices) desde el principio del búfer.

Observe que asignamos el puntero a la matriz vertexIndices al campo pSysMem de la estructura de D3D11_SUBRESOURCE_DATA. Si esto no es correcto, la malla estará dañada o vacía.

Direct3D: crear y establecer el búfer de vértices

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);

Paso 3: Creación y carga del búfer de índice

Los búferes de índice son una manera eficaz de permitir que el sombreador de vértices busque vértices individuales. Aunque no son necesarios, los usamos en este representador de ejemplo. Al igual que con los búferes de vértices en OpenGL ES 2.0, se crea un búfer de índice y se enlaza como búfer de uso general y los índices de vértice que creó anteriormente se copian en él.

Cuando esté listo para dibujar, vuelva a enlazar el vértice y el búfer de índice y llame a glDrawElements.

OpenGL ES 2.0: envíe el orden de índice a la llamada de dibujo.

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);

Con Direct3D, es un proceso muy similar, aunque un poco más didáctico. Proporcione el búfer de índice como un subrecurso direct3D al id3D11DeviceContext que creó al configurar Direct3D. Para ello, llame a ID3D11DeviceContext::IASetIndexBuffer con el subrecurso configurado para la matriz de índices, como se indica a continuación. (De nuevo, observe que asigna el puntero a la matriz cubeIndices al campo pSysMem de la estructura D3D11_SUBRESOURCE_DATA ).

Direct3D: cree el búfer de índice.

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);

Más adelante, dibujará los triángulos con una llamada a ID3D11DeviceContext::D rawIndexed (o ID3D11DeviceContext::D raw para vértices no indexados), como se indica a continuación. (Para obtener más detalles, vaya a Dibujar en la pantalla).

Direct3D: dibuje los vértices indexados.

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

// ...

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

Paso anterior

Migrar los objetos del sombreador

Paso siguiente

Puerto de GLSL

Comentarios

Al estructurar Direct3D, separe el código que llama a los métodos de ID3D11Device en un método al que se llama cada vez que es necesario volver a crear los recursos del dispositivo. (En la plantilla de proyecto de Direct3D, este código se encuentra en el objeto de representadorMétodos CreateDeviceResource . El código que actualiza el contexto del dispositivo (ID3D11DeviceContext), por otro lado, se coloca en el método Render, ya que aquí es donde realmente construye las fases del sombreador y enlaza los datos.