Unity 렌더링 기초: 불투명·반투명 그리기 순서와 컬링
Unity는 불투명(Opaque) 오브젝트와 반투명(Transparent) 오브젝트를 다른 순서와 방식으로 그린다. 그리기 전에는 카메라에 보이지 않을 오브젝트를 컬링(Culling)으로 걸러 낸다.
불투명 오브젝트
섹션 제목: “불투명 오브젝트”벽, 바닥, 캐릭터처럼 뒤가 비치지 않는 오브젝트다.
깊이 버퍼
섹션 제목: “깊이 버퍼”- 픽셀을 그릴 때 그 픽셀의 깊이 값(카메라로부터의 거리)을 깊이 버퍼(Z-Buffer)에 기록한다.
- 다른 오브젝트의 픽셀을 그릴 때 깊이 버퍼에 이미 있는 값과 새 픽셀의 깊이를 비교한다.
- 새 픽셀이 더 가까우면 색을 덮어쓰고 깊이 버퍼도 갱신한다.
- 더 멀면 그리지 않는다.
ShaderLab 기본값은 ZWrite On, ZTest LEqual이다. 기존 픽셀과 거리가 같거나 더 가까운 픽셀만 그린다(Cull and Depth).
그리기 순서
섹션 제목: “그리기 순서”깊이 테스트 덕분에 그리는 순서가 섞여도 겹침 결과는 맞게 나온다. 순서는 성능에 영향을 준다. 가까운 것부터 그리면 뒤쪽 픽셀이 깊이 테스트에서 걸러져 같은 픽셀을 여러 번 칠하는 오버드로(Overdraw)가 줄어든다. 정렬은 Unity가 드로우 콜을 만들 때 한다. Unity 문서는 불투명 큐의 오브젝트를 성능에 맞게 다시 정렬하고, 렌더 상태 변경을 줄이는 기준도 함께 쓴다고 설명한다(RenderQueue.GeometryLast).
- 깊이 버퍼를 읽고 쓴다.
- 기본 Render Queue는 Geometry(2000)다.
반투명 오브젝트
섹션 제목: “반투명 오브젝트”유리, 물, 파티클, UI처럼 뒤가 비치는 오브젝트다.
알파 블렌딩
섹션 제목: “알파 블렌딩”반투명 오브젝트가 깊이 버퍼에 쓰면, 그 뒤에 있는 오브젝트는 깊이 테스트에서 “더 멀다”고 판단되어 그려지지 않는다. 그래서 반투명 셰이더는 보통 ZWrite Off로 깊이 버퍼에 쓰지 않는다. 대신 알파 값으로 이미 그려진 색과 새 색을 섞는다.
// Blend SrcAlpha OneMinusSrcAlphaFinalColor = NewColor * Alpha + OldColor * (1 - Alpha);그리기 순서
섹션 제목: “그리기 순서”블렌딩 결과는 순서에 따라 달라진다. 가까운 반투명 오브젝트를 먼저 그리고 먼 것을 나중에 그리면 섞이는 순서가 뒤집힌다. 그래서 Unity는 Transparent 큐의 오브젝트를 카메라에서 먼 것부터 가까운 것 순서(Back-to-Front)로 정렬해 그린다.
- 깊이 버퍼를 읽기만 한다. 앞에 불투명 오브젝트가 있으면 그 픽셀은 그리지 않는다.
- 깊이 버퍼에 쓰지 않는다. 뒤에 있는 다른 반투명 오브젝트도 그려질 수 있게 하기 위해서다.
- 기본 Render Queue는 Transparent(3000)다.
렌더링 순서
섹션 제목: “렌더링 순서”- 불투명 패스: Render Queue 2500 이하의 오브젝트를 그린다. 이때 깊이 버퍼가 채워진다. AlphaTest(2450) 큐도 여기에 들어간다. 픽셀 단위로 그릴지 버릴지 정하지만, 남은 픽셀은 불투명처럼 깊이 버퍼에 쓴다.
- 스카이박스: 모든 불투명 오브젝트 다음, 반투명 오브젝트 전에 그린다.
- 반투명 패스: Render Queue 2501 이상의 오브젝트를 먼 것부터 정렬해 그린다. 깊이 버퍼를 참조해 불투명 오브젝트 뒤에 있는지 확인하고, 알파 블렌딩으로 색을 섞는다.
Unity 문서는 [0, GeometryLast] 범위를 불투명으로, [GeometryLast+1, 5000] 범위를 반투명으로 처리한다고 적는다(RenderQueue.GeometryLast).
Render Queue
섹션 제목: “Render Queue”그리기 순서는 셰이더 SubShader나 머티리얼의 Render Queue 값으로 정한다.
SubShader{ Tags { "Queue" = "Transparent" } // ...}| 큐 | 값 | 용도 |
|---|---|---|
| Background | 1000 | 가장 먼저 그리는 배경 |
| Geometry | 2000 | 기본값. 대부분의 불투명 오브젝트 |
| AlphaTest | 2450 | 나뭇잎, 철조망처럼 픽셀 단위로 그릴지 정하는 오브젝트. 불투명처럼 처리한다 |
| GeometryLast | 2500 | 불투명으로 취급하는 마지막 큐 |
| Transparent | 3000 | 반투명 기본값. 먼 것부터 정렬한다 |
| Overlay | 4000 | 렌즈 효과처럼 마지막에 그리는 오브젝트 |
"Geometry+1"처럼 이름에 오프셋을 더해 중간 값(2001)을 쓸 수도 있다. 불투명 오브젝트 다음, 다른 반투명 오브젝트 전에 그려야 하는 물 같은 경우에 쓴다. 값과 2500 경계는 ShaderLab SubShader Tags(2017.4)에 있다. 커스텀 SRP에서는 렌더 큐를 쓸지와 그리기 순서를 직접 정할 수 있다(SubShader 태그).
카메라에 보이지 않는 오브젝트를 GPU로 보내지 않는 최적화다.
절두체 컬링
섹션 제목: “절두체 컬링”절두체(Frustum)는 카메라가 볼 수 있는 공간이다. 윗부분이 잘린 사각뿔 모양이고, 시야각(FOV)과 Near Clip Plane, Far Clip Plane으로 정해진다.
Unity는 Mesh Renderer 같은 렌더러마다 경계 상자(Bounding Box)를 알고 있다. 매 프레임 CPU가 이 경계 상자가 절두체에 조금이라도 걸치는지 검사한다.
- 조금이라도 걸치면 보일 수 있으므로 그린다.
- 완전히 밖에 있으면 GPU로 보내지 않는다.
카메라는 기본으로 절두체 컬링을 한다(Occlusion culling).
오클루전 컬링
섹션 제목: “오클루전 컬링”절두체 컬링은 시야 범위만 본다. 시야 안에 있지만 벽이나 건물 같은 큰 오브젝트에 완전히 가려진 오브젝트는 걸러 내지 못한다. 오클루전 컬링은 이렇게 가려진 렌더러를 제외한다.
- 자동으로 동작하지 않는다. 에디터에서 데이터를 미리 굽는(Bake) 작업이 필요하다.
- 굽는 과정에서 Unity는 씬을 셀(Cell)로 나누고, 셀 안의 지오메트리와 인접한 셀 사이의 가시성을 데이터로 만든다. 가능한 셀은 합쳐 데이터 크기를 줄인다.
- 런타임에 이 데이터를 메모리에 올리고, Occlusion Culling을 켠 카메라마다 조회한다.
- 절두체 안에 있더라도 가려진(Occluded) 렌더러는 그리지 않는다.
내장 오클루전 컬링은 런타임 계산을 CPU에서 한다. 그래서 오버드로 때문에 GPU 병목인 프로젝트에서 효과가 크다(Occlusion culling). Unity 6의 URP·HDRP는 깊이 버퍼를 이용하는 GPU 오클루전 컬링도 지원한다. 관련 내용은 Unity 6의 GPU 기반 렌더링 성능 개선에 정리했다.
Z 테스트
섹션 제목: “Z 테스트”두 컬링을 통과해 GPU로 넘어간 뒤에도, 픽셀을 그릴 때 이미 그려진 불투명 픽셀보다 뒤에 있는 픽셀은 깊이 테스트에서 걸러진다. 컬링이 오브젝트 단위라면 Z 테스트는 픽셀 단위다.
| 단계 | 동작 시점 | 설정 | 거르는 대상 |
|---|---|---|---|
| 절두체 컬링 | CPU, 매 프레임 | 자동 | 시야 밖 오브젝트 |
| 오클루전 컬링 | CPU, 매 프레임(내장 방식) | Bake 필요 | 시야 안에서 완전히 가려진 오브젝트 |
| Z 테스트 | GPU, 픽셀마다 | 셰이더 ZTest |
이미 그려진 픽셀보다 뒤에 있는 픽셀 |