Culling
Culling은 카메라가 못 보는 것을 그리지 않아 draw call을 줄입니다. Frustum·Occlusion·LOD·Backface와 layerCullDistances를 익히면 실내외 장면에서 배치와 삼각형 수를 통제할 수 있습니다. Unity 렌더링 최적화의 핵심 가이드입니다.
상상해 보세요...
Culling은 똑똑한 무대 감독자와 같습니다. 무대 위 모든 배우에게 조명을 켜지 않고, 객석에서 보이는 배우만 비춥니다. 커튼 뒤? 불 끔. 각도 밖? 불 끔. GPU는 그들에 대해 듣지조차 않습니다 — 매 프레임 수천 draw call을 아낍니다.
개념 자세히
Frustum Culling이 기본입니다 — Unity가 카메라 frustum( FOV·near/far clip의 잘린 피라미드)의 여섯 평면을 각 Renderer의 bounding box와 검사합니다. bounds가 완전히 밖이면 → draw call 스킵. 내장, 항상 켜짐, 설정 불필요.
Occlusion Culling은 다음 단계 — frustum 안이지만 완전히 가려진 오브젝트를 스킵합니다. Unity가 bake로 occlusion 데이터(Umbra)를 미리 계산합니다: Window → Rendering → Occlusion Culling → Bake. Static 드롭다운에서 occluder/occludee를 표시합니다. 실내에 강하고 오픈월드에는 약합니다.
LOD (Level of Detail)는 관련 기법 — 완전히 cull하는 대신 LOD Group이 거리에 따라 메시 복잡도를 낮춥니다. LOD0 = 풀 디테일(가까움), LOD1 = 중간, LOD2 = 단순, LOD3 = billboard, Culled = 숨김. 거리별 품질/성능 트레이드오프.
Backface Culling은 GPU 수준 — 카메라에서
멀어지는 normal을 가진 삼각형을 버립니다. 모든 셰이더에서 기본 켜져 closed mesh의
fragment 작업을 ~50% 절약합니다. Material의 “Two Sided” 또는 ShaderLab의
Cull Off로 끌 수 있습니다.
Culling 유형
Frustum Culling
카메라 시야 밖 오브젝트는 완전히 건너뜁니다. 자동. 설정 불필요. 모든 프로젝트에서 유효.
Camera.layerCullDistances[]
Occlusion Culling
벽/건물에 가려진 오브젝트는 건너뜁니다. 베이크 필요 하고 Static으로 표시. 실내 씬에 최적.
Static → Occluder/Occludee
LOD Group
카메라에서 멀면 폴리곤 수를 줄입니다. LOD0→LOD3→Culled. 미리 준비해야 함 여러 메시. 오픈 월드에 적합.
LODGroup component
실습 단계
Frustum Culling: Camera Far Clip 줄이기
Camera → Far Clip Plane: 1000에서 200–500으로 낮춰 먼 오브젝트를 cull. 끊김은 fog로 가립니다.
Occlusion Culling용 Static 표시
벽/바닥/천장 선택 → Inspector → Static → Occluder Static과 Occludee Static.
occlusion 데이터 Bake
Window → Rendering → Occlusion Culling → Bake. Occlusion Area로 bake 볼륨을 제어합니다.
LOD Group 추가
Add Component → LOD Group → 각 슬롯에 LOD0/1/2 메시를 넣고 % screen size를 조절합니다.
Rendering Statistics로 측정
Game View → Stats: 카메라 이동 시 "Batches"와 "Tris"를 봅니다. Scene view의 Occlusion Culling Visualization.
인터랙티브 시뮬레이터
카메라를 씬 주위로 돌려 frustum-cull과 occlusion-cull되는 오브젝트를 보세요.
Visible: 0
Culled: 0
코드 예제
기본레이어별 cull 거리 설정과 renderer 가시성 확인.
using UnityEngine;
public class CullingSetup : MonoBehaviour
{
void Start()
{
Camera cam = Camera.main;
// Per-layer cull distance
// 32 entries (32 layers) — 0 = use Far Clip
float[] distances = new float[32];
distances[8] = 50f; // Layer 8 "SmallProps": cull after 50m
distances[9] = 150f; // Layer 9 "MediumProps": cull after 150m
distances[10] = 0f; // Layer 10: use Camera.farClipPlane
cam.layerCullDistances = distances;
// Spherical culling: from camera center, not frustum planes
cam.layerCullSpherical = true;
}
// Is this renderer currently visible to a camera?
void CheckVisibility(Renderer rend)
{
if (rend.isVisible)
Debug.Log(rend.name + " is being rendered");
}
}코드 예제
고급코드로 LOD Group을 구성하고 LOD 레벨을 강제(디버그 / 컷신).
using UnityEngine;
public class DynamicLOD : MonoBehaviour
{
public Renderer[] lod0, lod1, lod2;
LODGroup group;
void Awake()
{
group = GetComponent<LODGroup>();
if (group == null)
group = gameObject.AddComponent<LODGroup>();
// Configure LOD levels from code
LOD[] lods = new LOD[3];
lods[0] = new LOD(0.5f, lod0); // 50%+ screen → LOD0
lods[1] = new LOD(0.15f, lod1); // 15-50% → LOD1
lods[2] = new LOD(0.03f, lod2); // <3% → LOD2, <0 → culled
group.SetLODs(lods);
group.RecalculateBounds();
}
// Force a specific LOD (debug or cutscene close-up)
public void ForceLOD(int level)
{
group.ForceLOD(level); // -1 = disable force, use automatic
}
}📌 빠른 정리
- ▸Frustum Culling: 자동, 설정 불필요
- ▸Occlusion Culling: bake 필요, 실내에 최적
- ▸LOD Group: 거리에 따라 폴리 수 감소
- ▸layerCullDistances: 레이어별 cull 범위
- ▸renderer.isVisible: 렌더 중이면 true
⚠️ 흔한 실수
❌ Occlusion bake했는데 아무것도 cull 안 됨
Camera → Occlusion Culling = true를 잊음
✅ Camera Inspector → Rendering → Occlusion Culling 체크
❌ LOD pop(줌할 때 메시가 확 바뀜)
LOD 전환이 너무 넓고 LOD Fade 없음
✅ LOD Group에서 "Fade Mode = Cross Fade"로 블렌드