Crear sombreadores y dibujar primitivosCreate shaders and drawing primitives

Aquí te mostramos cómo usar archivos de origen HLSL para compilar y crear sombreadores que luego podrás usar para dibujar primitivos en la pantalla.Here, we show you how to use HLSL source files to compile and create shaders that you can then use to draw primitives on the display.

Creamos y dibujamos un triángulo amarillo con sombreadores de vértices y píxeles.We create and draw a yellow triangle by using vertex and pixel shaders. Después de crear el dispositivo Direct3D, la cadena de intercambio y la vista del destino de representación, leemos datos de los archivos de objetos binarios del sombreador.After we create the Direct3D device, the swap chain, and the render-target view, we read data from binary shader object files on the disk.

Objetivo: crear sombreadores y dibujar primitivos.Objective: To create shaders and to draw primitives.

Requisitos previosPrerequisites

Suponemos que estás familiarizado con C++.We assume that you are familiar with C++. También necesitas tener experiencia básica en los conceptos de programación de gráficos.You also need basic experience with graphics programming concepts.

Suponemos además que has consultado Inicio rápido: configurar recursos de DirectX y mostrar una imagen.We also assume that you went through Quickstart: setting up DirectX resources and displaying an image.

Tiempo para completarlo: 20minutos.Time to complete: 20 minutes.

InstruccionesInstructions

1. Compilar archivos de origen HLSL1. Compiling HLSL source files

Microsoft Visual Studio usa el compilador de código HLSL fxc.exe para compilar los archivos de origen .hlsl (SimpleVertexShader.hlsl y SimplePixelShader.hlsl) en archivos de objetos de sombreador binarios .cso (SimpleVertexShader.cso y SimplePixelShader.cso).Microsoft Visual Studio uses the fxc.exe HLSL code compiler to compile the .hlsl source files (SimpleVertexShader.hlsl and SimplePixelShader.hlsl) into .cso binary shader object files (SimpleVertexShader.cso and SimplePixelShader.cso). Para obtener más información acerca del compilador de código HLSL, consulta el tema sobre el compilador de efectos.For more info about the HLSL code compiler, see Effect-Compiler Tool. Para obtener más información acerca de cómo compilar código de sombreadores, consulta el tema sobre la compilación de sombreadores.For more info about compiling shader code, see Compiling Shaders.

Este es el código en SimpleVertexShader.hlsl:Here is the code 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;
}

Este es el código en SimplePixelShader.hlsl:Here is the code 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. Leer datos del disco2. Reading data from disk

Usamos la función DX::ReadDataAsync de DirectXHelper.h en la plantilla Aplicación DirectX 11 (Universal Windows) para leer datos desde un archivo en el disco de forma asincrónica.We use the DX::ReadDataAsync function from DirectXHelper.h in the DirectX 11 App (Universal Windows) template to asynchronously read data from a file on the disk.

3. Crear sombreadores de vértices y píxeles3. Creating vertex and pixel shaders

Leemos los datos del archivo SimpleVertexShader.cso y asignamos esos datos a la matriz de bytes vertexShaderBytecode.We read data from the SimpleVertexShader.cso file and assign the data to the vertexShaderBytecode byte array. Llamamos a ID3D11Device::CreateVertexShader con la matriz de bytes para crear el sombreador de vértices (ID3D11VertexShader).We call ID3D11Device::CreateVertexShader with the byte array to create the vertex shader (ID3D11VertexShader). Establecemos el valor de profundidad del vértice en 0,5 en el origen SimpleVertexShader.hlsl para garantizar que el triángulo se dibuja.We set the vertex depth value to 0.5 in the SimpleVertexShader.hlsl source to guarantee that our triangle is drawn. Rellenamos la matriz de estructuras D3D11_INPUT_ELEMENT_DESC para describir el diseño del código del sombreador de vértices y luego llamamos a ID3D11Device::CreateInputLayout para crear el diseño.We populate an array of D3D11_INPUT_ELEMENT_DESC structures to describe the layout of the vertex shader code and then call ID3D11Device::CreateInputLayout to create the layout. La matriz tiene un elemento de diseño que define la posición del vértice.The array has one layout element that defines the vertex position. Leemos los datos del archivo SimplePixelShader.cso y asignamos esos datos a la matriz de bytes pixelShaderBytecode.We read data from the SimplePixelShader.cso file and assign the data to the pixelShaderBytecode byte array. Llamamos a ID3D11Device::CreatePixelShader con la matriz de bytes para crear el sombreador de píxeles (ID3D11PixelShader).We call ID3D11Device::CreatePixelShader with the byte array to create the pixel shader (ID3D11PixelShader). Establecemos el valor del píxel en (1,1,1,1) en el origen SimplePixelShader.hlsl para que el triángulo sea amarillo.We set the pixel value to (1,1,1,1) in the SimplePixelShader.hlsl source to make our triangle yellow. Puedes cambiar el color modificando este valor.You can change the color by changing this value.

Creamos un búfer de índices y un búffer de vértices que definen un triángulo simple.We create vertex and index buffers that define a simple triangle. Para ello, primero definimos el triángulo, luego describimos cada uno de estos búferes (D3D11_BUFFER_DESC y D3D11_SUBRESOURCE_DATA) mediante la definición del triángulo y, por último, llamamos a ID3D11Device::CreateBuffer una vez para cada búfer.To do this, we first define the triangle, next describe the vertex and index buffers (D3D11_BUFFER_DESC and D3D11_SUBRESOURCE_DATA) using the triangle definition, and finally call ID3D11Device::CreateBuffer once for each 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
                  )
              );
        });

Hemos usado los sombreadores de vértices y píxeles, el diseño del sombreador de vértices, el búfer de índices y el búfer de vértices para dibujar un triángulo amarillo.We use the vertex and pixel shaders, the vertex shader layout, and the vertex and index buffers to draw a yellow triangle.

4. Dibujar el triángulo y mostrar la imagen representada4. Drawing the triangle and presenting the rendered image

Entramos en un bucle sin fin para representar y mostrar continuamente la escena.We enter an endless loop to continually render and display the scene. Llamamos a ID3D11DeviceContext::OMSetRenderTargets para especificar el destino de representación como el destino de salida.We call ID3D11DeviceContext::OMSetRenderTargets to specify the render target as the output target. Llamamos a ID3D11DeviceContext::ClearRenderTargetView con {0.071f, 0.04f, 0.561f, 1.0f} para borrar el destino de representación y que muestre un color azul sólido.We call ID3D11DeviceContext::ClearRenderTargetView with { 0.071f, 0.04f, 0.561f, 1.0f } to clear the render target to a solid blue color.

En el bucle sin fin, dibujamos un triángulo amarillo en la superficie azul.In the endless loop, we draw a yellow triangle on the blue surface.

Para dibujar un triángulo amarilloTo draw a yellow triangle

  1. En primer lugar, llamamos a ID3D11DeviceContext::IASetInputLayout para describir cómo se emitirán los datos del búfer de vértices a la etapa del ensamblador.First, we call ID3D11DeviceContext::IASetInputLayout to describe how vertex buffer data is streamed into the input-assembler stage.
  2. A continuación, llamamos a ID3D11DeviceContext::IASetVertexBuffers y ID3D11DeviceContext::IASetIndexBuffer para enlazar los búferes de vértices e índices a la etapa del ensamblador de entrada.Next, we call ID3D11DeviceContext::IASetVertexBuffers and ID3D11DeviceContext::IASetIndexBuffer to bind the vertex and index buffers to the input-assembler stage.
  3. Después llamamos a ID3D11DeviceContext::IASetPrimitiveTopology con el valor D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP para especificar que en la etapa del ensamblador de entrada se interpreten los datos de vértice como una franja de triángulos.Next, we call ID3D11DeviceContext::IASetPrimitiveTopology with the D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP value to specify for the input-assembler stage to interpret the vertex data as a triangle strip.
  4. A continuación, llamamos a ID3D11DeviceContext::VSSetShader para inicializar la etapa del sombreador de vértices con el código del sombreador de vértices e ID3D11DeviceContext::PSSetShader para inicializar la etapa del sombreador de píxeles con el código del sombreador de píxeles.Next, we call ID3D11DeviceContext::VSSetShader to initialize the vertex shader stage with the vertex shader code and ID3D11DeviceContext::PSSetShader to initialize the pixel shader stage with the pixel shader code.
  5. Por último, llamamos a ID3D11DeviceContext::DrawIndexed para dibujar el triángulo y enviarlo a la canalización de representación.Finally, we call ID3D11DeviceContext::DrawIndexed to draw the triangle and submit it to the rendering pipeline.

Llamamos a IDXGISwapChain::Present para mostrar la imagen representada en la ventana.We call IDXGISwapChain::Present to present the rendered image to the window.

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

Resumen y pasos siguientesSummary and next steps

Hemos creado y dibujado un triángulo amarillo con sombreadores de vértices y píxeles.We created and drew a yellow triangle by using vertex and pixel shaders.

A continuación, crearemos un cubo 3D en órbita y le aplicaremos efectos de iluminación.Next, we create an orbiting 3D cube and apply lighting effects to it.

Usar profundidad y efectos en primitivosUsing depth and effects on primitives