본문 바로가기
둥지/그래픽스

깊이 버퍼(Depth Buffer)

by 까닭 2023. 3. 31.

깊이 버퍼(Depth Buffer)

깊이 버퍼는 이미지가 굳이 필요 없는 텍스쳐이다.

왜냐하면 각 픽셀의 깊이 정보만 담으면 되기 때문이다.

 

픽셀의 깊이는 0.0 ~ 1.0 사이의 값으로 지정되는데, 0.0은 관찰자(화면 앞의 유저)와 최대한 가까운 픽셀에 해당하고 1.0은 가장 먼 픽셀에 해당한다. 깊이 버퍼의 원소들과 후면 버퍼(Back Buffer)의 픽셀들은 일대일로 매핑된다.


즉, 후면 버퍼의 xy번째 픽셀은 깊이 버퍼의 xy번째 원소에 대응되는 것.

그러므로 후면 버퍼의 해상도와 깊이 버퍼의 해상도는 같아야 한다.

 

하나의 오브젝트 A를 다른 오브젝트 B가 가린다고 가정해보자.

그렇다면 B가 A보다 앞에 있는지 어떻게 판단해야 할까?

 

DirectX에선 A의 픽셀들이 B의 픽셀보다 앞에 있는지를 판정하기 위해,
깊이 버퍼링(Depth Buffering) 혹은 Z-버퍼링(Z-Buffering)이라는 기법을 사용한다.

 

그 기법을 통해 프로그래머는 오브젝트들이 화면에 그려질 순서를 생각하지 않아도 된다.

 

반대로, 깊이 문제를 생각하고 싶지 않다면 화면 속 오브젝트들을
가장 먼 거리부터 가까운 거리 순으로 그리면 되는 것이다.

 

이는 마치 화가가 밑그림을 그리고 그 위에 덧씌워 그리는 것과 같은데, 이러한 기법을 화가 알고리즘이라 부른다.

 

🎨화가 알고리즘

화가 알고리즘은 컴퓨터 그래픽스에서 렌더링 순서를 정할 때 가장 단순하게 해결할 수 있는 방법이다.

 

풍경화를 그릴 때 사람과 집을 먼저 그리고 산과 바다를 그리는 화가가 없겠듯이,

화가 알고리즘 또한 먼 곳부터 가까운 순서대로 그려가면서 이전에 그린 먼 곳의 일부를 덮어버리는 기법이다.

 

화가 알고리즘에서는 화면 속 오브젝트들을 순서대로 정렬한 다음 먼 곳으로부터 가까운 곳으로까지 순서대로 렌더링한다. 이처럼 단순한 방법으로 가시도 문제를 해결할 수 있지만 화가 알고리즘은 한계가 명확하다.

 

가장 큰 문제는, 그리지 않아도 되는 오브젝트를 그리는 것이다.

화가 알고리즘의 특성 상 뒤에서 앞으로 그리기 때문에 앞의 오브젝트에 가려질 부분도 연산(라이팅, 텍스쳐링 등)이 들어가게 된다.

 

하지만 깊이 버퍼링을 사용할 경우 반투명 오브젝트가 아니라면 앞에서 뒤로 그리기 때문에 가려진 오브젝트는 그리지 않아도 돼서 비용 면에서 더 효율적이다.

 

화가 알고리즘 또한 이러한 단점이 보완된 역 화가 알고리즘이 존재하지만 위 사항을 제외하고는 일반 화가 알고리즘과 동일한 문제를 가지고 있다.

 

화가 알고리즘의 또 다른 한계는, 맞물려 있는 오브젝트의 경우 제대로 처리하지 못하는 것이다.

사실 이 문제는 깊이 버퍼링도 동일하다. 화가 알고리즘만큼 그 정도가 심하진 않지만 미세한 라운딩 오차로 깊이 버퍼링 혹은 Z-Fighting이라고 부르는 현상이 발생한다.

 

때문에 일부 그래픽 엔진들은 화가 알고리즘이 제공하는 순서대로 다각형의 모서리 부분을 그려내어 현상을 예방한다고 한다.

 

마지막으로 개인적인 생각은, 화가 알고리즘에서 렌더링 순서가 변경되었을 때 이를 재정렬하는 작업도 까다로울 것이다.

깊이 버퍼링의 경우 오브젝트마다 각 렌더링 순서를 수정할 수 있기 때문에 전체적인 렌더링 순서이나 정렬을 생각할 필요가 없다.

 

깊이 버퍼링

유저가 화면에 무언가 그릴 때 먼저 후면 버퍼를 기본 색상으로 지운다.

ID3D11DeviceContext::ClearRenderTargetView
(
    ID3D11RenderTargetView* pRenderTargetView, 
    const FLOAT *ColorRGBA //기본 색상
)

후면 버퍼가 지워지면 깊이 버퍼도 함께 디폴트 값으로 지워진다.

일반적으로 디폴트 값은 1.0.


즉, 픽셀이 가질 수 있는 최대 깊이값.

 

그럼 이제 A, B, C의 오브젝트들이 같은 위치에서 알파벳 순서대로 렌더링 되고 있다고 가정해보자.

그렇다면 화면이 갱신될 때마다 어떤 일이 벌어질까?

 

🔵A 오브젝트

깊이: 1.0

🔺B 오브젝트

깊이: 0.1

🟩C 오브젝트

깊이: 0.5

 

1. 버퍼가 지워진다.

: 픽셀들이 기본 색상으로 덮히고 깊이도 1.0으로 초기화된다.

2. A를 그린다.

: A의 픽셀과 깊이값이 깊이 버퍼의 깊이값보다 작거나 같으므로 화면에 그려지고
그 그려진 픽셀의 깊이값은 A의 깊이값으로 덮어진다.

3. B를 그린다.

: B의 픽셀과 깊이값이 A의 깊이값보다 작거나 같으므로 화면에 그려지고
그 그려진 픽셀의 깊이값은 B의 깊이값으로 덮어진다.

4. C를 그린다.

: C의 깊이값은 B의 깊이값보다 크므로 깊이 판정에 실패한다.
따라서 C의 깊이값은 B의 깊이값으로 덮어지지 않는다.

그려진 순서: 🔺, 🟩, 🔵

 

깊이 버퍼링 요약

렌더링되는 각 픽셀의 깊이값을 계산해서 깊이 판정을 수행한다.

깊이 판정은 후면 버퍼의 특정 픽셀 위치에 기록될 픽셀들의 깊이들을 비교한다.

깊이값을 비교했을 때 유저에게 가장 가까운 픽셀이 승자가 되어서 후면 버퍼에 기록된다.

 

후에, 이러한 깊이 버퍼를 통해 스텐실 버퍼에 부착하여 사용할 수도 있다.

'둥지 > 그래픽스' 카테고리의 다른 글

DirectX XInput 다뤄보기  (0) 2023.02.08