소소한 나의 하루들

카메라와 컬링(Camera & Culling), 시네머신(Cinemachine) 이해하기 본문

개발/유니티

카메라와 컬링(Camera & Culling), 시네머신(Cinemachine) 이해하기

소소한 나의 하루 2025. 1. 18. 13:46

유니티에서는 다양한 카메라 동작과 연출들을 쉽게 구현할 수 있게 도와주는 시네머신(Cinemachine)이라는 기능을 제공한다. 하지만 이렇게 좋은 기능을 제공받고도, 상대적으로 복잡한 3d 프로젝트가 아닌 2d 프로젝트에서조차 카메라에 보여지는 스크린과 오브젝트가 Scene에 보여지는 모습에 대한 이해가 부족하여 카메라 동작 로직을 의도대로 구현하는데 정말 많은 어려움을 겪었다. 나에게 있어서는 이 카메라 동작이 정말 큰 벽이었다.

 그래서 카메라 및 오브젝트에서 사용하는 좌표 간 상관관계나 이론을 조금이나마 이해하는게 필요하다고 생각했다. 따라서 아래 책과 유니티에서 제공하는 강의의 도움을 받아 공부해보았다.

 

사실 게임 엔진을 개발할 것도 아닌데 무엇하러 이렇게 이론부터 세세하게 파고드느냐 라고 하면.. 할말은 없다. 그저 처음에는 이게 미련한 짓 같고 기초부터 하나하나 파고들고 기본기부터 다져나가려는 성향 탓에 그러는 건 아닐까 싶어서,  일부러 외면하고 필요한 기능 정도만 파악하고 활용하려고 했다. 그런데 이것조차도 어려움을 겪고 의도대로 안되서 시간만 흘러가니 계속 수박 겉핥기 식으로만 해보려다 실패하는 상황이 계속 발생됐고, 그래서 적어도 '카메라' 만큼은 가장 기초적이고 범용적인 부분인만큼 원리라도 파악한다면 그래도 이전보다는 훨씬 응용하는데 도움이 되지 않을까 싶었다.

이 수고가 지금의 문제를 해결하는데 조금의 보탬이라도 되길 바란다.

 

아래 자료나 설명은 대부분 링크된 책과 영상을 통해 인용되거나 정보를 재해석하거나 정리한 내용이다.

https://www.yes24.com/Product/Goods/30119802

 

유니티로 배우는 게임 수학 - 예스24

이론과 실무를 접목한 게임 프로그래머 최고의 수학 학습서! 게임을 개발하며 수학을 독학한 소프트웨어 엔지니어가 현업에서의 시행착오 경험을 바탕으로 현장에서의 수요를 염두에 두고 현

www.yes24.com

 

https://www.youtube.com/watch?v=O0qliGO7Oes


유니티에서 기본적으로 제공하는 탬플릿인 카메라(camera)가 중요한 것은 컬링(Culling) 때문이다. 컬링은 즉 카메라에 보이지 않는 것은 그리지 않는다라는 컨셉이다. 당연한 말이지만, 이러한 기능을 위해 내부적으로 많은 기술이 결합되어 들어있다.

[MVP(Model View Projection) Transformation : 모델 뷰 변환] 과정을 통해 뷰 볼륨(View Volume) 안에 들어온 것들을 걸러주는 것을 [Frustum Culling : 카메라 뷰 안에 있는 것들만 렌더링] 이라고 하고, [Occlusion Culling : 카메라 뷰에 보이지 않는 것들을 비활성화], [Culling Layer : 카메라 컬링 마스크를 사용하여 특정 레이어에 있는 오브젝트를 선별적으로 렌더링] 등등 여러 복잡한 요소들이 포함되어있다.

카메라가 오브젝트를 비추면, 카메라를 통해 오브젝트가 비춰진다. 

여기서 [MVP(Model View Projection) Transformation : 모델 뷰 변환] 개념이 나온다. (MVP Transformation 또는 Vertex Transformation)

카메라에 오브젝트가 보여지기 위해서 오브젝트가 매 프레임마다 그릴 때 변환을 거치게 된다. 

카메라가 있고, 카메라가 오브젝트를 비추면, Game View에서는 (가운데) 화면이 보이게 된다. 카메라도 오브젝트도 가만히 있는 월드 공간(World Space)에서는 내부적으로 항상 변환(Transform)이 일어난다.

이것들은 다 Vertices에서 일어난다. 우리가 보는 오브젝트는 Mesh(덩어리) 형태이고, Mesh는 Polygons(다각형)으로 이루어지고, Polygons는 Edges(선)으로 이루어진다. 그리고 Edges는 결국 Vertex(점)으로 이루어진다.

 (Vertex(점) → Edge(선) → Polygon(다각형) → Mesh(덩어리) : 오브젝트)

따라서 'Mesh가 움직인다'는 사실 Vertex가 움직인다는 것이다. (Vertex로 구성되어 있는 Mesh가 움직인다)

= 어떤 오브젝트가 있고, 이것이 정확한 위치에 카메라로 렌더링되기위한 과정이 필요하다.

Model Space (= Local Space, Object Space)

카메라가 비행기를 정면으로 바라본다. 그러면 PlayerCam에 나와있는 것처럼 보이는 것이 당연하다고 생각되지만 비행기라는 오브젝트는 무수히 많은 점(Vertex)으로 구성되고, 이 점들이 Edge(선)으로 구성되고, 선들이 Polygon(다각형)을 구성하고, 다각형들이 '비행기'라는 Mesh를 이룬다. 각 Vertex(점)들은 좌표를 갖고있고, 점들의 조합으로 Mesh가 만들어진다.
이렇게 유니티 Scene에 올라가지있지 않는, '오브젝트 모델'만 있는 공간을 Model Space, Local Space, Object Space 등으로 부른다. 오브젝트 중점 기준으로 각 점(vertex)들이 얼마나 떨어져있는지 데이터로 기술되어있기 때문에 '오브젝트' 자체도 어떤 공간이라고 할 수 있다. : Model Space(=Local Space, Object Space)

World Space

그러한 오브젝트(= Local Space)를 유니티 Scene에 올리게 되면, 특정 Transform 데이터를 가지고 그 좌표로 이동하게 된다. 그 좌표에 오브젝트 중심점이 위치하게 된다. 그래서 이것을 world 기준의 어떤 공간으로 이동하였다 하여 World Space라고 한다. 그리고 Scene 공간에 World 기준으로 오브젝트가 위치하는 것을 World Transform이라고 한다.

 

중요한 것은 오브젝트도, 카메라도 어떤 World 공간에 있는데 결국 우리 눈으로 보게되는 것은 카메라를 통해서 보기 때문에 결론적으로 모든 오브젝트가 카메라 기준으로 정렬되어야한다. 따라서 위의 (-4.5, -7, 3)은 Scene에서 볼 수 있는 World 기준 좌표계이고, 카메라의 상대적인 좌표로 바꿔줘야하기 때문에 

View Space(= Camera Space)

카메라 기준 공간을 View Space 또는 Camera Space라고 한다. 카메라 기준 공간(View Space)으로 바꿔주는 과정을 View Transform이라고 한다. 결국 Local Space(= Model Space, Object Space) → World Space → View Space(= Camera Space)로 변환해주게 된다.

Clip Space

사실 카메라를 통해 보게된다고 했지만, 결국 사용자는 모니터 공간으로 보게되는데, 모니터 공간으로 보기 위해서는 Projection(투영)이라는 과정을 거치게된다. 2D 공간을 3D 공간으로 보여주기위해 Clip Space라는 공간으로 변환하게되는데 그것을 보통 Projection 과정을 거쳐서 변환하게 된다. : Projection Transform 과정

3D 렌더링을 할 때 원근법이 적용되는데, 카메라 가까이에 있는 것은 크게, 카메라에 멀리 떨어진 것은 작게 보인다. 이러한 원근법을 Projection Transform(투영 변환)을 통해서 구현하게된다.

→Projectoin 과정을 거치면 Clip Space로 변하게 된다.

Projection이 적용된 카메라

그래서 카메라를 세팅할 때 2D에서는 Orthographic으로, 3D에서는 (원근법이 적용된) Projection 설정을 사용하는 것이다.

가까이 있는 것은 크게, 멀리 있는 것은 작게 보이는 카메라를 Perspective Camera라고 한다. 앞에서 설명한 World Transform(월드 변환), View Transform(뷰 변환), Projection Transform(투영 변환)은 모두 행렬을 사용해서 변환하게 된다.

(가까이 있는 것, 멀리 있는 것 모두 똑같은 정방향의 공간에 행렬을 통해 변환되는 공간변화를 통해 변환된다)

 

아무튼 이러한 일련의 복잡한 Vertex에 대한 변환과정이 내부적으로 셰이더 내에서 일어나고 있다.


Frustum Culling (초록색 면 : Near Clip Plane 보라색 면 : Far Clip Plane)

Vertex 셰이더 변환 과정을 통해 오브젝트가 카메라에서 비춰지는 상대적인 공간으로 들어오게 되고, 결과적으로 카메라 입장에서는 카메라가 보는 것만 그려야하는데 그 과정에서 Culling이라는 개념이 들어가게되고, 유니티가 기본적으로 제공하는 것이 Frustum Culling이다. 오브젝트가 여러 개 있을 때 파란 오브젝트가 카메라 기준의 View Space 내에 있는 오브젝트이고 빨간 오브젝트가 View Space 바깥, 그리고 너무 멀리있는 오브젝트들이다. 주황색 공간을 Frustum이라고 하는데, 이 Frustum 안에 들어와있는지 아닌지를 판단하여 오브젝트를 그릴지 결정하는 것을 Frustum Culling이라고 한다.

Frustum Culling과 실제 Camera 설정창

Frustum Culling은 3DProject에서 카메라 오브젝트를 클릭해보면 확인해볼 수 있다. 그리고 Camera의 속성에서 Clipping Plane의 Near와 Far 설정값을 확인할 수 있다. Frustum 영역 바깥, 혹은 Near보다 앞에있거나 Far보다 뒤에 있는 오브젝트는 그리지 않고 오직 Frustum 영역 안에 있거나 겹쳐있는 오브젝트만 그린다.

오브젝트의 Transform(월드 좌표) 기준으로 (내부적으로 설정된) 오브젝트의 Volume에 따라 Frustum과 겹치는지 확인한다.

Batchs : 2064(위) / 1374(아래)

이 Frustum Culling이 중요한 점은, 오브젝트가 Scene에 굉장히 많은데 Frustum을 크게 잡으면 멀리있는 오브젝트도 다 계산해서 그리게된다.(랜더링) 그래서 보통 모바일 게임에서는 Far 값을 많이 줄여서 오브젝트 렌더링을 최소화한다.

위쪽은 Draw Call(GPU에 특정 오브젝트를 렌더링하라는 명령을 내리는 단위)이 약 2000개, 아래는 약 1300개정도로 크게 차이가 난다. (아래쪽은 Far Clipping 값이 작기 때문에) 그래서 멀리있는 오브젝트가 자연스럽게 가려지도록 안개같은 효과로 가리게 된다. 보통 Fog의 End 값과 Clipping의 Far 값과 비슷하게 설정한다.

3d의 탑다운 뷰 등이 오브젝트를 덜 그리기 위함이다. 

Spatial Patition (Octree/Quadtree)

Culling이 비용이 많이 들긴 하지만 모든 오브젝트에 대해 Culling 체크를 하는 것이 아니고 공간분할 기법을 사용한다. 2D 평면에서는 Quadtree라는 기법을, 3D에서는 Octree 기법을 사용한다. 원리는 동일하다.

World를 4등분했다가, 다시 4등분, 4등분을 계속 반복하여 공간을 분할한다. Culling할 때 Frustum에 걸리는 블록들을 체크해서 제외할 것들은 제외하기 때문에 Culling 비용이 전체 오브젝트의 개수에 정비례하지는 않는다. 공간을 계속 관리해줘야하기 때문에 공간 데이터를 계속 연산해야한다. (3D에서 주요하게 고려해야할 부분)
공간을 분할하는 것은 GPU 렌더링 파트에서 렌더링 파이프라인 상에서 하는 것이고, Culling은 엔진에서 담당한다. 

유니티 Scene에 보이는 화면이 World Space(월드 좌표계 사용)이고, 유니티 Game에 보이는 화면이 View Space(뷰 좌표계 사용)이다.


Occlusion Culling

추가적으로 고려해야할 부분은 Occlusion Culling이다. 가려지는 것들은 그리지 않는다. 바깥의 빨간 오브젝트는 Frustum 바깥에 있어서 그리지 않는데, Frustum 안쪽의 빨간 오브젝트는 앞의 파란 오브젝트에 의해 가려져 있기 때문에 그리지 않는다.

Indoor Scene에서 Occlusion Culling

 Occlusion Culling은 FPS 장르의 Indoor 장면 등에서 많이 활용된다.

Batchs : 444(왼쪽) / 917(오른쪽)

똑같이 보이는 두 이미지더라도 Draw Call이 Occlusion Culling에 따라 약 400개, 약 900개로 크게 차이난다. (벽으로 가려짐) 오른쪽 이미지는 보이지 않더라도, 벽에 의해 가려진 오브젝트들의 vertex에 대한 연산이 이뤄지고 draw call이 일어나고 있는 것이다.

Occluder와 Occludee

Occlusion Culling에는 Occluder(가리는 오브젝트)와 Occludee(가림을 당하는 오브젝트)로 구분되는데, 보통 Occluder는 정적인 오브젝트만 가능하다. 

Culling을 하기 전에는 오브젝트가 이렇게 많은데, Culling을 한 이후에는 오브젝트가 선택적으로 보이게 된다. 바닥의 경우 하나의 전체 Mesh라서 잘라낼 수는 없다. 그리고 Occlusion Culling을 하게되면 Occludee 오브젝트들이 다 날라가게 된다.

여러 수치를 대입해보며 최적의 값을 찾아내기(매직넘버)

Occlusion Culling의 성능을 높이기 위해서는 매직넘버를 찾아야한다. 공간을 얼마나 잘게 자르느냐에 대한 parameter라고 보면 된다. 공간을 크게 자르면 Culling 효율이 떨어질 것이고, 너무 작게 자르면 연산 overhead가 발생할 수 있다.

이때 매직넘버는 이런저런 시도를 했는데 이 값에서 culling 효율이 좋아진다는, 그런 최적의 수치를 말한다. (노가다)

InDoor는 문을 닫으면 안보이고, 문을 열면 보이는 Occlusion Portal이라는 개념이 있는데, 문 오브젝트에다가 Occlusion Portal이라는 컴포넌트를 추가해주고, 문을 열게되면 .open = true가 되고, 문을 닫게되면 .open = false로 설정해주면 연산할지 안할지 설정해줄 수 있다. 동적으로 움직이는 occluder는 불가능하지만, 정적인 것은 가능하다. (on/off)


Culling Mask

Camera 컴포넌트에는 Culling Mask가 있는데, 보통 카메라가 여러 개 있을 때 카메라마다 그려주는 오브젝트를 지정해준다. Culling Mask를 사용할 때 주의할 점은 Layer 필터링 후 Culling하는 것이 아니라 Culling을 먼저하고, 걸러진 것들을 Layer 필터링해준다는 것이다. (Culling을 여러 번 하게되어서 여러 카메라를 사용하는 것을 권장하진 않는다)

 

보통 카메라가 여러 개 존재해야한다고 하면, 실제 카메라를 여러 개 사용하는 것보다 시네머신(Cinemachine)을 사용하는 것이 추천된다. 시네머신은 실제 카메라를 제어해주는 가상의 카메라이다.


Camera Stacking

카메라를 여러 개 사용해야하는 경우라면 서로 카메라 Frusum이 최대한 겹치지 않도록 사용하는 것을 권장한다. 카메라가 겹치게 되면 Culling 연산이 겹친만큼 그 이상 발생하기 때문에 카메라 간 Frustum이 겹치지 않으면 공간분할 후 영역을 제하는 과정에서 서로 필요없는 부분은 확실하게 연산없이 건너뛸 수 있어서 효율적이다.


LOD (Batchs : 147(왼쪽) / 231(오른쪽))

그리고 Culling과 유사한 개념으로 LOD Culling, Distance Culling 등으로 불리는 어느정도 멀리 떨어진 오브젝트는 그리지 않는 개념도 있다. 육안으로 볼 때는 크게 차이나지 않는데, 실제로는 먼 곳의 오브젝트는 안보이도록 처리하여 성능에서 이득을 취하는 기법도 있다. 주로 OutDoor Scene에서 활용하게 된다.


결론적으로 카메라를 최적화하고 성능을 효율적으로 끌어올리기 위해서는

1. Multi Camera는 최대한 지양한다. (카메라별로 culling 후 Layer filtering이 발생하여 culling 연산이 중복 발생)

2. Multi Camera가 필요한 Stacking 사용 시에는 Camera 간 Frustum 영역이 서로 겹치지 않도록

3. Multi Camera 대신 Cinemachine을 권장한다.

4. Culling 기법으로 Frustum Culling은 기본적으로 유니티에서 수행 / Occlusion Culling 처리 / LOD 처리는 개발자 재량

5. 상황에 따라 Clipping Far은 최소화하고, 필요 시 Fog distance와 적절히 조절

6. Top-down 뷰 게임에서는 skybox 불필요

Comments