깊이 버퍼 디바이스 리소스 만들기

섀도 볼륨에 대한 깊이 테스트를 지원하는 데 필요한 Direct3D 디바이스 리소스 생성 방법을 알아봅니다. 연습: Direct3D 11에서 깊이 버퍼를 사용하여 섀도 볼륨 구현 1부.

필요한 리소스

섀도 볼륨에 대한 깊이 맵을 렌더링하려면 다음 Direct3D 디바이스 종속 리소스가 필요합니다.

  • 깊이 맵에 대한 리소스(버퍼)
  • 리소스에 대한 깊이 스텐실 보기 및 셰이더 리소스 뷰
  • 비교 샘플러 상태 개체
  • 밝은 POV 매트릭스에 대한 상수 버퍼
  • 그림자 맵을 렌더링하기 위한 뷰포트(일반적으로 정사각형 뷰포트)
  • 전면 컬링을 사용하도록 설정하는 렌더링 상태 개체
  • 또한 렌더링 상태 개체를 아직 사용하지 않은 경우 백 페이스 컬링으로 다시 전환해야 합니다.

이러한 리소스 만들기는 디바이스 종속 리소스 만들기 루틴에 포함되어야 합니다. 예를 들어 새 디바이스 드라이버가 설치되거나 사용자가 앱을 다른 그래픽 어댑터에 연결된 모니터로 이동하는 경우 렌더러가 다시 만들 수 있습니다.

기능 지원 확인

깊이 맵을 만들기 전에 Direct3D 디바이스에서 CheckFeatureSupport 메서드를 호출하고, D3D11_FEATURE_D3D9_SHADOW_SUPPORT를 요청한 다음, D3D11_FEATURE_DATA_D3D9_SHADOW_SUPPORT 구조를 제공합니다.

D3D11_FEATURE_DATA_D3D9_SHADOW_SUPPORT isD3D9ShadowSupported;
ZeroMemory(&isD3D9ShadowSupported, sizeof(isD3D9ShadowSupported));
pD3DDevice->CheckFeatureSupport(
    D3D11_FEATURE_D3D9_SHADOW_SUPPORT,
    &isD3D9ShadowSupported,
    sizeof(D3D11_FEATURE_D3D9_SHADOW_SUPPORT)
    );

if (isD3D9ShadowSupported.SupportsDepthAsTextureWithLessEqualComparisonFilter)
{
    // Init shadow map resources

이 기능이 지원되지 않는 경우 샘플 비교 함수를 호출하는 셰이더 모델 4 수준 9_x에 대해 컴파일된 셰이더를 로드하지 마세요. 대부분의 경우 이 기능에 대한 지원이 부족하다는 것은 GPU가 적어도 WDDM 1.2를 지원하도록 업데이트되지 않은 드라이버가 있는 레거시 디바이스임을 의미합니다. 디바이스가 기능 레벨 10_0 이상을 지원하는 경우 대신 셰이더 모델 4_0에 대해 컴파일된 샘플 비교 셰이더를 로드할 수 있습니다.

깊이 버퍼 만들기

먼저 정밀도가 높은 깊이 형식으로 깊이 맵을 만들어 봅니다. 일치하는 셰이더 리소스 뷰 속성을 먼저 설정합니다. 예를 들어 디바이스 메모리가 부족하거나 하드웨어에서 지원하지 않는 형식으로 인해 리소스를 만들지 못하는 경우 정밀도가 낮은 형식을 시도하고 일치하도록 속성을 변경합니다.

이 단계는 선택적 단계로서, 중간 해상도 Direct3D 기능 수준 9_1 디바이스에서 렌더링하는 경우 등 낮은 정밀도 깊이 형식만 필요한 경우에 수행합니다.

D3D11_TEXTURE2D_DESC shadowMapDesc;
ZeroMemory(&shadowMapDesc, sizeof(D3D11_TEXTURE2D_DESC));
shadowMapDesc.Format = DXGI_FORMAT_R24G8_TYPELESS;
shadowMapDesc.MipLevels = 1;
shadowMapDesc.ArraySize = 1;
shadowMapDesc.SampleDesc.Count = 1;
shadowMapDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_DEPTH_STENCIL;
shadowMapDesc.Height = static_cast<UINT>(m_shadowMapDimension);
shadowMapDesc.Width = static_cast<UINT>(m_shadowMapDimension);

HRESULT hr = pD3DDevice->CreateTexture2D(
    &shadowMapDesc,
    nullptr,
    &m_shadowMap
    );

그런 다음, 리소스 뷰를 만듭니다. 깊이 스텐실 보기에서 mip 슬라이스를 0으로 설정하고 셰이더 리소스 뷰에서 mip 수준을 1로 설정합니다. 이러한 두 보기 모두에 TEXTURE2D의 텍스처 차원이 있고, 이러한 두 보기 모두가 일치하는 DXGI_FORMAT을 사용해야 합니다.

D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
ZeroMemory(&depthStencilViewDesc, sizeof(D3D11_DEPTH_STENCIL_VIEW_DESC));
depthStencilViewDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
depthStencilViewDesc.Texture2D.MipSlice = 0;

D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;
ZeroMemory(&shaderResourceViewDesc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
shaderResourceViewDesc.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
shaderResourceViewDesc.Texture2D.MipLevels = 1;

hr = pD3DDevice->CreateDepthStencilView(
    m_shadowMap.Get(),
    &depthStencilViewDesc,
    &m_shadowDepthView
    );

hr = pD3DDevice->CreateShaderResourceView(
    m_shadowMap.Get(),
    &shaderResourceViewDesc,
    &m_shadowResourceView
    );

비교 상태 만들기

이제 비교 샘플러 상태 개체를 만듭니다. 기능 수준 9_1은 D3D11_COMPARISON_LESS_EQUAL만 지원합니다. 필터링 선택은 다양한 하드웨어에서 섀도 맵을 지원에 자세히 설명되어 있습니다. 또는 더 빠른 섀도 맵을 위해 포인트 필터링만 선택할 수 있습니다.

D3D11_TEXTURE_ADDRESS_BORDER 주소 모드를 지정할 수 있으며, 이 주소 모드를 지정하면 기능 수준 9_1 디바이스에서 작동합니다. 이는 깊이 테스트를 수행하기 전에 픽셀이 조명의 보기 절두체에 있는지 여부를 테스트하지 않는 픽셀 셰이더에 적용됩니다. 각 테두리에 대해 0 또는 1을 지정하여 조명의 보기 절두체 외부 픽셀이 깊이 테스트를 통과하거나 실패하는지 여부 및 조명 또는 그림자에 있는지 여부를 제어할 수 있습니다.

기능 수준 9_1에서 다음 필수 값을 설정해야 합니다. MinLOD를 0으로, MaxLODD3D11_FLOAT32_MAX로, MaxAnisotropy를 0으로 설정합니다.

D3D11_SAMPLER_DESC comparisonSamplerDesc;
ZeroMemory(&comparisonSamplerDesc, sizeof(D3D11_SAMPLER_DESC));
comparisonSamplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER;
comparisonSamplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
comparisonSamplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;
comparisonSamplerDesc.BorderColor[0] = 1.0f;
comparisonSamplerDesc.BorderColor[1] = 1.0f;
comparisonSamplerDesc.BorderColor[2] = 1.0f;
comparisonSamplerDesc.BorderColor[3] = 1.0f;
comparisonSamplerDesc.MinLOD = 0.f;
comparisonSamplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
comparisonSamplerDesc.MipLODBias = 0.f;
comparisonSamplerDesc.MaxAnisotropy = 0;
comparisonSamplerDesc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL;
comparisonSamplerDesc.Filter = D3D11_FILTER_COMPARISON_MIN_MAG_MIP_POINT;

// Point filtered shadows can be faster, and may be a good choice when
// rendering on hardware with lower feature levels. This sample has a
// UI option to enable/disable filtering so you can see the difference
// in quality and speed.

DX::ThrowIfFailed(
    pD3DDevice->CreateSamplerState(
        &comparisonSamplerDesc,
        &m_comparisonSampler_point
        )
    );

렌더링 상태 만들기

이제 전면 컬링을 사용하도록 설정하는 데 사용할 수 있는 렌더링 상태를 만듭니다. 기능 수준 9_1 디바이스를 사용하려면 DepthClipEnabletrue로 설정해야 합니다.

D3D11_RASTERIZER_DESC drawingRenderStateDesc;
ZeroMemory(&drawingRenderStateDesc, sizeof(D3D11_RASTERIZER_DESC));
drawingRenderStateDesc.CullMode = D3D11_CULL_BACK;
drawingRenderStateDesc.FillMode = D3D11_FILL_SOLID;
drawingRenderStateDesc.DepthClipEnable = true; // Feature level 9_1 requires DepthClipEnable == true
DX::ThrowIfFailed(
    pD3DDevice->CreateRasterizerState(
        &drawingRenderStateDesc,
        &m_drawingRenderState
        )
    );

후면 컬링을 사용하도록 설정하는 데 사용할 수 있는 렌더링 상태를 만듭니다. 렌더링 코드가 이미 후면 컬링을 켜면 이 단계를 건너뛸 수 있습니다.

D3D11_RASTERIZER_DESC shadowRenderStateDesc;
ZeroMemory(&shadowRenderStateDesc, sizeof(D3D11_RASTERIZER_DESC));
shadowRenderStateDesc.CullMode = D3D11_CULL_FRONT;
shadowRenderStateDesc.FillMode = D3D11_FILL_SOLID;
shadowRenderStateDesc.DepthClipEnable = true;

DX::ThrowIfFailed(
    pD3DDevice->CreateRasterizerState(
        &shadowRenderStateDesc,
        &m_shadowRenderState
        )
    );

상수 버퍼 만들기

조명의 관점에서 렌더링하기 위한 상수 버퍼를 만드는 것을 잊지 마세요. 이 상수 버퍼를 사용하여 셰이더에 대한 조명 위치를 지정할 수도 있습니다. 포인트 조명에 원근 행렬을 사용하고 방향성 광원(예: 햇빛)에 직교적 행렬을 사용합니다.

DX::ThrowIfFailed(
    m_deviceResources->GetD3DDevice()->CreateBuffer(
        &viewProjectionConstantBufferDesc,
        nullptr,
        &m_lightViewProjectionBuffer
        )
    );

상수 버퍼 데이터를 채웁니다. 초기화 중에 상수 버퍼를 한 번 업데이트하고 이전 프레임 이후 조명 값이 변경된 경우 다시 업데이트합니다.

{
    XMMATRIX lightPerspectiveMatrix = XMMatrixPerspectiveFovRH(
        XM_PIDIV2,
        1.0f,
        12.f,
        50.f
        );

    XMStoreFloat4x4(
        &m_lightViewProjectionBufferData.projection,
        XMMatrixTranspose(lightPerspectiveMatrix)
        );

    // Point light at (20, 15, 20), pointed at the origin. POV up-vector is along the y-axis.
    static const XMVECTORF32 eye = { 20.0f, 15.0f, 20.0f, 0.0f };
    static const XMVECTORF32 at = { 0.0f, 0.0f, 0.0f, 0.0f };
    static const XMVECTORF32 up = { 0.0f, 1.0f, 0.0f, 0.0f };

    XMStoreFloat4x4(
        &m_lightViewProjectionBufferData.view,
        XMMatrixTranspose(XMMatrixLookAtRH(eye, at, up))
        );

    // Store the light position to help calculate the shadow offset.
    XMStoreFloat4(&m_lightViewProjectionBufferData.pos, eye);
}
context->UpdateSubresource(
    m_lightViewProjectionBuffer.Get(),
    0,
    NULL,
    &m_lightViewProjectionBufferData,
    0,
    0
    );

뷰포트 만들기

그림자 맵에 렌더링하려면 별도의 뷰포트가 필요합니다. 뷰포트는 디바이스 기반 리소스가 아니며, 코드의 다른 위치에서 자유롭게 만들 수 있습니다. 섀도 맵과 함께 뷰포트를 만들면 뷰포트의 차원을 섀도 맵 차원과 일치시키는 데 더 편리할 수 있습니다.

// Init viewport for shadow rendering
ZeroMemory(&m_shadowViewport, sizeof(D3D11_VIEWPORT));
m_shadowViewport.Height = m_shadowMapDimension;
m_shadowViewport.Width = m_shadowMapDimension;
m_shadowViewport.MinDepth = 0.f;
m_shadowViewport.MaxDepth = 1.f;

이 연습의 다음 부분에서는 깊이 버퍼에 렌더링하여 섀도 맵을 만드는 방법을 알아봅니다.