Depth-Stencil 기능 구성

이 섹션에서는 출력 병합기 단계의 깊이 스텐실 버퍼와 깊이 스텐실 상태를 설정하는 단계를 다룹니다.

깊이 스텐실 버퍼와 해당 깊이 스텐실 상태를 사용하는 방법을 숙지한 후에는 고급 스텐실 기술을 참조하세요.

Depth-Stencil 리소스 만들기

텍스처 리소스를 사용하여 깊이 스텐실 버퍼를 만듭니다.

ID3D11Texture2D* pDepthStencil = NULL;
D3D11_TEXTURE2D_DESC descDepth;
descDepth.Width = backBufferSurfaceDesc.Width;
descDepth.Height = backBufferSurfaceDesc.Height;
descDepth.MipLevels = 1;
descDepth.ArraySize = 1;
descDepth.Format = pDeviceSettings->d3d11.AutoDepthStencilFormat;
descDepth.SampleDesc.Count = 1;
descDepth.SampleDesc.Quality = 0;
descDepth.Usage = D3D11_USAGE_DEFAULT;
descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
descDepth.CPUAccessFlags = 0;
descDepth.MiscFlags = 0;
hr = pd3dDevice->CreateTexture2D( &descDepth, NULL, &pDepthStencil );

깊이 스텐실 상태 만들기

깊이 스텐실 상태는 출력 병합기 단계에 깊이 스텐실 테스트를 수행하는 방법을 알려줍니다. 깊이 스텐실 테스트를 통해 지정된 픽셀을 그려야 하는지 여부가 결정됩니다.

D3D11_DEPTH_STENCIL_DESC dsDesc;

// Depth test parameters
dsDesc.DepthEnable = true;
dsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
dsDesc.DepthFunc = D3D11_COMPARISON_LESS;

// Stencil test parameters
dsDesc.StencilEnable = true;
dsDesc.StencilReadMask = 0xFF;
dsDesc.StencilWriteMask = 0xFF;

// Stencil operations if pixel is front-facing
dsDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
dsDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

// Stencil operations if pixel is back-facing
dsDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
dsDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
dsDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
dsDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

// Create depth stencil state
ID3D11DepthStencilState * pDSState;
pd3dDevice->CreateDepthStencilState(&dsDesc, &pDSState);

DepthEnable 및 StencilEnable은 깊이 및 스텐실 테스트를 사용하거나 사용하지 않도록 설정합니다. 깊이 테스트를 사용하지 않도록 설정하고 깊이 버퍼에 쓰지 않도록 하려면 DepthEnable을 FALSE 로 설정합니다. 스텐실 테스트를 사용하지 않도록 설정하고 스텐실 버퍼에 쓰지 않도록 하려면 StencilEnable을 FALSE 로 설정합니다(DepthEnable이 FALSE 이고 StencilEnable이 TRUE인 경우 깊이 테스트는 항상 스텐실 작업을 통과합니다).

DepthEnable은 출력 병합기 단계에만 영향을 줍니다. 데이터가 픽셀 셰이더에 입력되기 전에 값의 클리핑, 깊이 바이어스 또는 클램핑에는 영향을 주지 않습니다.

OM 단계에 깊이 스텐실 데이터 바인딩

깊이 스텐실 상태 바인딩

// Bind depth stencil state
pDevice->OMSetDepthStencilState(pDSState, 1);

뷰를 사용하여 깊이 스텐실 리소스를 바인딩합니다.

D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
descDSV.Format = DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
descDSV.Texture2D.MipSlice = 0;

// Create the depth stencil view
ID3D11DepthStencilView* pDSV;
hr = pd3dDevice->CreateDepthStencilView( pDepthStencil, // Depth stencil texture
                                         &descDSV, // Depth stencil desc
                                         &pDSV );  // [out] Depth stencil view

// Bind the depth stencil view
pd3dDeviceContext->OMSetRenderTargets( 1,          // One rendertarget view
                                &pRTV,      // Render target view, created earlier
                                pDSV );     // Depth stencil view for the render target

렌더링 대상 뷰의 배열은 ID3D11DeviceContext::OMSetRenderTargets에 전달될 수 있지만 이러한 모든 렌더링 대상 뷰는 단일 깊이 스텐실 보기에 해당합니다. Direct3D 11의 렌더링 대상 배열은 애플리케이션이 기본 수준에서 동시에 여러 렌더링 대상에 렌더링할 수 있도록 하는 기능입니다. 렌더링 대상 배열은 ID3D11DeviceContext::OMSetRenderTargets(기본적으로 Direct3D 9에서 사용하는 메서드)를 여러 차례 호출하여 렌더링 대상을 개별적으로 설정하는 데 비해 향상된 성능을 제공합니다.

렌더링 대상은 모두 같은 유형의 리소스여야 합니다. 다중 샘플링 앤티앨리어싱 사용 시 모든 바운드 렌더링 대상과 깊이 버퍼의 샘플 수가 동일해야 합니다.

버퍼를 렌더링 대상으로 사용할 경우 깊이 스텐실 테스트와 다중 렌더링 대상은 지원되지 않습니다.

  • 최대 8개의 렌더링 대상을 동시에 바인딩할 수 있습니다.
  • 모든 렌더링 대상의 크기는 모든 차원(3D의 경우 너비, 높이, 깊이, *배열 유형의 경우 배열 크기)에서 동일해야 합니다.
  • 각 렌더링 대상마다 데이터 형식이 다를 수 있습니다.
  • 쓰기 마스크는 렌더링 대상에 기록되는 데이터를 제어합니다. 출력 쓰기 마스크는 렌더링 대상과 구성 요소 수준별로 렌더링 대상에 기록되는 데이터를 제어합니다.

고급 스텐실 기술

깊이 스텐실 버퍼의 스텐실 부분은 합성, 전사, 윤곽선 등의 렌더링 효과를 만드는 데 사용할 수 있습니다.

합성

애플리케이션에서 스텐실 버퍼를 사용하여 2D 또는 3D 이미지를 3D 장면으로 합성할 수 있습니다. 스텐실 버퍼 안의 마스크는 렌더링 대상 화면의 특정 영역을 폐색하는 데 사용합니다. 그러면 텍스트 또는 비트맵과 같은 저장된 2D 정보가 폐색된 영역에 기록될 수 있습니다. 또는 애플리케이션이 렌더링 대상 화면의 스텐실 마스크된 영역에 추가 3D 원형을 렌더링할 수 있습니다. 심지어 전체 장면을 렌더링할 수도 있습니다.

게임은 흔히 여러 3D 장면을 합성합니다. 예를 들어 드라이빙 게임은 일반적으로 백미러를 표시합니다. 미러에는 드라이버 후방의 3D 장면에 대한 뷰가 포함됩니다. 이 뷰는 기본적으로 드라이버의 전방 뷰와 합성된 두 번째 3D 장면입니다.

전사

Direct3D 애플리케이션은 전사를 사용하여 특정 원형 이미지에서 어느 픽셀을 렌더링 대상 화면에 그릴지 제어할 수 있습니다. 애플리케이션은 공면 다각형이 올바로 렌더링될 수 있도록 원형의 이미지에 전사를 적용합니다.

예를 들어 도로에 타이어 마크와 노란색 선을 적용할 때 표시가 도로 바로 위에 나타나야 합니다. 그러나 표시와 도로의 z 값은 동일합니다. 따라서 깊이 버퍼가 두 표시를 명료하게 분리하지 못할 수 있습니다. 후면 원형의 일부 픽셀이 전면 원형 위에, 또는 그 반대로 렌더링될 수 있습니다. 결과 이미지가 프레임마다 어른거려 보입니다. 이 효과를 z 플라이팅 또는 미광이라고 합니다.

이 문제를 해결하기 위해 스텐실을 사용하여 전사가 표시될 후면 원형의 섹션을 마스킹합니다. z 버퍼링을 끄고 전면 원형의 이미지를 렌더링 대상 화면의 마스킹된 영역에 렌더링합니다.

다중 텍스처 블렌딩을 사용하여 이 문제를 해결할 수 있습니다.

윤곽선 및 실루엣

윤곽선과 실루엣 같은 보다 추상적인 효과에 스텐실 버퍼를 사용할 수 있습니다.

애플리케이션에서 2개의 렌더링 패스를 수행하는 경우(스텐실 마스크를 생성하는 렌더링 패스와 이미지에 스텐실 마스크를 적용하는 렌더링 패스, 단, 두 번째 패스의 원형이 조금 더 작음) 결과 이미지에는 원형의 윤곽선만 있습니다. 그러면 애플리케이션이 이미지의 스텐실 마스크된 영역을 단색으로 채워 원형이 볼록하게 보이도록 할 수 있습니다.

스텐실 마스크가 렌더링하는 원형과 동일한 크기 및 형태인 경우 결과 이미지는 원형이 위치해야 할 구멍을 포함합니다. 그러면 애플리케이션이 구멍을 검은색으로 채워 원형의 실루엣을 생성할 수 있습니다.

양면 스텐실

스텐실 버퍼를 사용하여 그림자를 그리는 데 섀도 볼륨이 사용됩니다. 애플리케이션은 실루엣 가장자리를 컴퓨팅하고 이들을 조명에서 3D 볼륨 집합으로 밀어내는 방식으로 기하 도형을 폐색하여 형성된 섀도 볼륨을 컴퓨팅합니다. 그러면 이러한 볼륨이 스텐실 버퍼로 두 번 렌더링됩니다.

첫 번째 렌더는 전면 다각형을 그리고 스텐실 버퍼 값을 점증시킵니다. 두 번째 렌더는 섀도 볼륨의 후면 다각형을 그리고 스텐실 버퍼 값을 점감시킵니다. 일반적으로 모든 증가 및 감소된 값은 상계됩니다. 그러나 장면은 이미 정상 기하 도형으로 렌더링되었기 때문에 섀도 볼륨이 렌더링될 때 일부 픽셀이 z 버퍼 테스트에서 실패합니다. 스텐실 버퍼에 남은 값이 그림자의 픽셀에 해당합니다. 이 나머지 스텐실 버퍼 콘텐츠는 마스크로 사용되어 전부를 포함하는 큰 검은색 사각형을 장면에 알파 혼합합니다. 스텐실 버퍼가 마스크로 작용하므로 결과는 그림자 안의 픽셀이 어두워지는 것입니다.

즉, 그림자 기하 도형을 광원당 두 번 그립니다. 따라서 GPU의 꼭짓점 처리량에 부담을 줍니다. 양면 스텐실 기능은 이 상황을 완화하기 위해 고안되었습니다. 이 접근 방법에는 전면 삼각형과 후면 삼각형에 대해 각각 하나씩, 두 개의 스텐실 상태 집합이 있으며, 이름은 아래에서 설명합니다. 이러한 방식으로 각 섀도 볼륨에 대해 광원당 한 번의 패스만 그립니다.

양면 스텐실 구현의 예는 ShadowVolume10 샘플에서 찾을 수 있습니다.

깊이 스텐실 버퍼를 텍스처로 읽기

셰이더에서 비활성 깊이 스텐실 버퍼를 텍스처로 읽을 수 있습니다. 깊이 스텐실 버퍼를 텍스처로 읽는 애플리케이션은 2개의 패스로 렌더링됩니다. 첫 번째 패스는 깊이 스텐실 버퍼에 쓰고, 두 번째 패스는 버퍼에서 읽습니다. 이를 통해 셰이더에서 이전에 버퍼에 기록된 깊이 또는 스텐실 값을 현재 렌더링 중인 픽셀에 대한 값과 비교할 수 있습니다. 비교 결과를 사용하여 섀도 매핑 또는 파티클 시스템의 부드러운 파티클과 같은 효과를 만들 수 있습니다.

깊이 스텐실 리소스와 셰이더 리소스 모두로 사용할 수 있는 깊이 스텐실 버퍼를 만들려면 Depth-Stencil 리소스 만들기 섹션의 샘플 코드에 대해 몇 가지 변경이 필요합니다.

  • 깊이 스텐실 리소스는 DXGI_FORMAT_R32_TYPELESS 같은 무형식 형식이어야 합니다.

    descDepth.Format = DXGI_FORMAT_R32_TYPELESS;
    
  • 깊이 스텐실 리소스는 D3D10_BIND_DEPTH_STENCIL 및 D3D10_BIND_SHADER_RESOURCE 바인딩 플래그를 모두 사용해야 합니다.

    descDepth.BindFlags = D3D10_BIND_DEPTH_STENCIL | D3D10_BIND_SHADER_RESOURCE;
    

또한 D3D11_SHADER_RESOURCE_VIEW_DESC 구조체 및 ID3D11Device::CreateShaderResourceView를 사용하여 깊이 버퍼에 대한 셰이더 리소스 뷰를 만들어야 합니다. 셰이더 리소스 뷰는 깊이 스텐실 리소스를 만들 때 지정된 무형식 형식과 동일한 DXGI_FORMAT_R32_FLOAT 형식화된 형식을 사용합니다.

첫 번째 렌더링 단계에서 깊이 버퍼는 OM 스테이지에 데이터 바인딩 Depth-Stencil 섹션에 설명된 대로 바인딩됩니다. 형식이 D3D11_DEPTH_STENCIL_VIEW_DESC 전달됩니다. 형식은 형식화된 형식(예: DXGI_FORMAT_D32_FLOAT)을 사용합니다. 첫 번째 렌더링 통과 후 깊이 버퍼에는 장면의 깊이 값이 포함됩니다.

두 번째 렌더링 패스에서 ID3D11DeviceContext::OMSetRenderTargets 함수는 깊이 스텐실 보기를 NULL 또는 다른 깊이 스텐실 리소스로 설정하는 데 사용되며 셰이더 리소스 뷰는 ID3D11EffectShaderResourceVariable::SetResource를 사용하여 셰이더에 전달됩니다. 이렇게 하면 셰이더가 첫 번째 렌더링 단계에서 계산된 깊이 값을 조회할 수 있습니다. 첫 번째 렌더링 패스의 관점이 두 번째 렌더링 패스와 다른 경우 깊이 값을 검색하려면 변환을 적용해야 합니다. 예를 들어 그림자 매핑 기술을 사용하는 경우 첫 번째 렌더링 패스는 광원의 관점에서, 두 번째 렌더링 패스는 뷰어의 관점에서 전달됩니다.

출력 병합기 단계

파이프라인 단계(Direct3D 10)