Partager via


Utiliser la profondeur et les effets sur les primitives

Ici, nous vous montrons comment utiliser la profondeur, la perspective, la couleur et d’autres effets sur les primitives.

Objectif : Pour créer un objet 3D et appliquer un éclairage et une coloration de vertex de base à celui-ci.

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 et création de nuanceurs et de primitives de dessin.

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

Instructions

1. Définition de variables de cube

Tout d’abord, nous devons définir les structures SimpleCubeVertex et ConstantBuffer pour le cube. Ces structures spécifient les positions et couleurs de vertex pour le cube et la façon dont le cube sera consulté. Nous déclarons ID3D11DepthStencilView et ID3D11Buffer avec ComPtr et déclarons une instance de ConstantBuffer.

struct SimpleCubeVertex
{
    DirectX::XMFLOAT3 pos;   // Position
    DirectX::XMFLOAT3 color; // Color
};

struct ConstantBuffer
{
    DirectX::XMFLOAT4X4 model;
    DirectX::XMFLOAT4X4 view;
    DirectX::XMFLOAT4X4 projection;
};

// This class defines the application as a whole.
ref class Direct3DTutorialFrameworkView : public IFrameworkView
{
private:
    Platform::Agile<CoreWindow> m_window;
    ComPtr<IDXGISwapChain1> m_swapChain;
    ComPtr<ID3D11Device1> m_d3dDevice;
    ComPtr<ID3D11DeviceContext1> m_d3dDeviceContext;
    ComPtr<ID3D11RenderTargetView> m_renderTargetView;
    ComPtr<ID3D11DepthStencilView> m_depthStencilView;
    ComPtr<ID3D11Buffer> m_constantBuffer;
    ConstantBuffer m_constantBufferData;

2. Création d’une vue de gabarit de profondeur

En plus de créer la vue de la cible de rendu, nous créons également une vue de gabarit de profondeur. La vue profondeur-gabarit permet à Direct3D de rendre efficacement les objets plus proches de la caméra devant les objets plus loin de la caméra. Avant de pouvoir créer une vue sur une mémoire tampon de gabarit de profondeur, nous devons créer la mémoire tampon de gabarit de profondeur. Nous remplit un D3D11_TEXTURE2D_DESC pour décrire la mémoire tampon de gabarit de profondeur, puis appeler ID3D11Device ::CreateTexture2D pour créer la mémoire tampon de profondeur-gabarit. Pour créer la vue profondeur-gabarit, nous remplit un D3D11_DEPTH_STENCIL_VIEW_DESC pour décrire la vue de profondeur et passer la description de la vue de gabarit de profondeur et la mémoire tampon de profondeur-gabarit à ID3D11Device ::CreateDepthStencilView.

        // Once the render target view is created, create a depth stencil view.  This
        // allows Direct3D to efficiently render objects closer to the camera in front
        // of objects further from the camera.

        D3D11_TEXTURE2D_DESC backBufferDesc = {0};
        backBuffer->GetDesc(&backBufferDesc);

        D3D11_TEXTURE2D_DESC depthStencilDesc;
        depthStencilDesc.Width = backBufferDesc.Width;
        depthStencilDesc.Height = backBufferDesc.Height;
        depthStencilDesc.MipLevels = 1;
        depthStencilDesc.ArraySize = 1;
        depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
        depthStencilDesc.SampleDesc.Count = 1;
        depthStencilDesc.SampleDesc.Quality = 0;
        depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
        depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
        depthStencilDesc.CPUAccessFlags = 0;
        depthStencilDesc.MiscFlags = 0;
        ComPtr<ID3D11Texture2D> depthStencil;
        DX::ThrowIfFailed(
            m_d3dDevice->CreateTexture2D(
                &depthStencilDesc,
                nullptr,
                &depthStencil
                )
            );

        D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
        depthStencilViewDesc.Format = depthStencilDesc.Format;
        depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
        depthStencilViewDesc.Flags = 0;
        depthStencilViewDesc.Texture2D.MipSlice = 0;
        DX::ThrowIfFailed(
            m_d3dDevice->CreateDepthStencilView(
                depthStencil.Get(),
                &depthStencilViewDesc,
                &m_depthStencilView
                )
            );

3. Mise à jour de la perspective avec la fenêtre

Nous mettons à jour les paramètres de projection de perspective pour la mémoire tampon constante en fonction des dimensions de la fenêtre. Nous corrigeons les paramètres sur un champ de vue de 70 degrés avec une plage de profondeur de 0,01 à 100.

        // Finally, update the constant buffer perspective projection parameters
        // to account for the size of the application window.  In this sample,
        // the parameters are fixed to a 70-degree field of view, with a depth
        // range of 0.01 to 100.  For a generalized camera class, see Lesson 5.

        float xScale = 1.42814801f;
        float yScale = 1.42814801f;
        if (backBufferDesc.Width > backBufferDesc.Height)
        {
            xScale = yScale *
                static_cast<float>(backBufferDesc.Height) /
                static_cast<float>(backBufferDesc.Width);
        }
        else
        {
            yScale = xScale *
                static_cast<float>(backBufferDesc.Width) /
                static_cast<float>(backBufferDesc.Height);
        }

        m_constantBufferData.projection = DirectX::XMFLOAT4X4(
            xScale, 0.0f,    0.0f,  0.0f,
            0.0f,   yScale,  0.0f,  0.0f,
            0.0f,   0.0f,   -1.0f, -0.01f,
            0.0f,   0.0f,   -1.0f,  0.0f
            );

4. Création de nuanceurs de vertex et de pixels avec des éléments de couleur

Dans cette application, nous créons des nuanceurs de vertex et de pixels plus complexes que ce que nous avons décrit dans le didacticiel précédent, création de nuanceurs et de primitives de dessin. Le nuanceur de vertex de l’application transforme chaque position de vertex en espace de projection et transmet la couleur de vertex au nuanceur de pixels.

Le tableau d’D3D11_INPUT_ELEMENT_DESC de l’application qui décrivent la disposition du code du nuanceur de vertex comporte deux éléments de disposition : un élément définit la position du sommet et l’autre élément définit la couleur.

Nous créons des tampons de vertex, d’index et de constantes pour définir un cube en orbite.

Pour définir un cube en orbite

  1. Tout d’abord, nous définissons le cube. Nous affectons chaque vertex à une couleur en plus d’une position. Cela permet au nuanceur de pixels de colorer différemment chaque visage afin que le visage puisse être distingué.
  2. Ensuite, nous décrivons les mémoires tampons de vertex et d’index (D3D11_BUFFER_DESC et D3D11_SUBRESOURCE_DATA) à l’aide de la définition du cube. Nous appelons ID3D11Device ::CreateBuffer une fois pour chaque mémoire tampon.
  3. Ensuite, nous créons une mémoire tampon constante (D3D11_BUFFER_DESC) pour passer des matrices de modèle, d’affichage et de projection au nuanceur de vertex. Nous pouvons ensuite utiliser la mémoire tampon constante pour faire pivoter le cube et appliquer une projection de perspective à celle-ci. Nous appelons ID3D11Device ::CreateBuffer pour créer la mémoire tampon constante.
  4. Ensuite, nous spécifions la transformation de vue qui correspond à une position de caméra de X = 0, Y = 1, Z = 2.
  5. Enfin, nous déclarons une variable de degré que nous allons utiliser pour animer le cube en le faisant pivoter chaque image.
        
        auto loadVSTask = DX::ReadDataAsync(L"SimpleVertexShader.cso");
        auto loadPSTask = DX::ReadDataAsync(L"SimplePixelShader.cso");
        
        
        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::XMFLOAT3 vector defining the vertex position, and
          // a DirectX::XMFLOAT3 vector defining the vertex color.
          const D3D11_INPUT_ELEMENT_DESC basicVertexLayoutDesc[] =
          {
              { "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 },
          };

          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 unit cube.
        auto createCubeTask = (createPSTask && createVSTask).then([this] () {

          // In the array below, which will be used to initialize the cube vertex buffers,
          // each vertex is assigned a color in addition to a position.  This will allow
          // the pixel shader to color each face differently, enabling them to be distinguished.
          SimpleCubeVertex cubeVertices[] =
          {
              { float3(-0.5f, 0.5f, -0.5f), float3(0.0f, 1.0f, 0.0f) }, // +Y (top face)
              { float3( 0.5f, 0.5f, -0.5f), float3(1.0f, 1.0f, 0.0f) },
              { float3( 0.5f, 0.5f,  0.5f), float3(1.0f, 1.0f, 1.0f) },
              { float3(-0.5f, 0.5f,  0.5f), float3(0.0f, 1.0f, 1.0f) },

              { float3(-0.5f, -0.5f,  0.5f), float3(0.0f, 0.0f, 1.0f) }, // -Y (bottom face)
              { float3( 0.5f, -0.5f,  0.5f), float3(1.0f, 0.0f, 1.0f) },
              { float3( 0.5f, -0.5f, -0.5f), float3(1.0f, 0.0f, 0.0f) },
              { float3(-0.5f, -0.5f, -0.5f), float3(0.0f, 0.0f, 0.0f) },
          };

          unsigned short cubeIndices[] =
          {
              0, 1, 2,
              0, 2, 3,

              4, 5, 6,
              4, 6, 7,

              3, 2, 5,
              3, 5, 4,

              2, 1, 6,
              2, 6, 5,

              1, 7, 6,
              1, 0, 7,

              0, 3, 4,
              0, 4, 7
          };

          D3D11_BUFFER_DESC vertexBufferDesc = {0};
          vertexBufferDesc.ByteWidth = sizeof(SimpleCubeVertex) * ARRAYSIZE(cubeVertices);
          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 = cubeVertices;
          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(cubeIndices);
          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 = cubeIndices;
          indexBufferData.SysMemPitch = 0;
          indexBufferData.SysMemSlicePitch = 0;

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


          // Create a constant buffer for passing model, view, and projection matrices
          // to the vertex shader.  This will allow us to rotate the cube and apply
          // a perspective projection to it.

          D3D11_BUFFER_DESC constantBufferDesc = {0};
          constantBufferDesc.ByteWidth = sizeof(m_constantBufferData);
          constantBufferDesc.Usage = D3D11_USAGE_DEFAULT;
          constantBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
          constantBufferDesc.CPUAccessFlags = 0;
          constantBufferDesc.MiscFlags = 0;
          constantBufferDesc.StructureByteStride = 0;
          DX::ThrowIfFailed(
              m_d3dDevice->CreateBuffer(
                  &constantBufferDesc,
                  nullptr,
                  &m_constantBuffer
                  )
              );

          // Specify the view transform corresponding to a camera position of
          // X = 0, Y = 1, Z = 2.  For a generalized camera class, see Lesson 5.

          m_constantBufferData.view = DirectX::XMFLOAT4X4(
              -1.00000000f, 0.00000000f,  0.00000000f,  0.00000000f,
               0.00000000f, 0.89442718f,  0.44721359f,  0.00000000f,
               0.00000000f, 0.44721359f, -0.89442718f, -2.23606800f,
               0.00000000f, 0.00000000f,  0.00000000f,  1.00000000f
              );

        });
        
        // This value will be used to animate the cube by rotating it every frame.
        float degree = 0.0f;
        

5. Rotation et dessin du cube et présentation de l’image rendue

Nous entrerons une boucle infinie pour afficher et afficher continuellement la scène. Nous appelons la fonction inline rotationY (BasicMath.h) avec une quantité de rotation pour définir des valeurs qui feront pivoter la matrice de modèle du cube autour de l’axe Y. Nous appelons ensuite ID3D11DeviceContext ::UpdateSubresource pour mettre à jour la mémoire tampon constante et faire pivoter le modèle de cube. Nous appelons ID3D11DeviceContext ::OMSetRenderTargets pour spécifier la cible de rendu comme cible de sortie. Dans cet appel OMSetRenderTargets, nous passons la vue profondeur-gabarit. Nous appelons ID3D11DeviceContext ::ClearRenderTargetView pour effacer la cible de rendu en couleur bleue unie et appeler ID3D11DeviceContext ::ClearDepthStencilView pour effacer la mémoire tampon de profondeur.

Dans la boucle infinie, nous dessinons également le cube sur la surface bleue.

Pour dessiner le cube

  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. Ensuite, nous appelons ID3D11DeviceContext ::VSSetConstantBuffers pour définir la mémoire tampon constante utilisée par l’étape de pipeline du nuanceur de vertex.
  6. Enfin, nous appelons ID3D11DeviceContext ::D rawIndexed pour dessiner le cube et le soumettre au pipeline de rendu.

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

            // Update the constant buffer to rotate the cube model.
            m_constantBufferData.model = XMMatrixRotationY(-degree);
            degree += 1.0f;

            m_d3dDeviceContext->UpdateSubresource(
                m_constantBuffer.Get(),
                0,
                nullptr,
                &m_constantBufferData,
                0,
                0
                );

            // Specify the render target and depth stencil we created as the output target.
            m_d3dDeviceContext->OMSetRenderTargets(
                1,
                m_renderTargetView.GetAddressOf(),
                m_depthStencilView.Get()
                );

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

            m_d3dDeviceContext->ClearDepthStencilView(
                m_depthStencilView.Get(),
                D3D11_CLEAR_DEPTH,
                1.0f,
                0
                );

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

            // Set the vertex and index buffers, and specify the way they define geometry.
            UINT stride = sizeof(SimpleCubeVertex);
            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->VSSetConstantBuffers(
                0,
                1,
                m_constantBuffer.GetAddressOf()
                );

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

            // Draw the cube.
            m_d3dDeviceContext->DrawIndexed(
                ARRAYSIZE(cubeIndices),
                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 utilisé la profondeur, la perspective, la couleur et d’autres effets sur les primitives.

Ensuite, nous appliquons des textures aux primitives.

Application de textures à des primitives