1. 개요[편집]
깊이 버퍼(depth buffer), 또는 z-버퍼(z-buffer)는 화면의 픽셀마다 “지금까지 그 픽셀에 그려진 것 중 가장 가까운 표면의 깊이”를 한 개씩 저장해 두고, 새로 들어오는 조각(fragment)이 그보다 더 가까울 때만 픽셀을 갱신하도록 하는 픽셀 단위 가시성 판정 장치이자 그 알고리즘이다. 덕분에 겹쳐 있는 기하를 그리는 순서와 무관하게 항상 올바르게 합성할 수 있다. 삼각형을 뒤에서 앞으로 일일이 정렬해야 하는 화가 알고리즘(painter’s algorithm)의 순서 문제를, 정렬 없이 픽셀 수준에서 한 방에 해결한다는 것이 z-버퍼의 위력이다.1
절차는 간결하다. 프레임 시작에 깊이 버퍼를 먼 평면(far plane) 값으로 초기화한다. 각 조각마다 자신의 깊이를 같은 위치의 버퍼 값과 비교해, 더 가까우면(depth test 통과) 색을 쓰고 버퍼를 그 깊이로 갱신하며, 더 멀면 버린다. 오늘날 사실상 모든 실시간 래스터라이저의 기본 골격이다.
2. z-버퍼 알고리즘[편집]
핵심 판정은 다음 한 줄이다. 조각의 깊이를 , 버퍼에 저장된 값을 라 하면(가까울수록 값이 작다는 관례에서):
정렬이 필요 없다는 점이 결정적이다. 화가 알고리즘은 서로 뚫고 지나가는 삼각형이나 순환 겹침(A가 B를, B가 C를, C가 A를 가리는) 앞에서 무너지지만, z-버퍼는 픽셀마다 독립적으로 최근접을 고르므로 이런 병리 사례도 자연히 처리한다. 대가는 메모리다 — 화면 해상도만큼의 깊이 값을 상시 들고 있어야 한다. 이 값비싼 하드웨어 자원이 저렴해진 것이 z-버퍼가 표준이 된 배경이다.
3. Early-Z 최적화[편집]
기본 파이프라인은 픽셀 셰이더를 돌려 색을 구한 뒤 깊이 판정을 한다. 그런데 어차피 가려져 버려질 조각의 셰이더를 돌리는 건 순수한 낭비다. Early-Z(early depth test)는 순서를 뒤집어, 셰이더 실행 전에 깊이 판정을 먼저 해서 탈락이 확정된 조각의 셰이더를 아예 건너뛴다. 셰이딩이 무거울수록 이득이 커, 현대 GPU의 큰 성능 원천이다.2
단, 이 최적화는 조각의 최종 깊이를 셰이더 실행 전에 알 수 있다는 전제 위에 선다. 셰이더가 자기 깊이를 직접 쓰거나(gl_FragDepth 등), discard로 조건부로 조각을 버리면 그 전제가 깨져 GPU가 early-Z를 끄고 전통적 순서로 되돌아간다. 알파 테스트 나뭇잎 셰이더가 성능을 갉아먹는 흔한 이유가 이것이다.
4. 정밀도와 z-파이팅[편집]
깊이 값은 정규화 장치 좌표(NDC)에서 균등하게 분포하지 않는다. 원근 투영 때문에 저장되는 깊이가 에 가깝게 휘어, 정밀도의 대부분이 카메라 근처에 몰리고 먼 곳은 성기게 표현된다. 그 결과 멀리 있는 두 개의 거의 같은 평면(예: 겹친 벽, 지형 위의 도로)이 유한 정밀도 안에서 구분되지 않아, 프레임마다 어느 쪽이 앞인지 깜빡이는 z-파이팅(z-fighting)이 생긴다.
현대 엔진의 실용적 처방이 역-Z(reversed-Z)다. 근/원 평면의 깊이 매핑을 뒤집어 먼 곳(값이 0에 가까운 쪽)에 정밀도를 몰아주면, 부동소수점 표현이 0 근처에서 촘촘하다는 성질과 원근 왜곡이 서로 상쇄되어 깊이 정밀도가 거리 전반에 훨씬 고르게 퍼진다. 부동소수점 깊이 버퍼와 짝지으면 넓은 거리 범위에서도 z-파이팅이 극적으로 줄어, 오늘날 대규모 장면의 사실상 표준 기법이 되었다.3
5. 깊이 프리패스[편집]
Early-Z의 이득을 장면 전체에서 극대화하려면 순서 의존성을 없애면 된다. 깊이 프리패스(depth pre-pass)는 첫 패스에서 색은 쓰지 않고 깊이만 렌더링해 z-버퍼를 완성한 뒤, 두 번째 셰이딩 패스에서는 이미 채워진 깊이 덕분에 모든 조각이 early-Z 혜택을 받게 하는 두 단계 전략이다. 첫 패스의 추가 비용을 무거운 셰이딩의 오버드로(overdraw) 제거로 벌충하는 거래이며, 이 완성된 깊이 버퍼는 소프트웨어 오클루전이나 화면 공간 효과의 입력으로도 재활용된다.
6. 관련 컬링·안티앨리어싱과의 관계[편집]
깊이 버퍼는 파이프라인 뒤쪽의 최종 심판이라, 앞단의 컬링들과 역할을 나눠 진다. 백페이스 컬링이 안 보일 뒷면을, 오클루전 컬링이 가려진 물체를 미리 걷어내 z-버퍼에 도달하는 조각 수를 줄여 주고, 그렇게 남은 조각들의 최종 겹침을 깊이 버퍼가 픽셀 단위로 정리한다. 한편 안티앨리어싱의 MSAA는 픽셀당 여러 서브표본 위치의 커버리지와 깊이를 저장하는 멀티표본 깊이 버퍼를 요구하므로, 깊이 버퍼는 AA 품질과도 직접 얽힌다. 이 모든 조각이 맞물려 실시간 렌더링의 가시성 파이프라인을 이룬다.
7. 관련 문서[편집]
8. Footnotes[편집]
-
화가 알고리즘은 “먼 것부터 덧칠하면 가까운 게 자연히 위에 온다”는 소박한 발상이지만, 서로를 관통하는 삼각형 앞에서는 어떤 순서로도 정답이 없다. z-버퍼는 “정렬을 포기하고 픽셀마다 따로 판정한다”는 발상의 전환으로 이 난제를 우회했다. 정렬 대신 메모리를 갈아 넣은 셈. ↩
-
Early-Z는 “안 보일 걸 알면 셰이더를 아낀다”는 단순한 아이디어지만, 오버드로가 심한 장면에서 성능을 몇 배로 끌어올린다. 다만 GPU가 알아서 켜주는 암묵적 최적화라, 셰이더에 깊이 쓰기나 discard를 무심코 넣으면 조용히 꺼지고 프레임이 뚝 떨어진다. “왜 갑자기 느려졌지”의 단골 범인. ↩
-
역-Z는 “부동소수점이 0 근처에서 촘촘하다”는 성질과 “원근 투영이 먼 곳을 성기게 만든다”는 성질을 정면으로 충돌시켜 서로 상쇄시키는, 우아하기로 소문난 트릭이다. 근/원 평면 하나 뒤집는 거의 공짜 변경으로 먼 거리 z-파이팅이 사라지니, 안 쓸 이유가 없어졌다. ↩