사전 깊이 경계 테스트
개념

깊이 경계 테스트

gabury1고친 사람 github-actions[bot]

깊이 경계 테스트는 화면 칸마다 이미 그려진 물체의 깊이가 미리 정한 범위 안일 때만 그 칸에 새로 그리게 하는 검사입니다. 깊이는 그 물체가 카메라에서 얼마나 먼지를 나타내는 값입니다. 범위 밖 칸은 색을 계산하기 전에 건너뜁니다. 조명처럼 일정한 거리 안에만 영향을 주는 효과를 그릴 때 헛계산을 덜어 줍니다.

쉽고 빠른 이해

깊이 경계 테스트는 화면 칸마다 이미 적혀 있는 깊이 값을 봅니다. 깊이 값은 그 칸에 보이는 물체가 카메라에서 얼마나 먼지를 나타내는 수입니다. 이 값이 정해 둔 범위 안인 칸에만 그리게 합니다. 방 안에 전구 하나를 켠다면 불빛이 닿는 거리 안에 물체가 있는 칸만 밝히면 됩니다.

이게 없으면 전구 빛이 화면에서 덮는 칸을 전부 계산해야 합니다. 그 칸 가운데 상당수는 전구보다 한참 뒤의 벽이나 한참 앞의 기둥이라 빛이 안 닿아도 계산은 돕니다.

어떻게 도나:

  1. 장면을 먼저 그려 칸마다 가장 가까운 물체의 깊이 값을 적어 둡니다
  2. 빛이 닿는 공간의 가까운 끝과 먼 끝을 깊이 값으로 바꿔 범위로 정합니다
  3. 조명을 그릴 때 칸에 적힌 깊이 값이 범위 밖이면 그 칸은 건너뜁니다

대가는 범위를 따로 구해야 한다는 것입니다. 앞뒤 깊이 하나로만 거르므로 범위 안이어도 빛이 안 닿는 칸은 남습니다. 모든 그래픽 환경이 이 검사를 갖추지도 않습니다.

상세

3층부터 5층까지만 맡은 배달원은 짐을 꾸리기 전에 주소의 층수부터 봅니다. 그 사이가 아닌 주소는 짐을 꾸리지도 않고 넘깁니다. 무엇을 배달할지는 따지지 않습니다. 이미 적혀 있는 주소 하나로 갈지 말지를 정합니다.

이 절은 이 검사가 무엇을 읽는지, 깊이 테스트와 어디서 갈리는지를 봅니다. 쓰임은 조명 계산에서 칸 셋을 따라가며 보입니다.

칸마다 적혀 있는 깊이

3차원 장면을 그리는 GPU(Graphics Processing Unit, 그래픽 처리 장치)는 화면 칸마다 그 칸에 보이는 물체가 카메라에서 얼마나 먼지를 수 하나로 적어 둡니다. 이 수를 깊이 값이라고 부릅니다. 흔히 가장 가까운 쪽을 0, 가장 먼 쪽을 1 로 둡니다.

칸마다 깊이 값을 담아 두는 메모리가 깊이 버퍼입니다. 물체를 하나 그릴 때마다 가려진 칸은 두고 앞에 온 칸만 새 값으로 바뀝니다. 장면을 다 그리고 나면 칸마다 가장 가까운 물체의 깊이가 남습니다.

새로 그리는 물체가 칸 하나를 차지하겠다고 나선 것을 프래그먼트라고 부릅니다. 칸 하나를 두고 온 후보 하나라고 보면 됩니다. 후보는 칸에 색을 쓰기 전에 몇 가지 검사를 거칩니다.

이미 있는 깊이를 범위와 견주는 검사

깊이 경계 테스트는 그 검사 가운데 하나입니다. 후보가 온 칸의 깊이 버퍼 값을 읽어, 그 값이 미리 정한 최솟값과 최댓값 사이에 드는지 봅니다. 사이에 들면 후보를 통과시킵니다. 벗어나면 버립니다.

눈여겨볼 것은 견주는 값입니다. 후보 자신의 깊이는 쓰지 않습니다. 칸에 이미 적힌 깊이만 봅니다. 그래서 이 검사는 새 물체가 어디 있느냐가 아니라 그 칸에 이미 그려진 물체가 어디쯤 있느냐를 묻습니다.

이 검사는 깊이 버퍼에 아무것도 쓰지 않습니다. 통과한 후보는 다음 검사로 넘어갑니다. 버려진 후보는 그 칸에 흔적을 남기지 않습니다.

검사를 끄면 모든 후보가 통과합니다. 범위를 0 에서 1 까지 전부로 잡아도 마찬가지입니다.

깊이 테스트와 갈리는 대목

이름이 닮은 깊이 테스트와 헷갈리기 쉽습니다. 깊이 테스트는 새 후보가 이미 그려진 것보다 앞에 있을 때만 그 칸을 덮어쓰게 하는 검사입니다. 두 검사가 무엇을 읽고 무엇을 묻는지 나란히 놓으면 이렇습니다.

깊이 테스트 깊이 경계 테스트
읽는 값 후보의 깊이와 칸의 깊이 칸의 깊이
견주는 상대 둘을 서로 견준다 미리 정한 최솟값과 최댓값
깊이 버퍼에 쓰나 통과하면 새 깊이를 쓸 수 있다 쓰지 않는다
묻는 것 새 물체가 더 앞인가 이미 있는 물체가 범위 안인가

표의 첫째 줄과 셋째 줄이 쓰임을 가릅니다. 깊이 경계 테스트는 칸에 이미 적힌 깊이를 읽으니, 장면을 먼저 다 그려 둬야 판정에 쓸 값이 생깁니다. 또 깊이 버퍼에 쓰지 않으니, 그 위에 효과를 덧그려도 장면의 깊이가 망가지지 않습니다.

두 검사는 따로 켜고 끌 수 있습니다. 함께 켜면 둘 다 통과한 후보만 색을 남깁니다.

조명 계산에서 헛도는 칸

장면의 모양을 먼저 다 그려 두고 조명은 나중에 칸마다 계산하는 방식을 디퍼드 셰이딩이라고 부릅니다. 첫 단계에서 칸마다 깊이와 표면 방향, 색 같은 정보를 적어 둡니다. 둘째 단계에서 광원마다 그 정보를 읽어 밝기를 더합니다.

전구 같은 광원은 빛이 닿는 거리가 정해져 있습니다. 그래서 전구를 가운데 둔 공 모양 안에만 빛이 닿는다고 보고 그 공 안만 계산합니다. 이 공을 광원 볼륨이라고 부릅니다.

문제는 공이 화면에서 덮는 칸과 빛이 실제로 닿는 칸이 다르다는 것입니다. 화면은 앞뒤를 눌러 평면으로 만든 그림이라서, 공 뒤 멀리 있는 벽도 공 앞을 지나는 기둥도 같은 칸에 겹쳐 보입니다. 이런 칸에도 조명 계산은 돌지만 더해지는 밝기는 0 입니다.

깊이 경계 테스트가 이런 칸을 거릅니다. 공의 가장 가까운 끝과 가장 먼 끝을 깊이 값으로 바꿔 최솟값과 최댓값에 둡니다. 그러면 칸에 적힌 물체가 공보다 앞이거나 뒤인 칸은 조명 계산 전에 버려집니다.

flowchart TD
    A["장면을 먼저 그린다 · 깊이 버퍼가 채워진다"] --> B["광원 하나를 고른다"]
    B --> C["공의 가까운 끝과 먼 끝을 깊이 값으로 바꾼다"]
    C --> D["둘을 최솟값과 최댓값으로 둔다"]
    D --> E{"공이 덮는 칸마다 · 칸의 깊이가 범위 안인가"}
    E -->|아니다| F["건너뛴다"]
    E -->|그렇다| G["조명을 계산해 밝기를 더한다"]

공이 깊이 0.4 에서 0.6 사이를 차지한다고 해 보겠습니다. 공이 화면에서 덮는 칸 가운데 셋을 골라 따라가면 이렇습니다.

칸 칸에 그려져 있는 물체 칸의 깊이 판정
첫째 공 뒤 멀리 있는 벽 0.9 버림
둘째 공 안에 놓인 탁자 0.5 통과 · 조명 계산
셋째 공 앞을 지나는 기둥 0.2 버림

셋 가운데 빛이 닿는 것은 둘째뿐입니다. 검사도 둘째만 통과시킵니다. 첫째와 셋째는 화면에서 공과 겹쳐 보였을 뿐 빛은 안 닿는 물체입니다.

화면 위 가로세로 범위는 다른 검사로 자릅니다. 화면에 정한 사각형 밖으로 색이 못 나가게 하는 검사를 시저 테스트라고 부릅니다. 시저 테스트는 공이 덮는 사각형 밖을 자릅니다. 깊이 경계 테스트는 공보다 앞이나 뒤를 자릅니다. 둘을 함께 쓰면 공을 감싸는 상자 하나 밖의 칸이 모두 걸러집니다.

그림자 볼륨과 하늘 칸

같은 생각을 그림자에도 씁니다. 물체가 빛을 가리면 그 뒤에 그늘진 공간이 생깁니다. 이 공간을 도형으로 만들어 그린 뒤 그 안에 든 칸을 그림자로 치는 방법을 그림자 볼륨이라고 부릅니다.

그늘 도형은 광원에서 멀어지는 쪽으로 길게 늘어나 화면을 넓게 덮기 쉽습니다. 광원의 빛이 닿는 깊이 범위로 검사를 걸어 두면 그 밖의 칸은 그림자 계산에서 빠집니다. 빛이 안 닿는 칸은 그림자인지 아닌지 가릴 필요가 없기 때문입니다.

하늘처럼 아무 물체도 안 그린 칸에는 깊이 버퍼를 처음 채울 때 넣은 가장 먼 값이 남아 있습니다. 최댓값을 그보다 조금 작게 두면, 화면 전체에 효과를 덧그릴 때 이런 빈 칸을 건너뜁니다.

셰이더보다 먼저 판정하는 까닭

후보의 색은 프래그먼트 셰이더라는 프로그램이 후보마다 한 번씩 돌며 정합니다. 조명 계산도 이 프로그램 안에서 돕니다. 버릴 후보를 이 프로그램이 돌기 전에 가려내야 계산을 아낄 수 있습니다. 아래에서는 이 프로그램을 줄여 셰이더라고 부릅니다.

깊이 경계 테스트는 셰이더보다 먼저 돌릴 수 있습니다. 판정에 쓰는 값이 칸에 이미 적혀 있으니, 후보가 셰이더를 거치기 전에도 답이 나옵니다.

셰이더는 색만 정하는 것이 아닙니다. 후보의 깊이 값도 바꿔 적을 수 있습니다. 깊이 경계 테스트는 후보의 깊이를 안 보니, 셰이더가 후보의 깊이를 고쳐 써도 판정은 바뀌지 않습니다.

깊이 테스트는 사정이 다릅니다. 셰이더를 돌리기 전에 깊이 테스트를 먼저 해 버리는 최적화를 얼리 Z라고 합니다. 가려질 후보를 셰이더 계산 없이 버려 계산을 아낍니다.

얼리 Z 는 셰이더가 후보의 깊이를 고쳐 쓰면 못 씁니다. 깊이 테스트는 후보의 깊이를 읽는데, 그 값이 셰이더를 거쳐야 정해지니 미리 견줄 수가 없습니다.

범위를 적는 값

깊이 값은 카메라에서 떨어진 거리를 나타내지만 거리 그 자체는 아닙니다. 거리를 변환해 0 에서 1 사이로 옮긴 수입니다. 최솟값과 최댓값도 이 깊이 값으로 줍니다. 광원 볼륨의 가까운 끝과 먼 끝은 거리로 알고 있으니, 장면을 그릴 때와 같은 변환을 거쳐 깊이 값으로 바꿔 둡니다.

이 변환은 거리를 고르게 옮기지 않습니다. 멀리 있는 것이 작게 보이게 하는 원근 투영을 거치면 가까운 쪽 거리는 깊이 값에서 넓게 퍼집니다. 먼 쪽 거리는 좁게 몰립니다.

그릴 수 있는 가장 가까운 거리를 1, 가장 먼 거리를 100 으로 잡아 보겠습니다. 이때 거리 50 에 있는 물체의 깊이 값은 약 0.99 입니다. 거리로는 한가운데인데 깊이 값으로는 먼 끝에 바짝 붙어 있습니다.

그러니 공의 가운데 거리만 깊이 값으로 바꾼 뒤 양쪽으로 같은 폭을 더하고 빼는 식은 틀립니다. 거리에서 같은 폭이 깊이 값에서는 가까운 쪽이 넓고 먼 쪽이 좁습니다. 가까운 끝과 먼 끝을 각각 깊이 값으로 바꿔야 합니다.

이 검사가 못 거르는 칸

깊이 경계 테스트는 깊이 범위 하나로만 거릅니다. 공을 감싸는 상자 안에 들었어도 상자의 모서리 쪽 칸은 공 밖이라 빛이 안 닿습니다. 그런 칸은 검사를 통과해 조명 계산을 받습니다. 결과만 0 이 됩니다.

모든 그래픽 환경이 이 검사를 갖추지는 않습니다. 갖춘 쪽에서도 하드웨어가 지원하는지 먼저 확인하고 켜는 선택 기능인 경우가 많습니다.

이 검사가 없으면 셰이더 안에서 깊이 버퍼 값을 읽어 직접 버리는 식으로 흉내 냅니다. 이때는 셰이더가 이미 돌기 시작한 뒤라서, 아낄 수 있는 계산이 그만큼 줄어듭니다.

관련 항목

깊이 경계 테스트와 나란히 도는 프래그먼트 검사

깊이 테스트 · 스텐실 테스트 · 시저 테스트 · 알파 테스트 · 프래그먼트별 테스트

깊이 경계 테스트가 읽는 깊이 값의 저장소

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

깊이 경계 테스트로 계산을 줄이는 렌더링 기법

디퍼드 셰이딩 · 광원 볼륨 · G 버퍼 · 점광원 · 그림자 볼륨 · 스텐실 그림자 · 타일 기반 디퍼드 셰이딩

셰이더 계산 전에 후보를 버리는 다른 최적화

얼리 Z · 계층적 Z · Z 프리패스 · 오클루전 컬링 · 컬링

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

원근 투영 · 근평면 · 원평면 · 깊이 정밀도 · 정규화 장치 좌표

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

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

깊이 경계 테스트를 켜고 끄는 그래픽 API(Application Programming Interface, 응용 프로그램 인터페이스)

OpenGL · Vulkan · Direct3D · OpenGL 확장

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

그래픽스 · 렌더링 · 실시간 렌더링 · GPU · 그래픽 API

다른 이름: depth bounds test · 뎁스 바운즈 테스트 · 깊이 범위 테스트