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

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

DirectX11
기본 설정을 기준으로 Depth Test
는 위 그림과 같이 이루어진다. 절두체의 가까운 면의 깊이값은 0
, 먼 면의 깊이값은 1
이 된다. 렌더링되는 오브젝트의 절두체의 두 면 사이의 위치에 따라 렌더링되는 각 픽셀의 깊이값은 0
~ 1
사이의 값을 가지게 된다.
동일한 위치의 픽셀에 새로운 값이 렌더링될 때 새로운 깊이값이 기존에 저장된 깊이값보다 작을 때 픽셀의 값은 새로운 값으로 갱신된다. 이 Depth Buffer
를 사용하는것에 대해 알아보자.
Texture2D 준비
깊이값을 저장하는 것도 결국은 스크린 해상도 크기와 동일한 영역의 리소스가 필요하다. 이런 유형의 값들을 저장하는데 가장 적합한 것은 Texture2D
이다. 따라서 해상도 크기의 Texture2D
리소스를 하나 준비해야 한다.
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 );
Width
와 Height
는 뷰포트 해상도와 동일한 크기를 지정해준다. Format
은 DXGI_FORMAT_D24_UNORM_S8_UINT
으로 지정되었는데 D24_UNORM
은 24비트의 unsigned normalized값, 즉 0
~ 1
사이의 값을 24비트로 표현한 데이터를 뜻한다. S8_UINT
는 8비트 unsigned int값을 뜻한다. 포맷이 2개의 데이터형으로 분리된 이유는 이 Texture
리소스를 Depth Buffer
와 Stencil Buffer
로 함께 사용하기 위함이다. Depth-Stencil Buffer
로 사용하므로 BindFlags
를 D3D11_BIND_DEPTH_STENCIL
로 지정해주는 것 또한 잊지 말아야 한다.
DepthStencilView 준비
새로운 픽셀이 렌더링될 때 픽셀의 깊이값은 렌더링 파이프라인의 Output-Merge Stage
에서 기록될 수 있다. 이 기록되는 값을 위에서 생성한 Texture2D
리소스에 저장하기 위해서는 리소스를 렌더링 파이프라인에 연결해주어야 한다. [DirectX11]3. 초기화 – DirectX11 API를 이용한 빈 화면 렌더링 하기에서와 마찬가지로 리소스뷰를 통해서만 렌더링 파이프라인에 연결할 수 있다. 따라서 ID3D11DepthStencilView
를 생성하고 렌더링 파이프라인에 연결해줘야 한다.
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를 이용한 빈 화면 렌더링 하기에서 사용했던 함수와 동일하다.
ID3D11DeviceContext->OMSetRenderTargets( 1, &RenderTargetView, DepthStencilView );
이전에는 nullptr
로 전달했던 ID3D11DepthStencilView
파라메터를 생성한 리소스 뷰를 전달하도록 수정하였다.
깊이값 초기화하기
새로운 프레임을 렌더링할 때마다 이전 프레임에 기록해두었던 Depth Buffer
의 값들을 초기화해줄 필요가 있다. 그렇지 않으면 올바른 깊이값의 비교가 되지 않아 정상적인 렌더링 결과를 얻지 못 할 수 있다.
ID3D11DeviceContext->ClearDepthStencilView( DepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.f, 0.f );
처음에 말했던 것처럼 디폴트는 새로운 픽셀의 깊이값이 작을 때 해당 픽셀을 갱신하므로 초기화값은 가장 큰 깊이값인 1
로 지정해준다.
Viewport의 깊이값 지정해주기
추가해줘야 할 셋팅이 하나 더 있다. D3D11_VIEWPORT
에서 MinDepth
와 MaxDepth
를 지정해주어야 하는 것이다. 이 값이 지정되지 않으면 정상적인 Depth Test
를 하지 못하므로 유의가 필요하다.
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
를 해도 다음과 같이 이상한 결과가 나온다.

뭔가 제대로 되는 것처럼 보일뻔했으나, 결과적으로는 잘 못 렌더링되고 있다.
이렇게 Depth Test
가 올바르게 이루어지면 다음과 같은 정상적인 렌더링 결과를 얻을 수 있다.

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