Renderizzare la mappa delle ombre nel buffer di profondità

Renderizzare dal punto di vista della luce per creare una mappa di profondità bidimensionale che rappresenti il volume dell'ombra. La mappa di profondità maschera lo spazio che verrà renderizzato in ombra. Parte 2 della procedura dettagliata: Implementare i volumi d'ombra utilizzando buffer di profondità in Direct3D 11.

Cancellare il buffer di profondità

Cancellare sempre il buffer di profondità prima di eseguirne il rendering.

context->ClearRenderTargetView(m_deviceResources->GetBackBufferRenderTargetView(), DirectX::Colors::CornflowerBlue);
context->ClearDepthStencilView(m_shadowDepthView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);

Renderizzare la mappa delle ombre nel buffer di profondità

Per il passaggio di rendering dell'ombra, specificare un buffer di profondità ma senza specificare alcun target di rendering.

Specificare la viewport della luce, uno shader di vertici e impostare i buffer costanti dello spazio della luce. Utilizzare l'eliminazione delle facce anteriori per questo passaggio per ottimizzare i valori di profondità ubicati nel buffer dell'ombra.

Notare che sulla maggior parte dei dispositivi è possibile specificare nullptr per lo shader di pixel (o ignorare completamente la specifica di uno shader di pixel). Ma alcuni driver potrebbero generare un'eccezione quando si chiama draw sul dispositivo Direct3D con un shader di pixel nullo impostato. Per evitare questa eccezione, è possibile impostare uno shader di pixel minimo per il passaggio di rendering dell'ombra. L'output di questo shader viene buttato via; è possibile chiamare discard su ogni pixel.

Eseguire il rendering degli oggetti che possono proiettare ombre, ma non preoccuparsi di eseguire il rendering della geometria che non può proiettare un'ombra (come il pavimento di una stanza o gli oggetti rimossi dal passaggio dell'ombra per motivi di ottimizzazione).

void ShadowSceneRenderer::RenderShadowMap()
{
    auto context = m_deviceResources->GetD3DDeviceContext();

    // Render all the objects in the scene that can cast shadows onto themselves or onto other objects.

    // Only bind the ID3D11DepthStencilView for output.
    context->OMSetRenderTargets(
        0,
        nullptr,
        m_shadowDepthView.Get()
        );

    // Note that starting with the second frame, the previous call will display
    // warnings in VS debug output about forcing an unbind of the pixel shader
    // resource. This warning can be safely ignored when using shadow buffers
    // as demonstrated in this sample.

    // Set rendering state.
    context->RSSetState(m_shadowRenderState.Get());
    context->RSSetViewports(1, &m_shadowViewport);

    // Each vertex is one instance of the VertexPositionTexNormColor struct.
    UINT stride = sizeof(VertexPositionTexNormColor);
    UINT offset = 0;
    context->IASetVertexBuffers(
        0,
        1,
        m_vertexBuffer.GetAddressOf(),
        &stride,
        &offset
        );

    context->IASetIndexBuffer(
        m_indexBuffer.Get(),
        DXGI_FORMAT_R16_UINT, // Each index is one 16-bit unsigned integer (short).
        0
        );

    context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    context->IASetInputLayout(m_inputLayout.Get());

    // Attach our vertex shader.
    context->VSSetShader(
        m_simpleVertexShader.Get(),
        nullptr,
        0
        );

    // Send the constant buffers to the Graphics device.
    context->VSSetConstantBuffers(
        0,
        1,
        m_lightViewProjectionBuffer.GetAddressOf()
        );

    context->VSSetConstantBuffers(
        1,
        1,
        m_rotatedModelBuffer.GetAddressOf()
        );

    // In some configurations, it's possible to avoid setting a pixel shader
    // (or set PS to nullptr). Not all drivers are tolerant of this, so to be
    // safe set a minimal shader here.
    //
    // Direct3D will discard output from this shader because the render target
    // view is unbound.
    context->PSSetShader(
        m_textureShader.Get(),
        nullptr,
        0
        );

    // Draw the objects.
    context->DrawIndexed(
        m_indexCountCube,
        0,
        0
        );
}

Ottimizzare il frustum di visione: Accertarsi che l'implementazione calcoli un frustum di visione ristretto in modo da ottenere la massima precisione dal buffer di profondità. Vedere Tecniche comuni per migliorare le mappe di profondità dell'ombra per ulteriori suggerimenti sulla tecnica dell'ombra.

Shader di vertici per il passaggio dell'ombra

Utilizzare una versione semplificata di shader di vertici per eseguire il rendering solo della posizione del vertice nello spazio luminoso. Non includere normali illuminazioni, trasformazioni secondarie e così via.

PixelShaderInput main(VertexShaderInput input)
{
    PixelShaderInput output;
    float4 pos = float4(input.pos, 1.0f);

    // Transform the vertex position into projected space.
    pos = mul(pos, model);
    pos = mul(pos, view);
    pos = mul(pos, projection);
    output.pos = pos;

    return output;
}

Nella parte successiva di questa procedura dettagliata, scopriremo come aggiungere ombre eseguendo il rendering con test di profondità.