Condividi tramite


Creare shader e primitive di disegno

Qui viene illustrato come usare i file di origine HLSL per compilare e creare shader da usare per disegnare primitive sullo schermo.

Creare e disegnare un triangolo giallo usando vertex e pixel shader. Dopo avere creato il dispositivo Direct3D, la swapchain e la visualizzazione di destinazione di rendering, i dati vengono letti dai file oggetto shader binari sul disco.

Obiettivo: creare shader e disegnare primitive.

Prerequisiti

Si presuppone che l'utente abbia familiarità con C++. È anche necessario avere familiarità con i concetti di programmazione grafica.

Si presuppone anche che sia stata seguita la sezione Configurazione delle risorse DirectX e visualizzazione di un'immagine.

Tempo di completamento: 20 minuti.

Istruzioni

1. Compilazione di file di origine HLSL

Microsoft Visual Studio usa il compilatore di codice HLSL fxc.exe per compilare i file di origine hlsl (SimpleVertexShader.hlsl e SimplePixelShader.hlsl) in file di oggetti shader binari con estensione cso (SimpleVertexShader.cso e SimplePixelShader.cso). Per maggiori informazioni sul compilatore HLSL, vedere Compilatore degli effetti. Per maggiori informazioni sulla compilazione del codice shader, vedere Compilazione di shader.

Questo è il codice in 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;
}

Questo è il codice in 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. Lettura dei dati dal disco

Usare la funzione DX::ReadDataAsync da DirectXHelper.h nel modello App DirectX 11 (Windows universale) per leggere in modo asincrono i dati da un file sul disco.

3. Creazione di vertex shader e pixel shader

Leggere i dati dal file SimpleVertexShader.cso e assegnare i dati alla matrice di byte vertexShaderBytecode. Chiamare ID3D11Device::CreateVertexShader con la matrice di byte per creare il vertex shader (ID3D11VertexShader). Il valore di profondità del vertice viene impostato su 0.5 nell'origine SimpleVertexShader.hlsl per garantire che il triangolo venga disegnato. Popolare una matrice di strutture di D3D11_INPUT_ELEMENT_DESC per descrivere il layout del codice del vertex shader, quindi chiamare ID3D11Device::CreateInputLayout per creare il layout. La matrice ha un elemento di layout che definisce la posizione del vertice. Leggere i dati dal file SimplePixelShader.cso e assegnare i dati alla matrice di byte pixelShaderBytecode. Chiamare ID3D11Device::CreatePixelShader con la matrice di byte per creare il pixel shader (ID3D11PixelShader). Impostare il valore del pixel su (1,1,1,1) nell'origine SimplePixelShader.hlsl per rendere giallo il triangolo. È possibile modificare il colore modificando questo valore.

Creare buffer di vertici e indici che definiscono un triangolo semplice. A tale scopo, definire prima di tutto il triangolo, quindi descrivere i buffer dei vertici e i buffer degli indici (D3D11_BUFFER_DESC e D3D11_SUBRESOURCE_DATA) usando la definizione del triangolo e infine chiamare ID3D11Device::CreateBuffer una volta per ogni buffer.

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

Usare i vertex shader e pixel shader, il layout del vertex shader e i buffer dei vertici e i buffer degli indici per disegnare un triangolo giallo.

4. Disegno del triangolo e presentazione dell'immagine sottoposta a rendering

Si inizia un ciclo infinito per eseguire il rendering e visualizzare la scena in modo continuo. Si chiama ID3D11DeviceContext::OMSetRenderTargets per specificare la destinazione del rendering come destinazione di output. Chiamare ID3D11DeviceContext::ClearRenderTargetView con { 0.071f, 0.04f, 0.561f, 1.0f } per cancellare la destinazione di rendering su un colore blu a tinta unita.

Nel ciclo infinito, disegnare un triangolo giallo sulla superficie blu.

Per disegnare un triangolo giallo

  1. Innanzitutto, si chiama ID3D11DeviceContext::IASetInputLayout per descrivere il modo in cui i dati del buffer dei vertici vengono trasmessi in streaming nella fase assemblaggio input.
  2. Successivamente, si chiama ID3D11DeviceContext::IASetVertexBuffers e ID3D11DeviceContext::IASetIndexBuffer per associare i buffer dei vertici e gli index buffer alla fase assemblaggio input.
  3. Successivamente, si chiama ID3D11DeviceContext::IASetPrimitiveTopology con il valore D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP per specificare che la fase assemblaggio input interpreti i dati dei vertici come striscia di triangoli.
  4. Successivamente, si chiama ID3D11DeviceContext::VSSetShader per inizializzare la fase del vertex shader con il codice del vertex shader e ID3D11DeviceContext::PSSetShader per inizializzare la fase del pixel shader con il codice del pixel shader.
  5. Infine, chiamare ID3D11DeviceContext::DrawIndexed per disegnare il triangolo e inviarlo alla pipeline del rendering.

Si chiama IDXGISwapChain::Present per presentare l'immagine sottoposta a rendering alla finestra.

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

Riepilogo e passaggi successivi

È stato creato e disegnato un triangolo giallo usando vertex shader e pixel shader.

Ora vedremo come creare un cubo 3D orbitante e come applicare effetti di illuminazione.

Uso di profondità ed effetti sulle primitive