ライトの観点からレンダリングして、シャドウ ボリュームを表す 2 次元深度マップを作成します。 深度マップは、影でレンダリングされる空間をマスクします。 チュートリアルのパート 2 : Direct3D 11 で深度バッファーを使用してシャドウ ボリュームを実装する。
深度バッファーをクリアする
レンダリングを行う前に、必ず深度バッファーをクリアしてください。
context->ClearRenderTargetView(m_deviceResources->GetBackBufferRenderTargetView(), DirectX::Colors::CornflowerBlue);
context->ClearDepthStencilView(m_shadowDepthView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
深度バッファーにシャドウ マップをレンダリングする
シャドウ レンダリング パスの場合は、深度バッファーを指定しますが、レンダー ターゲットは指定しません。
ライト ビューポート、頂点シェーダーを指定し、ライト空間定数バッファーを設定します。 このパスの前面カリングを使用して、シャドウ バッファーに配置された深度値を最適化します。
ほとんどのデバイスでは、ピクセル シェーダーに nullptr を指定できます (または、ピクセル シェーダーの指定を完全にスキップすることもできます)。 ただし、Direct3D デバイスで描画を呼び出す際に、ピクセル シェーダーが null セットの場合、一部のドライバーは例外をスローすることがあります。 この例外を回避するには、シャドウ レンダリング パスの最小ピクセル シェーダーを設定します。 このシェーダーの出力は破棄されます。すべてのピクセルで discard を呼び出すことができます。
シャドウをキャストできるオブジェクトをレンダリングしますが、影を投影できないジオメトリのレンダリングは気にしません (部屋の床など、最適化の理由からシャドウ パスから削除されたオブジェクト)。
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
);
}
ビューの錐台を最適化する: 深度バッファーから最大限の精度を得るために、実装においてタイトなビューの錐台が計算されていることを確認します。 シャドウ技術に関するより多くのヒントについては、「シャドウ深度マップを改善するための一般的な手法」と「」を参照してください。
シャドウ パスの頂点シェーダー
頂点シェーダーの簡略化されたバージョンを使用して、ライト空間内の頂点の位置のみをレンダリングします。 照明の法線、セカンダリ変換などを含めないでください。
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;
}
このチュートリアルの次のパートでは、深度テストを使用してレンダリングを