이득우의 게임 수학
다수의 게임 오브젝트를 그리는 경우, 카메라로부터 멀리 떨어진 게임 오브젝트를 먼저 그린 후 가까운 오브젝트를 그리도록 해야 원근감이 올바르게 표현횐다.
하지만 같은 거리에 두 게임 오브젝트가 서로 겹쳐 있다면, 물체 단위로 순서를 조절하는 것으로는 문제를 해결할 수 없다.
따라서 근본적인 해결 방법은 게임 오브젝트 단위가 아닌, 삼각형의 픽셀 단위로 깊이를 비교해 가까운 곳에 있는 픽셀만 그리는 것이다.
- 이를 위해 화면의 픽셀마다 현재의 깊이 값을 보관해주는 별도의 저장 공간이 필요한데, 이를 깊이 버퍼 혹은 Z-버퍼(Z-buffer)라고 한다.
최종 픽셀을 찍는 과정에서 현재 깊이 값을 버퍼에 저장된 값과 비교해, 현재 깊이 값이 작은 경우에만 픽셀을 찍도록 구성한다.
그러면 물체에 가려져 그리지 않아도 되는 픽셀을 파악해 그리기를 건너뛸 수 있고, 이 작업을 깊이 테스팅(Depth testing)이라고 한다.
깊이 테스팅은 깊이의 대소만 비교하면 되기 때문에, [-1, 1] 범위로 매핑된 NDC의 z값을 그대로 사용해도 된다.
삼각형 폴리곤을 구성하는 세 점의 깊이 값 z₁, z₂, z₃로부터 계산된 각 픽셀의 무게 중심 좌표 q₁, q₂, q₃를 사용해 픽셀의 깊이 값 z'을 구하는 수식은 다음과 같다.
// 책의 예제인 CK소프트렌더러에서 삼각형을 그리는 함수
void SoftRenderer::DrawTriangle3D(std::vector<Vertex3D>& InVertices, const LinearColor& InColor, FillMode InFillMode)
{
...
// 깊이 버퍼 테스팅
if (toggleDepthTesting)
{
float newDepth = InVertices[0].Position.Z * oneMinusST + InVertices[1].Position.Z * s + InVertices[2].Position.Z * t;
float prevDepth = r.GetDepthBufferValue(fragment);
if (newDepth < prevDepth)
{
// 픽셀을 처리하기 전 깊이 값을 버퍼에 보관
r.SetDepthBufferValue(fragment, newDepth);
}
else
{
// 이미 앞에 무언가 그려져 있으므로 픽셀 그리기 생략
continue;
}
}
}