Compartir a través de


Comparar búferes, uniformes y atributos de vértices de OpenGL ES 2.0 con Direct3D

API importantes

Durante el proceso de migración a Direct3D 11 desde OpenGL ES 2.0, debe cambiar la sintaxis y el comportamiento de la API para pasar datos entre la aplicación y los programas de sombreador.

En OpenGL ES 2.0, los datos se pasan a y desde programas de sombreador de cuatro maneras: como uniformes para datos constantes, como atributos para datos de vértices, como objetos de búfer para otros datos de recursos (como texturas). En Direct3D 11, estos se asignan aproximadamente a búferes de constantes, búferes de vértices y subrecursos. A pesar de la comúnidad superficial, se controlan bastante diferentes en el uso.

Esta es la asignación básica.

OpenGL ES 2.0 Direct3D 11
uniform campo de búfer de constantes (cbuffer).
atributo campo de elemento de búfer de vértices, designado por un diseño de entrada y marcado con una semántica HLSL específica.
buffer (objeto) búfer; Consulte D3D11_SUBRESOURCE_DATA y D3D11_BUFFER_DESC y para obtener definiciones de búfer de uso general.
objeto de búfer de marco (FBO) representar destinos; Consulte ID3D11RenderTargetView con ID3D11Texture2D.
back buffer cadena de intercambio con superficie de "búfer de reserva"; Consulte IDXGISwapChain1 con IDXGISurface1 adjunto.

 

Búferes de puerto

En OpenGL ES 2.0, el proceso para crear y enlazar cualquier tipo de búfer generalmente sigue este patrón.

  • Llame a glGenBuffers para generar uno o varios búferes y devolverles los identificadores.
  • Llame a glBindBuffer para definir el diseño de un búfer, como GL_ELEMENT_ARRAY_BUFFER.
  • Llame a glBufferData para rellenar el búfer con datos específicos (como estructuras de vértices, datos de índice o datos de color) en un diseño específico.

El tipo de búfer más común es el búfer de vértices, que contiene mínimamente las posiciones de los vértices en algún sistema de coordenadas. En uso típico, un vértice se representa mediante una estructura que contiene las coordenadas de posición, un vector normal a la posición del vértice, un vector tangente a la posición del vértice y coordenadas de búsqueda de textura (uv). El búfer contiene una lista contigua de estos vértices, en algún orden (como una lista de triángulos, o tira o ventilador) y que representan colectivamente los polígonos visibles en la escena. (En Direct3D 11, así como OpenGL ES 2.0, es ineficaz tener varios búferes de vértices por llamada de dibujo).

Este es un ejemplo de un búfer de vértices y un búfer de índice creado con OpenGL ES 2.0:

OpenGL ES 2.0: crear y rellenar un búfer de vértices y un búfer de índice.

glGenBuffers(1, &renderer->vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, renderer->vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * CUBE_VERTICES, renderer->vertices, GL_STATIC_DRAW);

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

Otros búferes incluyen búferes de píxeles y mapas, como texturas. La canalización del sombreador puede representarse en búferes de textura (mapas pix) o representar objetos de búfer y usar esos búferes en futuras pasadas del sombreador. En el caso más sencillo, el flujo de llamadas es:

  • Llame a glGenFramebuffers para generar un objeto de búfer de fotogramas.
  • Llame a glBindFramebuffer para enlazar el objeto de búfer de fotogramas para escribir.
  • Llame a glFramebufferTexture2D para dibujar en un mapa de textura especificado.

En Direct3D 11, los elementos de datos de búfer se consideran "subrecursos" y pueden variar desde elementos de datos de vértices individuales a texturas de mapa MIP.

Direct3D 11: Crear y rellenar un búfer de vértices y un búfer de índice.

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

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

Los mapas o búferes de píxeles grabables, como un búfer de fotogramas, se pueden crear como objetos ID3D11Texture2D. Estos se pueden enlazar como recursos a un ID3D11RenderTargetView o ID3D11ShaderResourceView, que, una vez dibujado, se pueden mostrar con la cadena de intercambio asociada o pasarse a un sombreador, respectivamente.

Direct3D 11: Crear un objeto de búfer de fotogramas.

ComPtr<ID3D11RenderTargetView> m_d3dRenderTargetViewWin;
// ...
ComPtr<ID3D11Texture2D> frameBuffer;

m_swapChainCoreWindow->GetBuffer(0, IID_PPV_ARGS(&frameBuffer));
m_d3dDevice->CreateRenderTargetView(
  frameBuffer.Get(),
  nullptr,
  &m_d3dRenderTargetViewWin);

Cambiar uniformes y objetos de búfer uniformes a búferes de constantes de Direct3D

En Open GL ES 2.0, los uniformes son el mecanismo para suministrar datos constantes a programas de sombreador individuales. Los sombreadores no pueden modificar estos datos.

Establecer un uniforme normalmente implica proporcionar uno de los métodos glUniform* con la ubicación de carga en la GPU junto con un puntero a los datos en la memoria de la aplicación. Después de ejecutar el método glUniform*, los datos uniformes están en la memoria de GPU y son accesibles por los sombreadores que han declarado ese uniforme. Se espera asegurarse de que los datos se empaquetan de forma que el sombreador pueda interpretarlos en función de la declaración uniforme en el sombreador (mediante tipos compatibles).

OpenGL ES 2.0 Crear un uniforme y cargar datos en él

renderer->mvpLoc = glGetUniformLocation(renderer->programObject, "u_mvpMatrix");

// ...

glUniformMatrix4fv(renderer->mvpLoc, 1, GL_FALSE, (GLfloat*) &renderer->mvpMatrix.m[0][0]);

En el GLSL de un sombreador, la declaración uniforme correspondiente tiene este aspecto:

Open GL ES 2.0: declaración uniforme GLSL

uniform mat4 u_mvpMatrix;

Direct3D designa datos uniformes como "búferes de constantes", que, al igual que los uniformes, contienen datos constantes proporcionados a los sombreadores individuales. Al igual que con los búferes uniformes, es importante empaquetar los datos del búfer de constantes en la memoria de forma idéntica a la forma en que el sombreador espera interpretarlos. El uso de tipos DirectXMath (como XMFLOAT4) en lugar de tipos de plataforma (como float* o float[4]) garantiza la alineación adecuada del elemento de datos.

Los búferes de constantes deben tener un registro de GPU asociado que se usa para hacer referencia a esos datos en la GPU. Los datos se empaquetan en la ubicación del registro, tal como se indica en el diseño del búfer.

Direct3D 11: Crear un búfer de constantes y cargar datos en él

struct ModelViewProjectionConstantBuffer
{
     DirectX::XMFLOAT4X4 mvp;
};

// ...

ModelViewProjectionConstantBuffer   m_constantBufferData;

// ...

XMStoreFloat4x4(&m_constantBufferData.mvp, mvpMatrix);

CD3D11_BUFFER_DESC constantBufferDesc(sizeof(ModelViewProjectionConstantBuffer), D3D11_BIND_CONSTANT_BUFFER);
m_d3dDevice->CreateBuffer(
  &constantBufferDesc,
  nullptr,
  &m_constantBuffer);

En la HLSL de un sombreador, la declaración de búfer de constantes correspondiente tiene este aspecto:

Direct3D 11: declaración HLSL del búfer de constantes

cbuffer ModelViewProjectionConstantBuffer : register(b0)
{
  matrix mvp;
};

Tenga en cuenta que se debe declarar un registro para cada búfer de constantes. Los diferentes niveles de características de Direct3D tienen registros máximos disponibles diferentes, por lo que no supere el número máximo del nivel de característica más bajo que tenga como destino.

Portabilidad de atributos de vértice a diseños de entrada de Direct3D y semántica de HLSL

Dado que la canalización del sombreador puede modificar los datos de vértices, OpenGL ES 2.0 requiere que se especifiquen como "atributos" en lugar de "uniformes". (Esto ha cambiado en versiones posteriores de OpenGL y GLSL). Los datos específicos del vértice, como la posición del vértice, los normales, las tangentes y los valores de color se proporcionan a los sombreadores como valores de atributo. Estos valores de atributo corresponden a desplazamientos específicos para cada elemento de los datos de vértices; por ejemplo, el primer atributo podría apuntar al componente de posición de un vértice individual y el segundo al normal, etc.

El proceso básico para mover los datos del búfer de vértices de la memoria principal a la GPU tiene este aspecto:

  • Cargue los datos de vértices con glBindBuffer.
  • Obtenga la ubicación de los atributos en la GPU con glGetAttribLocation. Llámalo para cada atributo del elemento de datos de vértices.
  • Llame a glVertexAttribPointer para proporcionar el tamaño de atributo correcto y el desplazamiento dentro de un elemento de datos de vértice individual. Haga esto para cada atributo.
  • Habilite la información de diseño de datos de vértices con glEnableVertexAttribArray.

OpenGL ES 2.0: carga de datos de búfer de vértices en el atributo del sombreador

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->vertexBuffer);
loc = glGetAttribLocation(renderer->programObject, "a_position");

glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 
  sizeof(Vertex), 0);
loc = glGetAttribLocation(renderer->programObject, "a_color");
glEnableVertexAttribArray(loc);

glVertexAttribPointer(loc, 4, GL_FLOAT, GL_FALSE, 
  sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
glEnableVertexAttribArray(loc);

Ahora, en el sombreador de vértices, declara atributos con los mismos nombres que definió en la llamada a glGetAttribLocation.

OpenGL ES 2.0: Declaración de un atributo en GLSL

attribute vec4 a_position;
attribute vec4 a_color;                     

De alguna manera, el mismo proceso se mantiene para Direct3D. En lugar de los atributos, los datos de vértices se proporcionan en búferes de entrada, que incluyen búferes de vértices y los búferes de índice correspondientes. Sin embargo, dado que Direct3D no tiene la declaración "attribute", debe especificar un diseño de entrada que declare el componente individual de los elementos de datos en el búfer de vértices y la semántica de HLSL que indique dónde y cómo los componentes se van a interpretar mediante el sombreador de vértices. La semántica de HLSL requiere que defina el uso de cada componente con una cadena específica que informe al motor de sombreador en cuanto a su propósito. Por ejemplo, los datos de posición de vértice se marcan como POSITION, los datos normales se marcan como NORMAL y los datos de color del vértice se marcan como COLOR. (Otras fases del sombreador también requieren semántica específica y esas semánticas tienen diferentes interpretaciones basadas en la fase del sombreador). Para obtener más información sobre la semántica de HLSL, lea Puerto de la canalización del sombreador y semántica de HLSL.

Colectivamente, el proceso de establecimiento de los búferes de vértices e índices, y el establecimiento del diseño de entrada se denomina fase "Ensamblado de entrada" (IA) de la canalización de gráficos direct3D.

Direct3D 11: Configuración de la fase de ensamblado de entrada

// Set up the IA stage corresponding to the current draw operation.
UINT stride = sizeof(VertexPositionColor);
UINT offset = 0;
m_d3dContext->IASetVertexBuffers(
        0,
        1,
        m_vertexBuffer.GetAddressOf(),
        &stride,
        &offset);

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

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

Un diseño de entrada se declara y se asocia a un sombreador de vértices declarando el formato del elemento de datos de vértices y la semántica usada para cada componente. El diseño de datos del elemento de vértice descrito en el D3D11_INPUT_ELEMENT_DESC que cree debe corresponder al diseño de la estructura correspondiente. Aquí, creará un diseño para los datos de vértices que tiene dos componentes:

  • Coordenada de posición de vértice, representada en la memoria principal como una XMFLOAT3, que es una matriz alineada de 3 valores de punto flotante de 3 32 bits para las coordenadas (x, y, z).
  • Valor de color de vértice, representado como un XMFLOAT4, que es una matriz alineada de 4 valores de punto flotante de 32 bits para el color (RGBA).

Asigne una semántica para cada una, así como un tipo de formato. A continuación, pase la descripción a ID3D11Device1::CreateInputLayout. El diseño de entrada se usa cuando llamamos a ID3D11DeviceContext1::IASetInputLayout al configurar el ensamblado de entrada durante nuestro método de representación.

Direct3D 11: Descripción de un diseño de entrada con semántica específica

ComPtr<ID3D11InputLayout> m_inputLayout;

// ...

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

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

// ...
// When we start the drawing process...

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

Por último, asegúrese de que el sombreador pueda comprender los datos de entrada declarando la entrada. La semántica asignada en el diseño se usa para seleccionar las ubicaciones correctas en la memoria de GPU.

Direct3D 11: Declaración de datos de entrada del sombreador con semántica de HLSL

struct VertexShaderInput
{
  float3 pos : POSITION;
  float3 color : COLOR;
};