이번 문서의 목표: Z-buffer 알고리즘이 픽셀마다 깊이값을 어떻게 비교·갱신하는지 표로 직접 채울 수 있고, 법선 벡터와 시선 벡터의 내적 부호로 후면(back-face)을 판정할 수 있으며, painter’s algorithm·BSP 트리·스캔라인 은면 제거의 차이를 설명할 수 있게 된다.
왜 “보이는 면만 그리기”가 어려운 문제인가
08편에서 3차원 물체를 카메라가 보는 2차원 화면으로 투영(projection)하는 방법을 배웠습니다. 하지만 투영만으로는 문제가 끝나지 않습니다. 상자 안쪽 면처럼 카메라에서 보이지 않아야 할 면도 투영 계산 자체는 문제없이 화면 좌표를 얻기 때문에, 그대로 그리면 뒤에 가려져야 할 면이 앞면을 뚫고 나와 보이는 이상한 그림이 됩니다.
은면 제거(Hidden Surface Removal, 가시표면 판별이라고도 함)는 여러 개의 면(폴리곤) 중에서 카메라에서 실제로 보이는 면만 화면에 그리고, 다른 면에 가려진 부분은 그리지 않도록 걸러내는 과정입니다. 이 편에서는 그중 가장 널리 쓰이는 Z-buffer(깊이 버퍼) 알고리즘과, 계산량을 줄이기 위한 사전 필터링 기법인 후면 제거(Back-Face Culling)를 실제 숫자로 계산해 보고, 그 밖의 대표 기법인 painter’s algorithm·BSP 트리·스캔라인 방식과 비교합니다.
쉽게 말하면: 은면 제거는 카메라 쪽에서 봤을 때 다른 물체에 가려서 안 보여야 하는 면을 실제로 화면에 그리지 않도록 걸러내는 작업입니다.
1. Z-buffer(깊이 버퍼) 알고리즘
쉽게 말하면: 화면의 각 픽셀마다 “지금까지 그려진 것 중 카메라에 가장 가까운 거리”를 따로 기억해 두고, 새로 그리려는 점이 그보다 더 가까울 때만 화면을 갱신하는 방법입니다.
Z-buffer는 색상을 저장하는 프레임버퍼(01편에서 다룬 화면 픽셀 배열)와 똑같은 크기의 보조 배열을 하나 더 두고, 여기에 색상 대신 각 픽셀에서 카메라로부터의 깊이값(z값, 카메라 좌표계 기준 거리)을 저장합니다. 이 보조 배열이 바로 깊이 버퍼(Depth Buffer)입니다.
알고리즘은 다음 순서로 동작합니다.
- 깊이 버퍼의 모든 칸을 “가장 먼 값”(보통 무한대, )으로 초기화하고, 색상 버퍼는 배경색으로 초기화한다.
- 장면에 있는 폴리곤을 순서에 상관없이 하나씩 꺼낸다.
- 그 폴리곤이 덮는 각 픽셀에 대해, 그 폴리곤의 해당 픽셀에서의 깊이값 를 계산한다.
- 깊이 버퍼에 저장된 현재 값 와 비교해, 이면(카메라에 더 가까우면) 색상 버퍼와 깊이 버퍼를 모두 새 값으로 갱신한다. 그렇지 않으면 아무것도 하지 않는다.
- 모든 폴리곤의 모든 픽셀을 처리할 때까지 2~4를 반복한다.
- : 지금 그리려는 폴리곤이 특정 픽셀에서 갖는 깊이값(카메라에서 가까울수록 작은 값이라고 정한다)
- : 그 픽셀에 지금까지 기록된, 가장 가까웠던 깊이값
실제 계산 — 픽셀별 깊이 비교표 채우기
화면의 한 가로줄에서 픽셀 5개(P1~P5)를 겹치는 폴리곤 A(삼각형, 카메라에서 먼 배경)와 폴리곤 B(사각형, 카메라에서 가까운 물체)가 아래 표의 깊이값으로 덮는다고 하겠습니다. 폴리곤이 그 픽셀을 덮지 않으면 -로 표시합니다. 처리 순서는 A를 먼저, B를 나중에 그린다고 가정합니다.
| 픽셀 | 배경(초기 깊이 버퍼) | 폴리곤 A의 깊이 | A 처리 후 깊이 버퍼 | 폴리곤 B의 깊이 | B 처리 후 깊이 버퍼 | 최종 화면에 남는 폴리곤 |
|---|---|---|---|---|---|---|
| P1 | 8 | 8 (A) | – | 8 (A) | A | |
| P2 | 8 | 8 (A) | 4 | 4 (B) | B | |
| P3 | 8 | 8 (A) | 4 | 4 (B) | B | |
| P4 | – | (배경) | 4 | 4 (B) | B | |
| P5 | – | (배경) | – | (배경) | 배경 |
P2를 예로 계산 과정을 풀어 보면 다음과 같습니다.
- 결과: P2는 A(깊이 8)보다 B(깊이 4)가 카메라에 더 가까우므로, 나중에 B를 그리면서 깊이 버퍼가 8에서 4로 갱신되고 화면에는 B의 색이 남는다.
여기서 중요한 성질은, 폴리곤을 A, B 순서로 그리든 B, A 순서로 그리든 최종 결과가 같다는 것입니다. B를 먼저 그리면 P2의 깊이 버퍼는 4가 되고, 이어서 A를 그릴 때 은 보다 크므로 갱신되지 않아 결국 B가 그대로 남습니다. 이렇게 그리는 순서와 무관하게 항상 올바른 결과를 내는 것이 Z-buffer의 가장 큰 장점이며, 뒤에서 다룰 painter’s algorithm과의 결정적인 차이입니다.
자주 틀리는 점: 깊이값이 클수록 가까운 것으로 착각하는 경우가 많습니다. 이 문서(그리고 대부분의 교재)는 카메라 좌표계에서 값이 작을수록 카메라에 가깝다는 관례를 사용합니다. 문제에서 관례를 다르게 정의하면(예: 값이 클수록 가까움) 부등호 방향이 반대로 바뀌므로, 항상 문제에 주어진 정의를 먼저 확인해야 합니다.
2. 후면 제거(Back-Face Culling) — 계산량을 줄이는 사전 필터
쉽게 말하면: 상자나 구처럼 속이 막힌 물체는 카메라 반대쪽을 향한 면이 화면에 아예 안 보이므로, Z-buffer 계산을 하기도 전에 그런 면을 미리 걸러내는 것입니다.
Z-buffer는 정확하지만 모든 폴리곤의 모든 픽셀에 대해 깊이 비교를 해야 하므로 계산량이 많습니다. 속이 막힌(convex, 볼록한) 물체라면, 카메라 반대 방향을 향한 면은 어차피 다른 면에 완전히 가려져 절대 보이지 않는다는 사실을 미리 알 수 있습니다. 이런 면을 Z-buffer 계산에 넣기 전에 걸러내는 것이 후면 제거입니다.
판정 방법은 폴리곤의 법선 벡터(Normal Vector, 면에 수직으로 뻗은 벡터)와 시선 벡터(View Vector, 면에서 카메라를 향하는 벡터)의 내적(Dot Product)을 이용합니다.
- : 폴리곤 표면에 수직인 법선 벡터(관례상 물체 바깥쪽을 향하도록 정의)
- : 폴리곤 위의 한 점에서 카메라(시점)를 향하는 시선 벡터
- 내적 는 두 벡터 사이 각도 에 대해 와 같으므로, 두 벡터가 도보다 좁은 각을 이루면(같은 방향에 가까우면) 양수, 도보다 넓은 각을 이루면(반대 방향에 가까우면) 음수가 된다.
실제 계산 — 법선·시선 벡터의 내적으로 판정하기
삼각형의 세 꼭짓점이 , , 이고, 카메라(시점)가 에 있다고 하겠습니다. 먼저 법선 벡터를 구하기 위해 두 변 벡터를 만듭니다.
두 변 벡터의 외적(Cross Product)이 법선 벡터입니다.
- 외적 공식 을 그대로 대입한 결과다.
이제 삼각형 위의 점 에서 카메라 를 향하는 시선 벡터를 구합니다.
두 벡터의 내적을 계산합니다.
- 결과: 내적이 이므로 이 삼각형은 전면이다. 법선이 카메라와 같은 쪽(양의 방향)을 향하고 있으므로, 실제로 카메라에서 이 면의 앞쪽이 보인다는 뜻과 일치한다.
같은 삼각형이지만 꼭짓점 순서를 반대로( 순서) 정의하면 법선이 반대 방향이 됩니다.
- 결과: 내적이 이므로 이번에는 후면으로 판정되어 제거된다. 이 예는 같은 위치의 같은 삼각형이라도 꼭짓점을 나열하는 순서(winding order, 정점 감김 순서)에 따라 법선의 방향이 뒤집히고, 그 결과 전면·후면 판정도 뒤집힌다는 것을 보여준다.
자주 틀리는 점: 후면 제거는 Z-buffer를 대체하는 것이 아니라 Z-buffer 계산 전에 부담을 줄이는 사전 단계입니다. 볼록하지 않은(오목한, concave) 물체나 여러 물체가 서로를 가리는 장면에서는 후면 제거만으로 은면 문제를 다 해결할 수 없고, 결국 Z-buffer 같은 정밀한 가시성 판별이 함께 필요합니다.
3. 그 밖의 은면 제거 기법 비교
| 기법 | 핵심 아이디어 | 장점 | 단점 |
|---|---|---|---|
| Z-buffer | 픽셀마다 깊이값을 저장해 더 가까운 값으로만 갱신 | 폴리곤 처리 순서와 무관하게 항상 정확, 구현이 단순해 하드웨어(GPU) 가속에 적합 | 폴리곤 수·해상도에 비례해 깊이 버퍼용 메모리와 비교 연산이 늘어난다 |
| Painter’s algorithm(화가 알고리즘) | 폴리곤을 카메라에서 먼 것부터 가까운 순서로 정렬해 뒤에서부터 덧칠하듯 그린다 | 깊이 버퍼 없이 정렬만으로 동작해 개념이 단순하다 | 폴리곤끼리 서로 겹쳐 자르거나(intersect) 순환 가림(A가 B를 가리고 B가 C를 가리고 C가 다시 A를 가리는 경우)이 있으면 올바른 정렬 자체가 불가능하다 |
| BSP 트리(Binary Space Partitioning Tree, 이진 공간 분할 트리) | 장면을 평면으로 계속 반으로 나누어 트리를 만들어 두고, 카메라 위치에 따라 트리를 순회하는 순서만 바꿔 항상 올바른 뒤에서 앞 순서를 얻는다 | 트리를 한 번만 만들어 두면(전처리) 카메라가 움직여도 매번 정렬할 필요 없이 순회 순서만 결정하면 된다 | 트리를 만드는 전처리 비용이 크고, 물체가 움직이는(동적인) 장면에서는 트리를 다시 만들어야 한다 |
| 스캔라인 은면 제거(Scanline Hidden Surface) | 화면을 가로줄(scanline) 단위로 처리하면서, 그 줄과 교차하는 폴리곤들 중 각 구간에서 가장 가까운 폴리곤만 채운다 | 한 줄 단위로만 깊이 정보를 유지하면 되어 전체 화면 크기의 깊이 버퍼가 필요 없다 | 폴리곤과 스캔라인의 교차 구간을 매번 계산·정렬해야 해 구현이 복잡하다 |
핵심 정리
- Z-buffer는 픽셀마다 깊이값을 저장해 두고, 새 폴리곤의 깊이가 더 작을(가까울) 때만 색상과 깊이를 갱신하며, 이 방식은 폴리곤 처리 순서와 무관하게 항상 정확한 결과를 준다.
- 후면 제거는 법선 벡터와 시선 벡터의 내적 부호로 판정한다 — 내적이 양수면 전면, 0 이하면 후면으로 제거한다.
- 법선 벡터의 방향은 꼭짓점을 나열하는 순서(winding order)에 따라 정해지며, 순서가 바뀌면 전면·후면 판정도 뒤바뀐다.
- 후면 제거는 Z-buffer를 대체하지 못하며, 계산 부담을 줄이는 사전 필터 역할만 한다.
- Painter’s algorithm은 정렬만으로 동작하지만 순환 가림·교차 폴리곤에서 실패할 수 있고, BSP 트리는 전처리로 이 문제를 해결하며, 스캔라인 방식은 한 줄 단위로 깊이 정보를 유지한다.