Condividi tramite


Convertire gli oggetti shader

API importanti

Quando si esegue la conversione del renderer semplice da OpenGL ES 2.0, il primo passaggio consiste nel configurare gli oggetti vertex e fragment shader equivalenti in Direct3D 11 e per assicurarsi che il programma principale possa comunicare con gli oggetti shader dopo la compilazione.

Nota

Hai creato un nuovo progetto Direct3D? In caso contrario, seguire le istruzioni in Creare un nuovo progetto DirectX 11 per piattaforma UWP (Universal Windows Platform) (UWP). Questa procedura dettagliata presuppone che siano state create le risorse DXGI e Direct3D per il disegno sullo schermo e fornite nel modello.

Analogamente a OpenGL ES 2.0, gli shader compilati in Direct3D devono essere associati a un contesto di disegno. Tuttavia, Direct3D non ha il concetto di un oggetto programma shader per se; È invece necessario assegnare gli shader direttamente a un ID3D11DeviceContext. Questo passaggio segue il processo OpenGL ES 2.0 per la creazione e l'associazione di oggetti shader e fornisce i comportamenti API corrispondenti in Direct3D.

Istruzioni

Passaggio 1: Compilare gli shader

In questo semplice esempio openGL ES 2.0, gli shader vengono archiviati come file di testo e caricati come dati stringa per la compilazione in fase di esecuzione.

OpenGL ES 2.0: compilare uno shader

GLuint __cdecl CompileShader (GLenum shaderType, const char *shaderSrcStr)
// shaderType can be GL_VERTEX_SHADER or GL_FRAGMENT_SHADER. Returns 0 if compilation fails.
{
  GLuint shaderHandle;
  GLint compiledShaderHandle;
   
  // Create an empty shader object.
  shaderHandle = glCreateShader(shaderType);

  if (shaderHandle == 0)
  return 0;

  // Load the GLSL shader source as a string value. You could obtain it from
  // from reading a text file or hardcoded.
  glShaderSource(shaderHandle, 1, &shaderSrcStr, NULL);
   
  // Compile the shader.
  glCompileShader(shaderHandle);

  // Check the compile status
  glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compiledShaderHandle);

  if (!compiledShaderHandle) // error in compilation occurred
  {
    // Handle any errors here.
              
    glDeleteShader(shaderHandle);
    return 0;
  }

  return shaderHandle;

}

In Direct3D gli shader non vengono compilati durante la fase di esecuzione; vengono sempre compilati in file CSO quando il resto del programma viene compilato. Quando si compila l'app con Microsoft Visual Studio, i file HLSL vengono compilati in file CSO (con estensione cso) che l'app deve caricare. Assicurarsi di includere questi file CSO con l'app al momento del pacchetto.

Nota L'esempio seguente esegue il caricamento e la compilazione dello shader in modo asincrono usando la parola chiave auto e la sintassi lambda. ReadDataAsync() è un metodo implementato per il modello che legge in un file CSO come matrice di dati di byte (fileData).

 

Direct3D 11: Compilare uno shader

auto loadVSTask = DX::ReadDataAsync(m_projectDir + "SimpleVertexShader.cso");
auto loadPSTask = DX::ReadDataAsync(m_projectDir + "SimplePixelShader.cso");

auto createVSTask = loadVSTask.then([this](Platform::Array<byte>^ fileData) {

m_d3dDevice->CreateVertexShader(
  fileData->Data,
  fileData->Length,
  nullptr,
  &m_vertexShader);

auto createPSTask = loadPSTask.then([this](Platform::Array<byte>^ fileData) {
  m_d3dDevice->CreatePixelShader(
    fileData->Data,
    fileData->Length,
    nullptr,
    &m_pixelShader;
};

Passaggio 2: Creare e caricare i vertex e fragment shader (pixel)

OpenGL ES 2.0 ha la nozione di "programma" shader, che funge da interfaccia tra il programma principale in esecuzione sulla CPU e gli shader, che vengono eseguiti sulla GPU. Gli shader vengono compilati (o caricati da origini compilate) e associati a un programma, che consente l'esecuzione nella GPU.

OpenGL ES 2.0: caricamento dei vertex shader e dei frammenti in un programma di ombreggiatura

GLuint __cdecl LoadShaderProgram (const char *vertShaderSrcStr, const char *fragShaderSrcStr)
{
  GLuint programObject, vertexShaderHandle, fragmentShaderHandle;
  GLint linkStatusCode;

  // Load the vertex shader and compile it to an internal executable format.
  vertexShaderHandle = CompileShader(GL_VERTEX_SHADER, vertShaderSrcStr);
  if (vertexShaderHandle == 0)
  {
    glDeleteShader(vertexShaderHandle);
    return 0;
  }

   // Load the fragment/pixel shader and compile it to an internal executable format.
  fragmentShaderHandle = CompileShader(GL_FRAGMENT_SHADER, fragShaderSrcStr);
  if (fragmentShaderHandle == 0)
  {
    glDeleteShader(fragmentShaderHandle);
    return 0;
  }

  // Create the program object proper.
  programObject = glCreateProgram();
   
  if (programObject == 0)    return 0;

  // Attach the compiled shaders
  glAttachShader(programObject, vertexShaderHandle);
  glAttachShader(programObject, fragmentShaderHandle);

  // Compile the shaders into binary executables in memory and link them to the program object..
  glLinkProgram(programObject);

  // Check the project object link status and determine if the program is available.
  glGetProgramiv(programObject, GL_LINK_STATUS, &linkStatusCode);

  if (!linkStatusCode) // if link status <> 0
  {
    // Linking failed; delete the program object and return a failure code (0).

    glDeleteProgram (programObject);
    return 0;
  }

  // Deallocate the unused shader resources. The actual executables are part of the program object.
  glDeleteShader(vertexShaderHandle);
  glDeleteShader(fragmentShaderHandle);

  return programObject;
}

// ...

glUseProgram(renderer->programObject);

Direct3D non ha il concetto di un oggetto programma shader. Gli shader vengono invece creati quando viene chiamato uno dei metodi di creazione dello shader nell'interfaccia ID3D11Device (ad esempio ID3D11Device::CreateVertexShader o ID3D11Device::CreatePixelShader). Per impostare gli shader per il contesto di disegno corrente, vengono forniti a ID3D11DeviceContext corrispondente con un metodo set shader, ad esempio ID3D11DeviceContext::VSSetShader per il vertex shader o ID3D11DeviceContext::PSSetShader per il frammento shader.

Direct3D 11: impostare gli shader per il contesto di disegno del dispositivo grafico.

m_d3dContext->VSSetShader(
  m_vertexShader.Get(),
  nullptr,
  0);

m_d3dContext->PSSetShader(
  m_pixelShader.Get(),
  nullptr,
  0);

Passaggio 3: Definire i dati da fornire agli shader

Nell'esempio openGL ES 2.0 è disponibile un'uniforme da dichiarare per la pipeline dello shader:

  • u_mvpMatrix: una matrice 4x4 di float che rappresenta la matrice finale di trasformazione della proiezione di visualizzazione modello che accetta le coordinate del modello per il cubo e le trasforma in coordinate di proiezione 2D per la conversione dell'analisi.

E due valori di attributo per i dati dei vertici:

  • a_position: vettore a 4 float per le coordinate del modello di un vertice.
  • a_color: vettore a 4 float per il valore del colore RGBA associato al vertice.

Open GL ES 2.0: definizioni GLSL per le uniformi e gli attributi

uniform mat4 u_mvpMatrix;
attribute vec4 a_position;
attribute vec4 a_color;

Le variabili di programma principali corrispondenti sono definite come campi nell'oggetto renderer, in questo caso. Fare riferimento all'intestazione in Procedura: convertire un semplice renderer OpenGL ES 2.0 in Direct3D 11. Al termine, è necessario specificare i percorsi in memoria in cui il programma principale fornirà questi valori per la pipeline dello shader, che in genere si esegue subito prima di una chiamata di disegno:

OpenGL ES 2.0: contrassegnare la posizione dei dati uniformi e degli attributi


// Inform the shader of the attribute locations
loc = glGetAttribLocation(renderer->programObject, "a_position");
glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 
    sizeof(Vertex), 0);
glEnableVertexAttribArray(loc);

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


// Inform the shader program of the uniform location
renderer->mvpLoc = glGetUniformLocation(renderer->programObject, "u_mvpMatrix");

Direct3D non ha il concetto di "attributo" o "uniforme" nello stesso senso (o, almeno, non condivide questa sintassi). Contiene invece buffer costanti, rappresentati come sottorisorse Direct3D, ovvero risorse condivise tra il programma principale e i programmi shader. Alcune di queste sottorisorse, ad esempio le posizioni dei vertici e i colori, sono descritte come semantica HLSL. Per altre informazioni sui buffer costanti e sulla semantica HLSL in relazione ai concetti di OpenGL ES 2.0, vedere Oggetti buffer dei frame di porta, uniformi e attributi.

Quando si sposta questo processo in Direct3D, si converte l'uniforme in un buffer costante Direct3D (cbuffer) e si assegna un registro per la ricerca con la semantica HLSL del registro . I due attributi dei vertici vengono gestiti come elementi di input per le fasi della pipeline dello shader e vengono assegnati anche semantica HLSL (POSITION e COLOR0) che informano gli shader. Il pixel shader accetta un SV_POSITION, con il prefisso SV_ che indica che si tratta di un valore di sistema generato dalla GPU. In questo caso, si tratta di una posizione in pixel generata durante la conversione dell'analisi. VertexShaderInput e PixelShaderInput non vengono dichiarati come buffer costanti perché il primo verrà usato per definire il buffer dei vertici (vedere Port the vertex buffers and data) e i dati per quest'ultimo vengono generati come risultato di una fase precedente nella pipeline, che in questo caso è il vertex shader.

Direct3D: definizioni HLSL per i buffer costanti e i dati dei vertici

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

// Per-vertex data used as input to the vertex shader.
struct VertexShaderInput
{
  float4 pos : POSITION;
  float4 color : COLOR0;
};

// Per-vertex color data passed through the pixel shader.
struct PixelShaderInput
{
  float4 pos : SV_POSITION;
  float3 color : COLOR0;
};

Per altre informazioni sulla conversione in buffer costanti e sull'applicazione della semantica HLSL, vedere Port frame buffer objects, uniforms e attributes.For more info on port port frame buffer objects, uniforms, and attributes.

Ecco le strutture per il layout dei dati passati alla pipeline dello shader con un buffer costante o di vertice.

Direct3D 11: dichiarazione del layout di buffer costanti e vertici

// Constant buffer used to send MVP matrices to the vertex shader.
struct ModelViewProjectionConstantBuffer
{
  DirectX::XMFLOAT4X4 modelViewProjection;
};

// Used to send per-vertex data to the vertex shader.
struct VertexPositionColor
{
  DirectX::XMFLOAT4 pos;
  DirectX::XMFLOAT4 color;
};

Usare i tipi DirectXMath XM* per gli elementi del buffer costante, poiché forniscono la compressione e l'allineamento appropriati per il contenuto quando vengono inviati alla pipeline dello shader. Se si usano i tipi float e le matrici della piattaforma Windows standard, è necessario eseguire manualmente la compressione e l'allineamento.

Per associare un buffer costante, creare una descrizione del layout come struttura CD3D11_BUFFER_DESC e passarla a ID3DDevice::CreateBuffer. Quindi, nel metodo di rendering passare il buffer costante a ID3D11DeviceContext::UpdateSubresource prima del disegno.

Direct3D 11: Associare il buffer costante

CD3D11_BUFFER_DESC constantBufferDesc(sizeof(ModelViewProjectionConstantBuffer), D3D11_BIND_CONSTANT_BUFFER);

m_d3dDevice->CreateBuffer(
  &constantBufferDesc,
  nullptr,
  &m_constantBuffer);

// ...

// Only update shader resources that have changed since the last frame.
m_d3dContext->UpdateSubresource(
  m_constantBuffer.Get(),
  0,
  NULL,
  &m_constantBufferData,
  0,
  0);

Il vertex buffer viene creato e aggiornato in modo analogo e viene illustrato nel passaggio successivo Convertire i vertex buffer e i dati.

Passaggio successivo

Convertire i vertex buffer e i dati

Procedura: convertire un semplice renderer OpenGL ES 2.0 in Direct3D 11

Convertire i vertex buffer e i dati

Convertire il codice GLSL

Disegnare sullo schermo