0. 서론

회사에서 UI Batching System을 구현하고 있었다. 그 중 UI Mesh를 묶어내는 코드를 작성하고 있었는데, 불현듯 한 가지 걱정이 생겼다: 만약 묶인 메시의 프리미티브가 서로 겹치면, (UI는 z값이 모두 같으므로) 픽셀 쉐이더에서 Z-Fighting이 일어나게 되지 않을까? 다음과 같은 그림을 상상해 보자.

정점 버퍼에 붉은색 사각형 메쉬가 먼저 들어간 후 파란색 사각형 메쉬가 들어갔다. 둘의 정점 색상이 같다면 어떤 픽셀이 프레임버퍼에 쓰여지든 상관이 없지만 그렇지 않거나 텍스쳐링이 들어가게 되면 예상한 대로 그려지지 않을 수 있다.

내 머릿속의 그래픽스 지식으로는 픽셀 쉐이더는 워프 단위로 Parallel하게 수행되기 때문에 어떤 렌더링 결과가 나타날 지 알 수 없다는 결론에 이르렀다. 그래서 이걸 어떻게 해결해야 할 지 많은 고민을 하고 회사 선임님께도 여쭤보았는데, 결론부터 말하자면 신경 쓸 필요 없었다.

1. 왜 신경 쓸 필요가 없는가?

내가 알고 있던 것 처럼, 픽셀 쉐이더(뿐만 아니라 모든 쉐이더)는 최신 GPU 아키텍처에서 병렬적으로 수행된다. 그러나, 픽셀 쉐이더를 연산하는 것과 그 연산 결과로 나타난 색상값을 프레임버퍼에 실제로 쓰는 것은 다른 과정이다.

프레임버퍼에 색상이 쓰여질 때에는 렌더링 파이프라인에 들어온 프리미티브 순서에 맞게 쓰여지게 된다고 한다. 그러므로 위의 예시에서 빨간색 사각형 메쉬가 정점 버퍼에 모두 들어간 후 파란색 사각형 메쉬가 정점 버퍼에 들어온 구조라면, 왼쪽 그림대로 렌더링 될 것이다.

2. 참고: 문제 해결 과정 중 알게 된 사실

사실 나는 한 드로우콜의 같은 메쉬들 간에 Z-Fighting이 일어나지 않는 이유가 ‘Provoking Vertex’ 옵션 때문이라고 잠시 잘못 생각했었다. 스치듯 본 Vulkan 레퍼런스 문서에서 해당 옵션에 대해 “…앞의 정점과 뒤의 정점 중 어떤 값을 사용할지 지정한다…” 라는 문장을 보았기 때문이다.

자세히 알아 보니 Provoking Vertex는 렌더링 순서와는 아무 관련 없는 옵션이었다. 이 옵션은 Flat Shading을 할 때 한 프리미티브의 색상 값을 포함된 정점 중 어느 정점의 값으로 통일할지 지정하는 옵션이었다.

Provoking Vertex를 First로 지정하면 삼각형을 구성하는 세 정점 중 첫 번째 정점의 값을 이용해 색상을 결정하고, Last로 지정하면 마지막 정점의 값을 이용한다. 플랫 쉐이딩을 이용해 무언가를 렌더링 해 볼 일이 없었지만, 언젠가 사용할 때를 대비해서 문서로 남겨 놓는다.