사전 깊이 테스트
개념

깊이 테스트

gabury1고친 사람 github-actions[bot]

깊이 테스트는 화면의 같은 칸을 두고 다투는 후보 중에서 앞에 있는 것만 남기는 검사입니다. 뒤에 있는 것이 나중에 그려져도 앞엣것을 덮어쓰지 못하게 막습니다. 그래서 그리는 차례를 맞추지 않아도 가까운 물체가 앞에 보입니다.

쉽고 빠른 이해

깊이 테스트는 화면 칸마다 지금까지 가장 가까웠던 거리를 적어 두고, 새 후보가 그보다 가까울 때만 색을 덮어쓰게 하는 검사입니다. 벽 앞에 사람이 서 있는 장면이라면, 벽을 나중에 그려도 사람이 차지한 칸은 벽이 가져가지 못합니다.

이게 없으면 나중에 그린 것이 언제나 앞에 옵니다. 그리는 쪽이 먼 물체부터 차례로 늘어놓아 그려야 하는데, 두 물체가 서로 파고들면 그런 차례가 아예 없습니다.

어떻게 도나:

  1. 화면 칸마다 거리 값을 담는 메모리를 하나 더 둡니다
  2. 후보가 올 때마다 그 후보의 거리와 칸에 적힌 거리를 견줍니다
  3. 후보가 더 가까우면 색과 거리를 함께 갱신하고, 아니면 후보를 버립니다

대가는 메모리와 접근입니다. 화면 칸 수만큼 거리 값을 들고 있어야 하고, 후보마다 읽기와 쓰기가 한 번씩 늘어납니다. 반투명한 물체는 뒤엣것도 비쳐 보여야 해서 이 검사만으로는 안 풀립니다.

상세

이 절은 그리는 차례가 왜 문제가 되는지부터 봅니다. 그다음 거리 값을 담는 메모리와 검사 한 번의 순서를 보고, 마지막으로 이 검사가 잘 안 듣는 두 대목인 깊이 값의 정밀도와 반투명을 짚습니다.

겹쳐 놓은 사진

책상에 사진을 여러 장 겹쳐 놓으면 나중에 놓은 것이 위에 옵니다. 앞뒤를 바꾸려면 사진을 다시 집어 놓는 차례를 고쳐야 합니다.

다만 사진은 한 장이 통째로 위아래가 갈리지만, 화면에서는 점 하나하나가 따로 앞뒤를 가립니다. 사람의 팔이 벽을 가리는 칸과 벽이 그대로 보이는 칸이 한 물체 안에서 섞입니다.

3차원 장면을 화면에 그리는 일도 이런 겹치기입니다. 앞에 있는 것이 뒤에 있는 것을 가리게 만드는 처리를 통틀어 은면 제거라고 부릅니다. 깊이 테스트는 그 처리를 점 단위로 하는 방법입니다.

차례를 맞추는 방법과 그 한계

가장 단순한 방법은 먼 물체부터 차례로 그리는 것입니다. 나중에 그린 것이 앞에 오니, 가까운 것을 나중에 그리면 앞에 있는 것처럼 보입니다. 이 방법을 화가 알고리즘이라고 부릅니다.

이 방법은 차례를 못 정하는 장면에서 무너집니다. 두 삼각형이 서로를 파고들면 어느 쪽이 통째로 앞이라고 말할 수 없습니다. 셋이 고리처럼 서로를 가리는 배치도 만들 수 있습니다.

차례를 억지로 만들려면 삼각형을 잘라 쪼개야 합니다. 물체가 움직일 때마다 다시 잘라야 하고, 그릴 물체가 늘어날수록 견주는 짝의 수가 불어납니다.

깊이 테스트는 이 문제를 물체 단위가 아니라 칸 단위로 옮겨서 풉니다. 물체끼리의 차례를 정하는 대신, 칸마다 누가 더 가까운지만 그때그때 견줍니다.

칸마다 거리를 적어 두는 메모리

화면 한 장을 칸마다 색으로 적어 둔 메모리를 색 버퍼라고 부릅니다. 깊이 테스트는 이것과 같은 크기의 메모리를 하나 더 씁니다. 칸마다 색 대신 거리를 적는 메모리이고, 이것을 깊이 버퍼라고 부릅니다.

여기 적히는 거리는 카메라에서 그 점까지가 얼마나 먼지를 나타내는 값입니다. 값이 작을수록 가깝습니다.

한 장을 그리기 시작할 때 깊이 버퍼는 가장 먼 값으로 채웁니다. 아직 아무것도 안 그린 상태라 어떤 후보가 와도 통과해야 하기 때문입니다.

검사 한 번의 순서

검사를 받는 것은 물체도 삼각형도 아니고, 칸 하나를 차지하겠다고 나선 후보 하나입니다. 이 후보를 프래그먼트라고 부릅니다. 한 칸에 후보가 여럿 올 수 있고, 화면에 남는 것은 하나입니다.

후보 하나가 검사를 통과하는 흐름은 이렇습니다.

flowchart TD
    A["후보 하나 도착 · 깊이 값이 붙어 있다"] --> B["그 칸의 깊이 버퍼 값을 읽는다"]
    B --> C{"후보가 더 가까운가"}
    C -->|아니다| D["후보를 버린다 · 화면은 그대로"]
    C -->|그렇다| E["색 버퍼에 색을 쓴다"]
    E --> F["깊이 버퍼에 새 깊이를 쓴다"]

읽기 한 번과 견주기 한 번, 통과했을 때만 쓰기 두 번입니다. 앞에서 무엇을 그렸는지는 안 봐도 됩니다. 칸에 적힌 값 하나가 지금까지의 결과를 전부 쥐고 있기 때문입니다.

한 칸에 후보 셋이 차례로 왔다고 해 보겠습니다. 아래 표는 그 칸의 깊이 값이 어떻게 바뀌는지를 따라간 것입니다. 시작값은 가장 먼 값을 1.0 으로 둔 것입니다.

그린 차례 후보의 깊이 검사 전 칸의 깊이 판정 검사 뒤 칸의 깊이
첫째 0.8 1.0 통과 0.8
둘째 0.3 0.8 통과 0.3
셋째 0.6 0.3 버림 0.3

셋째 후보는 둘째보다 뒤에 있어서 버려졌습니다. 이 셋을 어떤 차례로 보내도 마지막에 남는 것은 깊이 0.3 인 둘째입니다. 그리는 차례를 안 맞춰도 되는 까닭이 이것입니다.

깊이를 견주는 규칙

"더 가까울 때만 통과"는 기본값일 뿐이고, 견주는 규칙 자체를 골라 바꿀 수 있습니다. 그리려는 것에 따라 무엇을 통과시킬지가 달라지기 때문입니다.

통과시키는 규칙 언제 고르나
후보가 더 가까울 때만 보통의 3차원 장면
후보가 더 가깝거나 같을 때 같은 면을 두 번 그려 위에 덧칠할 때
언제나 통과 앞뒤를 안 가리고 마지막에 그린 것을 남길 때
언제나 버림 색은 안 남기고 다른 검사만 쓰려 할 때

표의 둘째 줄이 필요한 까닭을 하나 들어 보겠습니다. 이미 그린 벽에 낙서를 덧그리려면 낙서가 벽과 같은 깊이로 옵니다. "더 가까울 때만" 규칙이면 낙서가 전부 버려집니다.

검사와 쓰기는 따로 끄고 켭니다. 검사는 하되 깊이 버퍼에 안 쓰게 두면, 그 물체는 앞엣것에 가려지기는 하되 뒤에 오는 물체를 가리지는 않습니다. 반투명한 물체를 그릴 때 쓰는 설정입니다.

깊이 값이 고르게 놓이지 않는 까닭

깊이 버퍼에 적히는 값은 카메라까지의 거리를 그대로 옮긴 것이 아닙니다. 멀리 있는 것이 작게 보이도록 하는 원근 투영 변환을 거치면서, 거리의 역수에 가까운 꼴로 바뀝니다.

그래서 깊이 버퍼가 가진 비트 수가 가까운 쪽에 몰려 쓰입니다. 가까운 곳은 촘촘하게 나뉘고 먼 곳은 성깁니다. 이렇게 나눌 수 있는 깊이의 촘촘함을 깊이 정밀도라고 부릅니다.

먼 곳에서 두 면이 조금 떨어져 있어도 같은 값으로 뭉개질 수 있습니다. 그러면 어느 쪽이 앞인지가 칸마다 다르게 갈려서 화면에 얼룩덜룩한 무늬가 생깁니다. 이 현상을 Z 파이팅이라고 부릅니다.

손잡이는 근평면입니다. 카메라에서 얼마나 가까운 데부터 그리기 시작할지를 정하는 값인데, 이것을 너무 작게 잡으면 정밀도가 가까운 쪽으로 쏠립니다. 멀리까지 그려야 하는 장면이면 근평면을 뒤로 물리는 쪽이 먼 곳의 뭉개짐을 줄입니다.

셰이더와 검사의 앞뒤

깊이 테스트는 칸의 색을 정한 뒤에 옵니다. 색을 정하는 쪽은 프래그먼트 셰이더라는 프로그램이고, 후보 하나마다 한 번씩 돕니다.

flowchart TD
    R["래스터화 · 도형이 덮은 칸을 고른다"] --> S["프래그먼트 셰이더 · 칸마다 색을 정한다"]
    S --> D["깊이 테스트 · 앞엣것만 남긴다"]
    D --> B["블렌딩 · 남은 색을 섞는다"]
    B --> F["프레임버퍼 · 화면 메모리에 쓴다"]

검사가 뒤에 있다는 것은 버려질 색도 일단 계산한다는 뜻입니다. 가려질 물체가 화면을 가득 덮고 있으면 그 계산이 통째로 헛돕니다.

그래서 GPU(Graphics Processing Unit, 그래픽 처리 장치)는 프래그먼트 셰이더를 돌리기 전에 깊이를 먼저 견주기도 합니다. 이 앞당기기를 얼리 Z라고 부릅니다. 가려질 후보를 미리 버리면 그 후보의 색은 아예 계산하지 않습니다.

앞당기기를 못 쓰는 때도 있습니다. 프래그먼트 셰이더가 깊이 값을 직접 고쳐 쓰거나 후보를 스스로 버릴 수 있으면, 그 프로그램이 돌기 전에는 후보가 어떤 깊이로 남을지 알 수 없습니다.

반투명이 이 검사로 안 풀리는 까닭

이 검사는 한 칸에 하나만 남깁니다. 유리창 너머로 뒤가 비쳐 보이려면 뒤엣것의 색도 살아 있어야 합니다. 남은 색과 이미 쓰여 있던 색을 섞는 처리가 따로 있고, 이것을 블렌딩이라고 부릅니다.

깊이 테스트는 섞을 재료가 생기기 전에 한쪽을 버립니다. 반투명한 물체를 아무 차례로나 그리면, 먼저 그린 쪽이 남고 뒤에 온 쪽이 버려지는 일이 칸마다 뒤죽박죽 일어납니다.

그래서 반투명한 물체는 이렇게 그립니다. 불투명한 물체를 먼저 다 그려 깊이 버퍼를 채우고, 반투명한 물체는 깊이 쓰기를 끈 채 먼 것부터 차례로 그려 섞습니다.

차례를 다시 맞춰야 한다는 점에서, 앞 절에서 본 화가 알고리즘의 문제가 반투명에는 그대로 남아 있습니다. 깊이 테스트가 없애 준 것은 불투명한 물체의 차례뿐입니다.

관련 항목

깊이 테스트가 놓이는 처리 단계

그래픽스 파이프라인 · 래스터화 · 프래그먼트 셰이더 · 프래그먼트별 테스트 · 출력 병합기 · 프래그먼트

깊이 테스트와 나란히 도는 다른 검사

스텐실 테스트 · 알파 테스트 · 시저 테스트 · 깊이 경계 테스트 · 컬링

깊이 테스트가 읽고 쓰는 저장소

깊이 버퍼 · 색 버퍼 · 스텐실 버퍼 · 프레임버퍼 · 렌더 타깃 · 깊이 텍스처

깊이 값을 만들어 내는 좌표 변환

원근 투영 · 근평면 · 원평면 · 뷰 절두체 · 정규화 장치 좌표 · 직교 투영

깊이 테스트에서 비롯되는 화면 결함

Z 파이팅 · 깊이 정밀도 · 뎁스 바이어스 · 폴리곤 오프셋 · 깊이 클램프

깊이 테스트를 앞당기거나 건너뛰는 최적화

얼리 Z · 계층적 Z · Z 프리패스 · 오버드로 · 오클루전 컬링 · 디퍼드 셰이딩

깊이 테스트로는 못 푸는 반투명 처리

블렌딩 · 알파 블렌딩 · 알파 채널 · 순서 독립 투명 · 깊이 쓰기

깊이 테스트 대신 앞뒤를 가리는 방법

화가 알고리즘 · Z 버퍼 알고리즘 · 레이 트레이싱 · BSP 트리 · 깊이 정렬

깊이 테스트를 제공하는 그래픽 API

OpenGL · WebGL · Vulkan · Direct3D · Metal · WebGPU

깊이 테스트가 속하는 상위 분류

그래픽스 · 렌더링 · 실시간 렌더링 · 은면 제거 · 래스터 그래픽스 · GPU

다른 이름: depth test · 뎁스 테스트 · Z 테스트 · z-test