KoreanFoodie's Study

언리얼 렌더링 최적화 : Visibility 와 오클루전 컬링(Occlusion Culling) 본문

Game Dev/Unreal C++ : Study

언리얼 렌더링 최적화 : Visibility 와 오클루전 컬링(Occlusion Culling)

GoldGiver 2023. 3. 29. 23:46

언리얼 렌더링 최적화 : Visibility 와 오클루전 컬링(Occlusion Culling)

핵심 :

1. 렌더링에는 컬링(Culling) 이라는 개념이 있다. 이 개념은, 카메라의 시점에서 보이지 않는 녀석들을 굳이 렌더링 할 필요 없이 걷어내는(Culling) 기능이라고 생각하면 된다. 액터의 Visibility 는 Bounds Scale 을 설정해 해당 액터를 카메라에 보여줄 범위를 설정할 수 있다.
2. 컬링 방법(Culling Method) 에는 크게 4 종류가 있다 : Distance, View Frustum, Precomputed Visibility, Dynamic Occulusion.
3. Culling 과 관련된 정보들은 'stat initviews' 명령어로 확인할 수 있는데, 이 중 'Visible static mesh elements' 가 렌더링 성능에 제일 큰 영향을 미치는 녀석이다.

컬링에 대하여

언리얼에서 Visibility 와 Occlusion Culling 의 기본적인 아이디어는, 성능 향상을 위해 볼 수 있는 오브젝트의 갯수를 줄이자는 것이다.

예를 들어, 다음과 같은 오브젝트 들이 있다고 하자. (씬 뷰)

위의 씬을 위에서 바라보면 다음과 같이 표현할 수 있다.

그런데 사실 카메라 시야의 바깥쪽에 있는 부분은 어차비 보이지 않으므로, 컬링을 할 수 있다.

위 사진에서 빨간색으로 표시된 부분은 시야 바깥에 있으므로 렌더링 할 필요가 없다.

또한 다른 오브젝트에 가려져서 보이지 않는 오브젝트도 마찬가지로 렌더링할 필요가 없다!

 

 

액터의 Bound 설정

액터는 기본적으로 bounding sphere 과 bounding box 를 가지고 있다.

bounding sphere 은 간단한 거리 테스트를 위한 fast collision detection 에 사용되며, 일반적으로 해당 오브젝트가 점유하는 영역보다 더 크다.

반면, bounding box 는 상대적으로 실제 액터의 모양과 조금 더 정확하게 일치한다.

각각의 Static Mesh 와 Skeletal Mesh 는 bounding box 와 sphere 를 갖고 있으며 viewport 에 import 될 때나 scale, rotation 등이 조정될때 자동으로 액터에 맞게 조정된다.

 

Bounds Scale 은 Detail 패널에서 조정할 수 있다.

Static Mesh 나 Skeletal Mesh 에디터를 키고 Positive/Negative Bounds Exension 을 조절할 수도 있다.

참고로, 액터의 Bound 를 늘리는 것은 Culling 가능성을 낮춰 성능에 영향을 끼칠 수 있다.

 

 

컬링 기법

언리얼 공식 문서에는 크게 4가지의 컬링 기법을 소개한다. 하나씩 찬찬히 알아보자!

 

거리 기반

거리 기반 컬링은, 액터의 위치와 카메라의 거리를 기준으로 컬링을 적용한다.

레벨에 배치된 각각의 액터는 'Draw Distance' 를 가지고 있는데, Min/Max 값을 조절해서 카메라와의 거리에 따라 해당 액터를 보여줄지 말지를 결정할 수 있다. 거리값은 언리얼 Unit(단위) 값이다.

즉, Distance 기반 컬링을 적용하면, 카메라와의 거리가 Min 값과 Max 값 사이에 있는 액터만 화면에 보여질 것이다.

Current Max Draw Distance 는 Cull Distance Volume 이 있을 경우, 해당 값을 조회해서 값을 세팅한다.

Cull Distance Volume 이란, 해당 액터가 특정 값보다 작은 Volume 을 갖고 있을 때, 거리에 따라 렌더링 될지 안될지를 저장하는 배열이라고 볼 수 있다. 예를 들어, 먼 거리에 빌딩이 있고 내부가 보일 때, Cull Distance Volume 에 '거리 1000 이하일 때는 Bounds 가 10000 이하인 물체는 컬링한다' 같이 값을 세팅할 수 있을 것이다.

Volume 사이즈가 200보다 작은 녀석은 거리가 1000 이상이면 컬링된다. 다른 것들도 마찬가지로 생각하면 됨.

자세한 내용은 공식 문서를 참고하자.

 

절두체 (View Frustum) 방식

절두체 방식은 카메라의 FOV(Field of View) 를 이용해, 해당 영역에 있지 않은 물체를 컬링하는 방식이다.

절두체에서는 위의 3가지 용어를 알아두면 된다.

1. Near Clipping Plane : 오브젝트가 보일 수 있는, 카메라로부터 가장 가까운 지점의 집합

2. Camera Frustum : Near Clipping Plane 과 Far Clipping Plane 사이의 영역 (1번과 3번 사이)

3. Far Clipping Plane : 오브젝트가 보일 수 있는, 카메라로부터 가장 먼 지점의 집합

Level Viewport 에서 Show > Advanced 를 클릭하고 Camera Frustum 을 활성화하면 위와 같은 화면을 확인할 수 있다!

절두체 컬링에 대한 자세한 내용은 이 블로그를 참고하자. 설명이 명쾌하다.

 

Precomputed Visibility

Precomputed Visibility Volume 은 움직이지 않는 액터들을 대상으로, Shadow Casting Surface 위에 Visibility State 관련 정보를 저장해 놓는다. 이 컬링 방식은 라이팅 빌드 도중에 visibility 정보를 오프라인에서 생성하며, 크지 않은 레벨과 저사양/모바일 기기에 적합하다. 

더 자세한 내용은 언리얼 공식 문서를 참고하자.

 

Dynamic Occlusion

UE4 에서의 Dynamic Occlusion 시스템은 선택할 수 있는 여러 방식을 제공한다. 이때, 각 방식은 카메라의 절두체 영역에 특정 액터가 다른 액터에 의해 가려지는지 여부를 추적하며, 해당 정보를 GPU 나 CPU 에게 쿼리로 날리게 된다(쿼리라는 용어를 잘 기억하자 앞으로 계속 쓸 것이다). 그 후, 효과적인 컬링과 성능을 위해 휴리스틱하게 visibility 한 것들을 체크하고 불 필요한 것들을 보여주지 않을 것이다.

언리얼 엔진은 occlusion 쿼리를 만들 때 'Scene Depth Buffer' 를 사용하는데, 이 버퍼는 Maximum Draw Distance(아까 Far Clipping Plane 얘기하면서 언급된 개념이다) 대신 카메라로부터의 추정 거리에 의존하므로, 더 먼 거리까지 물체를 보여줄 수 있도록 한다. 또한 Depth Buffer 를 사용함으로써 해당 액터가 움직일 수 있는지 없는지 여부에 상관없이 컬링이 가능하고, opaque 하거나 masked blend mode 가 적용된 액터들 또한 처리할 수 있다.

Dynamic Occlusion 에 포함된 4가지 방식으로 소개한다 :

Hardware Occlusion Queries

Dynamic Occlusion 에서 제일 주요한 방식으로, 각 액터 당 프레임마다 GPU 에 Visibility Check 을 위한 쿼리를 날리는 방식이다. 액터의 visibility 여부는 한 프레임 늦게 갱신되므로, 카메라가 매우 빠르게 움직인다면 액터가 갑자기 화면에서 툭 튀어나오는 단점이 있을 수 있다. Hardware Occlusion 의 비용은 GPU 에서 계산되는 쿼리의 양에 달려있으며, Distance 과 Precomputed Visibility 방식을 사용해서 매 프레임마다 GPU 에서 계산해야 하는 쿼리의 양을 줄일 수 있다.

Hardware Occlusion 쿼리는 기본적으로 활성화되어 있으며 모바일 디바이스에서도 가능하다. iOS 는 ES 3.1 이상, Andriod 에서는 Vulkan 을 사용한다면 해당 기능을 사용 가능하다. 이 기능을 사용하지 않는 단말의 경우, Project Settings > Rendering > Culling > Occlusion Culling 옵션에서 해당 옵션을 끌 수 있다. 참고로, device config file 에서도 해당 값을 변경할 수 있다.

r.AllowOcclusionQueries=0

 

Hierarchical Z-Buffer Occlusion

Hierarchical Z-Buffer Occlusion(이하 HZBO) 는 Hardware Occlusion Queries 와 비슷하게 동작하나, 컬링에 조금 보수적이라는 차이가 있다(더 적은 오브젝트를 컬링한다는 뜻). 이 방식은 Mip mapped 된 버전의 Scene Depth 렌더 타겟을 이용해 액터의 Bounds 를 체크한다. 이 방식은 더 낮은 MIP 으로부터 샘플링을 하면 더 적은 텍스쳐를 요한다.

HZBO 는 다음 콘솔 커맨드로 활성화할 수 있다 :

r.HZBOcclusion=1

해당 개념에 대한 설명은 이 블로그를 참고하면 도움이 될 듯 하다.

 

Software Occlusion Queries for Mobile

Software Occlusion Queries 은 특정 액터에 가려진 다른 액터를 컬링하는 데 지정된 LOD(Level of Details)를 사용하는 방식이다. 이 컬링 방식은 CPU 에 씬을 레스터라이즈한다(Hardware Occlusion Queries 가 GPU 에 체크를 맡기는 것과는 상반되는 방식이다). 이 방식은 모바일 기기에서 적용될 수 있다. 자세한 내용은 공식 문서를 참고하자.

 

Round Robin Occlusion for VR

사실 일반적인 경우는 아닌데, 우리가 이미 알고 있는 Round Robin 의 개념처럼, VR 기기 사용시 왼쪽과 오른쪽의 프레임을 동시에 업데이트 해 주는것이 아닌 한 쪽 눈씩 업데이트 해주는 것을 말한다. 즉, 왼쪽으로 고개를 돌리는 경우, 왼쪽 버퍼를 한 프레임 먼저 업데이트 해주는 느낌이라고 생각하면 되겠다. 아래 변수를 바꿔서 설정할 수 있다 :

vr.RoundRobinOcclusion=1

 

 

Performance Statistics

최적화를 위해 컬링을 잘 조절하는 것은 매우 중요하다.

예를 들어, Precomputed Visibility 의 경우, 컬링된 오브젝트를 런타임에 로딩하는데 얼마나 많은 메모리가 사용하는지 살펴 보아야 한다. 또 Hardware Occlusion Queries 같은 경우, GPU 에 각 프레임당 얼마나 많은 부담이 가해지는지 살펴볼 필요가 있다.

stat initviews 명령어로 해당 수치들을 확인할 수 있다.

위의 값은 Cycle Counters 와 Counters 로 나뉜다. Cycle Counter 값은 primitives 를 처리하는데 걸리는 사이클을 밀리초로 표시한 것이다. 주목할 값은 View Visibility, Occlusion Cull, View Relevance, 그리고 Frustum Cull 등이 있다.

Counter 는 현재 뷰에서 처리된 모든 primitive 들을 더한다.

Visibility 를 테스트할 때는 다음 내용을 기억하자 :

  • Occlusion culling is disabled in Wireframe view mode.
  • Use the hotkey G to switch to gamemode view while working in the viewport to see some culling methods, like Cull Distance Volumes or Precomputed Visibility Volumes.
  • For the most accurate results, use Stat InitViews statistics when in Play-In-Editor (PIE) or Standalone Game. Geometry for the Actors that represent Lights, Cameras, and others will be included when calculating visibility and occlusion culling results.
  • Keep an eye on Visible Static Mesh Elements because it is the single biggest contributor to rendering thread time and should be carefully watched and optimized.

Visible Stataic Mesh Element 의 갯수가 렌더링 성능에 미우 큰 영향을 끼친다는 것을 기억하자!

각각의 항목에 대한 설명도 참고로 알아두도록 하자.

 

 

Debugging Culling

 다음 값을 설정하면, 에디터에서 해당 액터가 컬링되었는지를 확인할 수 있다 :

r.VisualizeOccludedPrimitives 1

숨겨진 액터들의 WireFrame 이 보이게 된다!

 

Freezing Rendering of the Scene

커맨드 콘솔에 FreezeRendering 을 치면, 컬링된 오브젝트들을 다음과 같이 확인할 수 있다!

뒤에는 박스들이 있다 (보이지는 않는)

카메라를 Freeze 한 후, 컬링된 녀석을 보여준다.

관련 커맨드를 참고로 알아두자.

 

 

Using Game View Mode

Game View Mode 를 사용하면 에디터에서도 실제 게임이 실행되었을 때의 화면을 확인해 볼 수 있다.

단축키 G 를 누르거나, Game View 모드를 Viewport Dropdown 메뉴에서 선택하자.

Game View 모드에서는, 액터 아이콘(Lights, Particle Systems 등)이 사라지고, 컬링을 적용했을 경우 컬링한 결과까지 적용되어 화면이 나오게 된다.

일반 에디터 뷰

Game View 모드

 

참고 : 언리얼 공식 문서
Comments