Partager via


Créer des nuanceurs et dessiner des primitives

Ici, nous vous montrons comment utiliser des fichiers sources HLSL pour compiler et créer des nuanceurs que vous pouvez ensuite utiliser pour dessiner des primitives sur l’affichage.

Nous créons et dessinons un triangle jaune à l’aide de nuanceurs de vertex et de pixels. Après avoir créé l’appareil Direct3D, la chaîne d’échange et la vue de cible de rendu, nous lisons les données des fichiers objet du nuanceur binaire sur le disque.

Objectif : créer des nuanceurs et dessiner des primitives.

Prérequis

Nous partons du principe que vous connaissez C++. Vous avez également besoin d’une expérience de base avec les concepts de programmation graphique.

Nous partons également du principe que vous avez suivi le guide de démarrage rapide : configuration des ressources DirectX et affichage d’une image.

Durée d’exécution : 20 minutes.

Instructions

1. Compilation de fichiers sources HLSL

Microsoft Visual Studio utilise le compilateur de code HLSL fxc.exe pour compiler les fichiers sources .hlsl (SimpleVertexShader.hlsl et SimplePixelShader.hlsl) en fichiers objet de nuanceur binaire .cso (SimpleVertexShader.cso et SimplePixelShader.cso). Pour plus d’informations sur le compilateur de code HLSL, consultez l’outil Effect-Compiler. Pour plus d’informations sur la compilation de code de nuanceur, consultez Compilation de nuanceurs.

Voici le code dans SimpleVertexShader.hlsl :

struct VertexShaderInput
{
    DirectX::XMFLOAT2 pos : POSITION;
};

struct PixelShaderInput
{
    float4 pos : SV_POSITION;
};

PixelShaderInput SimpleVertexShader(VertexShaderInput input)
{
    PixelShaderInput vertexShaderOutput;

    // For this lesson, set the vertex depth value to 0.5, so it is guaranteed to be drawn.
    vertexShaderOutput.pos = float4(input.pos, 0.5f, 1.0f);

    return vertexShaderOutput;
}

Voici le code dans SimplePixelShader.hlsl :

struct PixelShaderInput
{
    float4 pos : SV_POSITION;
};

float4 SimplePixelShader(PixelShaderInput input) : SV_TARGET
{
    // Draw the entire triangle yellow.
    return float4(1.0f, 1.0f, 0.0f, 1.0f);
}

2. Lecture des données à partir du disque

Nous utilisons la fonction DX ::ReadDataAsync de DirectXHelper.h dans le modèle d’application DirectX 11 (Windows universel) pour lire de manière asynchrone des données à partir d’un fichier sur le disque.

3. Création de nuanceurs de vertex et de pixels

Nous lisons les données du fichier SimpleVertexShader.cso et affectons les données au tableau d’octets vertexShaderBytecode . Nous appelons ID3D11Device ::CreateVertexShader avec le tableau d’octets pour créer le nuanceur de vertex (ID3D11VertexShader). Nous définissons la valeur de profondeur de vertex sur 0,5 dans la source SimpleVertexShader.hlsl pour garantir que notre triangle est dessiné. Nous remplit un tableau de structures D3D11_INPUT_ELEMENT_DESC pour décrire la disposition du code du nuanceur de vertex, puis appeler ID3D11Device ::CreateInputLayout pour créer la disposition. Le tableau a un élément de disposition qui définit la position du vertex. Nous lisons les données du fichier SimplePixelShader.cso et affectons les données au tableau d’octets pixelShaderBytecode . Nous appelons ID3D11Device ::CreatePixelShader avec le tableau d’octets pour créer le nuanceur de pixels (ID3D11PixelShader). Nous définissons la valeur du pixel sur (1,1,1,1) dans la source SimplePixelShader.hlsl pour rendre notre triangle jaune. Vous pouvez modifier la couleur en modifiant cette valeur.

Nous créons des tampons de vertex et d’index qui définissent un triangle simple. Pour ce faire, nous définissons d’abord le triangle, décrivez ensuite les mémoires tampons de vertex et d’index (D3D11_BUFFER_DESC et D3D11_SUBRESOURCE_DATA) à l’aide de la définition du triangle, puis appelez l’ID3D11Device ::CreateBuffer une fois pour chaque mémoire tampon.

        auto loadVSTask = DX::ReadDataAsync(L"SimpleVertexShader.cso");
        auto loadPSTask = DX::ReadDataAsync(L"SimplePixelShader.cso");
        
        // Load the raw vertex shader bytecode from disk and create a vertex shader with it.
        auto createVSTask = loadVSTask.then([this](const std::vector<byte>& vertexShaderBytecode) {


          ComPtr<ID3D11VertexShader> vertexShader;
          DX::ThrowIfFailed(
              m_d3dDevice->CreateVertexShader(
                  vertexShaderBytecode->Data,
                  vertexShaderBytecode->Length,
                  nullptr,
                  &vertexShader
                  )
              );

          // Create an input layout that matches the layout defined in the vertex shader code.
          // For this lesson, this is simply a DirectX::XMFLOAT2 vector defining the vertex position.
          const D3D11_INPUT_ELEMENT_DESC basicVertexLayoutDesc[] =
          {
              { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
          };

          ComPtr<ID3D11InputLayout> inputLayout;
          DX::ThrowIfFailed(
              m_d3dDevice->CreateInputLayout(
                  basicVertexLayoutDesc,
                  ARRAYSIZE(basicVertexLayoutDesc),
                  vertexShaderBytecode->Data,
                  vertexShaderBytecode->Length,
                  &inputLayout
                  )
              );
        });
        
        // Load the raw pixel shader bytecode from disk and create a pixel shader with it.
        auto createPSTask = loadPSTask.then([this](const std::vector<byte>& pixelShaderBytecode) {
          ComPtr<ID3D11PixelShader> pixelShader;
          DX::ThrowIfFailed(
              m_d3dDevice->CreatePixelShader(
                  pixelShaderBytecode->Data,
                  pixelShaderBytecode->Length,
                  nullptr,
                  &pixelShader
                  )
              );
        });

        // Create vertex and index buffers that define a simple triangle.
        auto createTriangleTask = (createPSTask && createVSTask).then([this] () {

          DirectX::XMFLOAT2 triangleVertices[] =
          {
              float2(-0.5f, -0.5f),
              float2( 0.0f,  0.5f),
              float2( 0.5f, -0.5f),
          };

          unsigned short triangleIndices[] =
          {
              0, 1, 2,
          };

          D3D11_BUFFER_DESC vertexBufferDesc = {0};
          vertexBufferDesc.ByteWidth = sizeof(float2) * ARRAYSIZE(triangleVertices);
          vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
          vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
          vertexBufferDesc.CPUAccessFlags = 0;
          vertexBufferDesc.MiscFlags = 0;
          vertexBufferDesc.StructureByteStride = 0;

          D3D11_SUBRESOURCE_DATA vertexBufferData;
          vertexBufferData.pSysMem = triangleVertices;
          vertexBufferData.SysMemPitch = 0;
          vertexBufferData.SysMemSlicePitch = 0;

          ComPtr<ID3D11Buffer> vertexBuffer;
          DX::ThrowIfFailed(
              m_d3dDevice->CreateBuffer(
                  &vertexBufferDesc,
                  &vertexBufferData,
                  &vertexBuffer
                  )
              );

          D3D11_BUFFER_DESC indexBufferDesc;
          indexBufferDesc.ByteWidth = sizeof(unsigned short) * ARRAYSIZE(triangleIndices);
          indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
          indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
          indexBufferDesc.CPUAccessFlags = 0;
          indexBufferDesc.MiscFlags = 0;
          indexBufferDesc.StructureByteStride = 0;

          D3D11_SUBRESOURCE_DATA indexBufferData;
          indexBufferData.pSysMem = triangleIndices;
          indexBufferData.SysMemPitch = 0;
          indexBufferData.SysMemSlicePitch = 0;

          ComPtr<ID3D11Buffer> indexBuffer;
          DX::ThrowIfFailed(
              m_d3dDevice->CreateBuffer(
                  &indexBufferDesc,
                  &indexBufferData,
                  &indexBuffer
                  )
              );
        });

Nous utilisons les nuanceurs de vertex et de pixels, la disposition du nuanceur de vertex et les tampons de vertex et d’index pour dessiner un triangle jaune.

4. Dessin du triangle et présentation de l’image rendue

Nous entrerons une boucle infinie pour afficher et afficher continuellement la scène. Nous appelons ID3D11DeviceContext ::OMSetRenderTargets pour spécifier la cible de rendu comme cible de sortie. Nous appelons ID3D11DeviceContext ::ClearRenderTargetView avec { 0.071f, 0.04f, 0.561f, 1.0f } pour effacer la cible de rendu dans une couleur bleue unie.

Dans la boucle infinie, nous dessinons un triangle jaune sur la surface bleue.

Pour dessiner un triangle jaune

  1. Tout d’abord, nous appelons ID3D11DeviceContext ::IASetInputLayout pour décrire comment les données de mémoire tampon de vertex sont diffusées dans l’étape d’assembleur d’entrée.
  2. Ensuite, nous appelons ID3D11DeviceContext ::IASetVertexBuffers et ID3D11DeviceContext ::IASetIndexBuffer pour lier les tampons de vertex et d’index à l’étape d’assembleur d’entrée.
  3. Ensuite, nous appelons ID3D11DeviceContext ::IASetPrimitiveTopology avec la valeur D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP à spécifier pour l’étape d’assembleur d’entrée pour interpréter les données de vertex sous la forme d’une bande de triangles.
  4. Ensuite, nous appelons ID3D11DeviceContext ::VSSetShader pour initialiser l’étape du nuanceur de vertex avec le code du nuanceur de vertex et ID3D11DeviceContext ::P SSetShader pour initialiser l’étape du nuanceur de pixels avec le code du nuanceur de pixels.
  5. Enfin, nous appelons ID3D11DeviceContext ::D rawIndexed pour dessiner le triangle et le soumettre au pipeline de rendu.

Nous appelons IDXGISwapChain ::P resent pour présenter l’image rendue à la fenêtre.

            // Specify the render target we created as the output target.
            m_d3dDeviceContext->OMSetRenderTargets(
                1,
                m_renderTargetView.GetAddressOf(),
                nullptr // Use no depth stencil.
                );

            // Clear the render target to a solid color.
            const float clearColor[4] = { 0.071f, 0.04f, 0.561f, 1.0f };
            m_d3dDeviceContext->ClearRenderTargetView(
                m_renderTargetView.Get(),
                clearColor
                );

            m_d3dDeviceContext->IASetInputLayout(inputLayout.Get());

            // Set the vertex and index buffers, and specify the way they define geometry.
            UINT stride = sizeof(float2);
            UINT offset = 0;
            m_d3dDeviceContext->IASetVertexBuffers(
                0,
                1,
                vertexBuffer.GetAddressOf(),
                &stride,
                &offset
                );

            m_d3dDeviceContext->IASetIndexBuffer(
                indexBuffer.Get(),
                DXGI_FORMAT_R16_UINT,
                0
                );

            m_d3dDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

            // Set the vertex and pixel shader stage state.
            m_d3dDeviceContext->VSSetShader(
                vertexShader.Get(),
                nullptr,
                0
                );

            m_d3dDeviceContext->PSSetShader(
                pixelShader.Get(),
                nullptr,
                0
                );

            // Draw the cube.
            m_d3dDeviceContext->DrawIndexed(
                ARRAYSIZE(triangleIndices),
                0,
                0
                );

            // Present the rendered image to the window.  Because the maximum frame latency is set to 1,
            // the render loop will generally be throttled to the screen refresh rate, typically around
            // 60 Hz, by sleeping the application on Present until the screen is refreshed.
            DX::ThrowIfFailed(
                m_swapChain->Present(1, 0)
                );

Résumé et étapes suivantes

Nous avons créé et dessiné un triangle jaune à l’aide de nuanceurs de vertex et de pixels.

Ensuite, nous créons un cube 3D en orbite et appliquons des effets d’éclairage à celui-ci.

Utilisation de profondeur et d’effets sur les primitives