Partager via


Porter les objets nuanceur

API importantes

Lors du portage du convertisseur simple à partir d’OpenGL ES 2.0, la première étape consiste à configurer les objets de nuanceur de vertex et de fragments équivalents dans Direct3D 11 et à s’assurer que le programme principal peut communiquer avec les objets de nuanceur après leur compilation.

Remarque

Avez-vous créé un projet Direct3D ? Si ce n’est pas le cas, suivez les instructions fournies dans Créer un projet DirectX 11 pour plateforme Windows universelle (UWP). Cette procédure pas à pas suppose que vous avez créé les ressources DXGI et Direct3D pour dessiner à l’écran, et qui sont fournies dans le modèle.

Tout comme OpenGL ES 2.0, les nuanceurs compilés dans Direct3D doivent être associés à un contexte de dessin. Toutefois, Direct3D n’a pas le concept d’un objet de programme de nuanceur par elle ; Au lieu de cela, vous devez affecter les nuanceurs directement à un ID3D11DeviceContext. Cette étape suit le processus OpenGL ES 2.0 pour la création et la liaison d’objets de nuanceur, et vous fournit les comportements d’API correspondants dans Direct3D.

Instructions

Étape 1 : Compiler les nuanceurs

Dans cet exemple OpenGL ES 2.0 simple, les nuanceurs sont stockés en tant que fichiers texte et chargés en tant que données de chaîne pour la compilation au moment de l’exécution.

OpenGL ES 2.0 : Compiler un nuanceur

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;

}

Dans Direct3D, les nuanceurs ne sont pas compilés pendant l’exécution ; ils sont toujours compilés en fichiers CSO lorsque le reste du programme est compilé. Lorsque vous compilez votre application avec Microsoft Visual Studio, les fichiers HLSL sont compilés en fichiers CSO (.cso) que votre application doit charger. Veillez à inclure ces fichiers d’authentification unique avec votre application lorsque vous la empaqueter !

Notez que l’exemple suivant effectue le chargement et la compilation du nuanceur de manière asynchrone à l’aide du mot clé automatique et de la syntaxe lambda. ReadDataAsync() est une méthode implémentée pour le modèle qui lit dans un fichier CSO sous la forme d’un tableau de données d’octets (fileData).

 

Direct3D 11 : Compiler un nuanceur

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

Étape 2 : Créer et charger les nuanceurs de vertex et de fragments (pixels)

OpenGL ES 2.0 a la notion d’un nuanceur « program », qui sert d’interface entre le programme principal s’exécutant sur l’UC et les nuanceurs, qui sont exécutés sur le GPU. Les nuanceurs sont compilés (ou chargés à partir de sources compilées) et associés à un programme, ce qui permet l’exécution sur le GPU.

OpenGL ES 2.0 : chargement des nuanceurs de vertex et de fragments dans un programme d’ombrage

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 n’a pas le concept d’un objet de programme de nuanceur. Au lieu de cela, les nuanceurs sont créés quand l’une des méthodes de création du nuanceur sur l’interface ID3D11Device (par exemple, ID3D11Device ::CreateVertexShader ou ID3D11Device ::CreatePixelShader) est appelée. Pour définir les nuanceurs pour le contexte de dessin actuel, nous leur fournissons l’ID3D11DeviceContext correspondant avec une méthode de nuanceur set, telle que ID3D11DeviceContext ::VSSetShader pour le nuanceur de vertex ou ID3D11DeviceContext ::P SSetShader pour le nuanceur de fragments.

Direct3D 11 : Définissez les nuanceurs pour le contexte de dessin du périphérique graphique.

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

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

Étape 3 : Définir les données à fournir aux nuanceurs

Dans notre exemple OpenGL ES 2.0, nous avons un uniforme à déclarer pour le pipeline de nuanceur :

  • u_mvpMatrix : tableau 4x4 de floats qui représente la matrice finale de transformation de projection de vue de modèle qui prend les coordonnées du modèle pour le cube et les transforme en coordonnées de projection 2D pour la conversion d’analyse.

Et deux valeurs d’attribut pour les données de vertex :

  • a_position : vecteur à 4 virgules flottantes pour les coordonnées de modèle d’un sommet.
  • a_color : vecteur à 4 flottants pour la valeur de couleur RVBA associée au sommet.

Ouvrir GL ES 2.0 : définitions GLSL pour les uniformes et les attributs

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

Les variables de programme principales correspondantes sont définies en tant que champs sur l’objet renderer, dans ce cas. (Reportez-vous à l’en-tête dans Guide pratique pour porter un convertisseur OpenGL ES 2.0 simple vers Direct3D 11.) Une fois que nous l’avons fait, nous devons spécifier les emplacements en mémoire où le programme principal fournit ces valeurs pour le pipeline du nuanceur, que nous faisons généralement juste avant un appel de dessin :

OpenGL ES 2.0 : marquage de l’emplacement des données uniformes et d’attributs


// 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 n’a pas le concept d’un « attribut » ou d’un « uniforme » dans le même sens (ou, au moins, il ne partage pas cette syntaxe). Il a plutôt des mémoires tampons constantes, représentées sous forme de sous-ressources Direct3D , ressources partagées entre le programme principal et les programmes de nuanceur. Certaines de ces sous-ressources, telles que les positions de vertex et les couleurs, sont décrites comme sémantiques HLSL. Pour plus d’informations sur les mémoires tampons constantes et la sémantique HLSL, telles qu’elles sont liées aux concepts OpenGL ES 2.0, lisez les objets de mémoire tampon de trame port, les uniformes et les attributs.

Lors du déplacement de ce processus vers Direct3D, nous convertissons l’uniforme en mémoire tampon constante Direct3D (cbuffer) et lui affectons un registre pour la recherche avec la sémantique HLSL du registre . Les deux attributs de vertex sont gérés en tant qu’éléments d’entrée aux étapes de pipeline du nuanceur, et sont également attribués à la sémantique HLSL (POSITION et COLOR0) qui informent les nuanceurs. Le nuanceur de pixels prend un SV_POSITION, avec le préfixe SV_ indiquant qu’il s’agit d’une valeur système générée par le GPU. (Dans ce cas, il s’agit d’une position de pixel générée pendant la conversion d’analyse.) VertexShaderInput et PixelShaderInput ne sont pas déclarés comme mémoires tampons constantes, car l’ancien sera utilisé pour définir la mémoire tampon de vertex (voir Port les mémoires tampons et les données de vertex), et les données de ce dernier sont générées à la suite d’une étape précédente du pipeline, qui, dans ce cas, est le nuanceur de vertex.

Direct3D : définitions HLSL pour les mémoires tampons constantes et les données de vertex

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

Pour plus d’informations sur le portage vers des mémoires tampons constantes et l’application de la sémantique HLSL, lisez les objets de mémoire tampon de trame port, les uniformes et les attributs.

Voici les structures de la disposition des données passées au pipeline du nuanceur avec une mémoire tampon de vertex ou constante.

Direct3D 11 : déclaration de la disposition des mémoires tampons constantes et vertex

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

Utilisez les types DirectXMath XM* pour vos éléments de mémoire tampon constante, car ils fournissent un emballage et un alignement appropriés pour le contenu lorsqu’ils sont envoyés au pipeline du nuanceur. Si vous utilisez des types et tableaux flottants de plateforme Windows standard, vous devez effectuer l’emballage et l’alignement vous-même.

Pour lier une mémoire tampon constante, créez une description de disposition en tant que structure CD3D11_BUFFER_DESC et passez-la à ID3DDevice ::CreateBuffer. Ensuite, dans votre méthode de rendu, passez la mémoire tampon constante à ID3D11DeviceContext ::UpdateSubresource avant de dessiner.

Direct3D 11 : Lier la mémoire tampon constante

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

La mémoire tampon de vertex est créée et mise à jour de la même façon et est abordée à l’étape suivante, Portez les mémoires tampons de vertex et les données.

Étape suivante

Porter les mémoires tampons et les données de vertex

Guide pratique pour porter un convertisseur OpenGL ES 2.0 simple vers Direct3D 11

Porter les mémoires tampons et les données de vertex

Porter le langage GLSL

Dessiner sur l’écran