[DirectX11] 17. Depth-Stencil Buffer 사용하기.



[DirectX11] 16. FBX File로부터 모델 데이터 Import하기.에서 아래와 같이 렌더링 결과물을 얻을 수 있었다.



이는 앞면에 가려져 보이지 않아야할 그러니까, 렌더링되지 않아야할 부분들까지 모두 렌더링되어서 그런것이다. Depth Buffer를 이용한 Depth Test를 하면 이 이슈를 해소할 수 있다. Depth Buffer는 렌더링되는 픽셀의 깊이값을 저장한다. 그리고 같은 위치에 새로운 픽셀이 그려져야 할 때 저장된 깊이값과 새로운 픽셀의 깊이값을 비교해서 픽셀의 렌더링을 갱신할지 결정을 하게 된다.



DirectX11 기본 설정을 기준으로 Depth Test는 위 그림과 같이 이루어진다. 절두체의 가까운 면의 깊이값은 0, 먼 면의 깊이값은 1이 된다. 렌더링되는 오브젝트의 절두체의 두 면 사이의 위치에 따라 렌더링되는 각 픽셀의 깊이값은 0 ~ 1 사이의 값을 가지게 된다.
동일한 위치의 픽셀에 새로운 값이 렌더링될 때 새로운 깊이값이 기존에 저장된 깊이값보다 작을 때 픽셀의 값은 새로운 값으로 갱신된다. 이 Depth Buffer를 사용하는것에 대해 알아보자.



Texture2D 준비


깊이값을 저장하는 것도 결국은 스크린 해상도 크기와 동일한 영역의 리소스가 필요하다. 이런 유형의 값들을 저장하는데 가장 적합한 것은 Texture2D이다. 따라서 해상도 크기의 Texture2D리소스를 하나 준비해야 한다.

C++
ID3D11Texture2D* DepthTexture = nullptr;

D3D11_TEXTURE2D_DESC td;  
ZeroMemory( &td, sizeof( D3D11_TEXTURE2D_DESC ) );  
  
td.Width            = 1920;  
td.Height           = 1080;  
td.MipLevels        = 1;  
td.ArraySize        = 1;  
td.Format           = DXGI_FORMAT_D24_UNORM_S8_UINT;  
td.SampleDesc.Count = 1;  
td.Usage            = D3D11_USAGE_DEFAULT;  
td.BindFlags        = D3D11_BIND_DEPTH_STENCIL;  
  
ID3D11Device->CreateTexture2D( &td, nullptr, &DepthTexture );

WidthHeight는 뷰포트 해상도와 동일한 크기를 지정해준다. FormatDXGI_FORMAT_D24_UNORM_S8_UINT으로 지정되었는데 D24_UNORM은 24비트의 unsigned normalized값, 즉 0 ~ 1사이의 값을 24비트로 표현한 데이터를 뜻한다. S8_UINT는 8비트 unsigned int값을 뜻한다. 포맷이 2개의 데이터형으로 분리된 이유는 이 Texture리소스를 Depth BufferStencil Buffer로 함께 사용하기 위함이다. Depth-Stencil Buffer로 사용하므로 BindFlagsD3D11_BIND_DEPTH_STENCIL로 지정해주는 것 또한 잊지 말아야 한다.



DepthStencilView 준비


새로운 픽셀이 렌더링될 때 픽셀의 깊이값은 렌더링 파이프라인의 Output-Merge Stage에서 기록될 수 있다. 이 기록되는 값을 위에서 생성한 Texture2D리소스에 저장하기 위해서는 리소스를 렌더링 파이프라인에 연결해주어야 한다. [DirectX11]3. 초기화 – DirectX11 API를 이용한 빈 화면 렌더링 하기에서와 마찬가지로 리소스뷰를 통해서만 렌더링 파이프라인에 연결할 수 있다. 따라서 ID3D11DepthStencilView를 생성하고 렌더링 파이프라인에 연결해줘야 한다.

C++
ID3D11DepthStencilView* DepthStencilView = nullptr;

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

ID3D11Device->CreateDepthStencilView( DepthTexture, &dsvd, &DepthStencilView );

공식 레퍼런스는 마이크로 소프트에 있다. 위와 같이 미리 생성한 Texture2D를 렌더링 파이프라인에 연결할 ID3D11DepthStencilView를 생성해준다.



렌더링 파이프라인에 연결하기


리소스 뷰를 생성했으니, 이것을 렌더링 파이프라인에 연결해주어야 한다. 렌더링 파이프라인에 대한 연결은 [DirectX11]3. 초기화 – DirectX11 API를 이용한 빈 화면 렌더링 하기에서 사용했던 함수와 동일하다.

C++
ID3D11DeviceContext->OMSetRenderTargets( 1, &RenderTargetView, DepthStencilView );

이전에는 nullptr로 전달했던 ID3D11DepthStencilView파라메터를 생성한 리소스 뷰를 전달하도록 수정하였다.



깊이값 초기화하기


새로운 프레임을 렌더링할 때마다 이전 프레임에 기록해두었던 Depth Buffer의 값들을 초기화해줄 필요가 있다. 그렇지 않으면 올바른 깊이값의 비교가 되지 않아 정상적인 렌더링 결과를 얻지 못 할 수 있다.

C++
ID3D11DeviceContext->ClearDepthStencilView( DepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.f, 0.f );

처음에 말했던 것처럼 디폴트는 새로운 픽셀의 깊이값이 작을 때 해당 픽셀을 갱신하므로 초기화값은 가장 큰 깊이값인 1로 지정해준다.



Viewport의 깊이값 지정해주기


추가해줘야 할 셋팅이 하나 더 있다. D3D11_VIEWPORT에서 MinDepthMaxDepth를 지정해주어야 하는 것이다. 이 값이 지정되지 않으면 정상적인 Depth Test를 하지 못하므로 유의가 필요하다.

C++
D3D11_VIEWPORT viewport;  
ZeroMemory( &viewport, sizeof( D3D11_VIEWPORT ) );  
  
viewport.TopLeftX = 0;  
viewport.TopLeftY = 0;  
viewport.MinDepth = 0.0f;  
viewport.MaxDepth = 1.0f;  
viewport.Width    = Width;  
viewport.Height   = Height;  
  
ID3D11DeviceContext->RSSetViewports( 1, &viewport );

특히 ZeroMemory로 구조체 데이터를 초기화한 상태이기 때문에 따로 지정하지 않으면 MinDepth, MaxDepth가 모두 0으로 초기화되어 있다. 이 경우 Depth Test를 해도 다음과 같이 이상한 결과가 나온다.


Pasted image 20250125225859.png

뭔가 제대로 되는 것처럼 보일뻔했으나, 결과적으로는 잘 못 렌더링되고 있다.


이렇게 Depth Test가 올바르게 이루어지면 다음과 같은 정상적인 렌더링 결과를 얻을 수 있다.


Pasted image 20250125230108.png

드디어 [DirectX11] 16. FBX File로부터 모델 데이터 Import하기.를 이용하여 올바르게 렌더링되는 결과를 얻을 수 있었다. 물론 아직 머티리얼 매칭이나 애니메이션등 갈 길이 멀긴 하지만 하나씩 차근차근 해보자.



yunei0313/CRY at Depth-Stencil-Buffer에서 코드확인을 할 수 있다.



이전글 : [DirectX11] 16. FBX File로부터 모델 데이터 Import하기.



Leave a Comment