본문 바로가기

Game/Unity & C#

5장 카메라, 렌더링, 씬 (1)

5장 카메라, 렌더링, 씬

 

- 카메라 : 씬이 렌더링되는 시점이다. 카메라는 주어진 원근감과 시야로 만들어진 씬의 광경을 이루는 3D 공간 안의 한 점으로서, 픽셀 형태의 텍스처로 담기며 래스터화된다. 이런 과정을 거친 후, 이 텍스처는 다른 카메라들을 통한 이전의 렌더링 결과의 맨 위에 혼합 및 합성되어 화면상에 렌더링된다.

 

1. 카메라 기즈모

- 씬에서의 카메라 위치, 시야 등의 속성을 통해 카메라에 보이는 모습 등을 명확히 보여주는 것을 프러스텀 기즈모(frustum gizmo)라고 한다.

 

 

2. 보이기

- 게임이 진행되는 동안 오브젝트 가시성에 대한 의문이 생길 때가 많다. 오브젝트가 보이지 않을 때, 처리 부하를 줄이기 위해 멈출 수 있는 동작이나 계산이 많기 때문이다. 게다가 카메라가 움직였을 때 오브젝트가 보이게 될지를 알면, 어떤 오브젝트가 다음 프레임에 보이게 되므로 미리 준비해야 하는지 예상 가능하도록 도움을 준다.

 

- 가시성에 대해서는 두 가지 주요 개념이 있는데, 바로 프러스텀과 오클루전이다. 원근 카메라는 시야 프러스톰을 가지고 있다. 이 프러스텀은 카메라 렌즈의 바깥쪽 방향으로 뻗어나가는 사다리꼴의 체적으로서 시야와 절단면 거리 속성으로 정의된 영역을 포함한다. 기본적으로 프러스텀은 수학적으로 정의된 카메라의 지평선이다. 즉, 프러스텀은 카메라가 현재 볼 수 있는 가능성을 가진 영역이다. 가능성이라는 단어가 중요한데, 카메라 프러스텀 안에 볼 수 있는 유효한 오브젝트가 존재하더라도 꼭 카메라에 보인다는 보장은 없다. 바로 프러스텀 안의 오브젝트들이 서로를 전체적으로든 부분적으로든 가릴 수 있기 때문이다.

 

 

3. 오브젝트 가시성 감지

- 유니티에서 가장 간단하고 직접적인 가시성 테스트 방법은 어느 카메라에서든 오브젝트가 보이거나 보이지 않게 될 때를 감지하는 방법일 것이다. 두 짝꿍 이벤트인 OnBecameVisible과 OnBecameInvisible은 MeshRenderer나 SkinnedMeshRenderer와 같은 렌더러 컴포넌트를 가지는 모든 오브젝트에서 자동으로 호출된다. (빈 게임오브젝트가 카메라의 뷰 안에 존재하는 경우에는 눈에 보이는 부분이 없기 때문에 이런 이벤트가 호출되지 않는다)

 

- 이 이벤트들을 사용할 때 주의사항이 있다. 첫째는 여기서 알려주는 가시성은 단지 오브젝트가 카메라 프러스텀 내에 들어왔다는 의미이므로 다른 더 가까운 오브젝트 등에 의해 차단되어 실제로는 보이지 않을 수 있다. 둘째는 이벤트는 특정 카메라에 속하는 것이 아니라 전체 카메라에 대해 발생하는 것이다. 즉 이 이벤트들은 NPC 간의 상호작용과 같이 가시성에 의존하는 AI 동작을 켜고 끄는 등의 동작을 넣기에 좋다.

 

 

4. 오브젝트 가시성에 대해 좀 더 살펴보기

- 다른 중요한 검사는 오브젝트가 카메라 가시권에 들어가고 나가는 것뿐만 아니라, 오브젝트가 특정 카메라에 보이는지를 검사하는 것이다. 한 번만 호출되는 OnBecameVisible이나 OnBecameInvisible과는 다르게 이 검사 방법은 이전 상태에 대한 정보 없이 오브젝트의 현재 상태를 검사한다. OnWillRenderObject 이벤트를 이용해 이러한 검사를 할 수 있다. 이 이벤트는 오브젝트가 카메라에 보이는 동안 각각의 카메라에서 매 프레임 한 번씩 계속해서 호출된다. 여기서 'Visible'은 'In Camera Frustum'을 의미한다. 하지만 이 이벤트에서도 다른 오브젝트에 차단되는지를 검사하지는 않는다.

 

 

5. 프러스텀 검사: 렌더러

- 앞서 살펴봤듯이 유니티에 내장된 카메라 이벤트들은 독자들에게 필요한 가시성 및 프로스텀 검사에 필요한 요건들을 충분히 갖추지 못하는 경우가 많다. 구체적으로, 카메라 하나가 렌더러를 볼 수 있는지, 보이지 않는 오브젝트가 보이는 상태였다면 정말 보일 것인지, 공간상의 특정 지점이 카메라에 보이는지, 특정한 오브젝트가 새로운 위치로 옮겨지면 카메라에 보이게 될지 등을 간단히 검사하길 원할 수 있다. 이런 모든 경우들은 각자 다른 상황에서의 가시성 검사를 위해 중요한 예시이고, 각각 직접 검사하는 단계가 필요하다. 이러한 카메라 가시성 검사에 필요한 코드를 집중적으로 작성해야 한다. 다음 예제 코드처럼 특정 카메라 오브젝트의 프러스텀 내에 특정 컴포넌트가 있는지 검사하는 함수를 만들어보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class CameraUtility : MonoBehaviour
{
    // 렌더러가 특정 카메라의 프러스텀에 포함되었는지를 감지하는 함수
    // 렌더러가 프러스텀 내에 있으면 true, 아니면 false를 반환한다
    public static bool IsRendererInFrustum(Renderer fRenderable, Camera fCamera)
    {
        // 카메라에서 프러스텀 평면을 생성한다
        // 각 평면은 프러스텀의 벽 한 면을 나타내는 것이다
        Plane[] planes = GeometryUtility.CalculateFrustumPlanes(fCamera);
 
        // 프러스텀 평면 안에 렌더링 가능한 것이 있는지 검사한다
        return GeometryUtility.TestPlanesAABB(planes, fRenderable.bounds);
    }
}
 

- GeometryUtility 클래스를 이용해 카메라의 프러스텀을 표현하는 평면 오브젝트들의 배열을 생성했다. 선이 2D 공간에 대응한다면, 평면은 3D 공간에 대응해 두께를 가지지 않는 가상의 표면을 3D 공간상에 표시한다. 프러스텀 평면은 여섯 개 평면의 집합으로 3D 공간에서 완전한 사다리꼴 카메라 프러스텀을 표현하게 된다. 이 배열은 TestPlanesAABB 함수에 의해 검사에 이용된다. AABB(Axially Aligned Bounding Box)는 프러스텀 안에 메시 렌더러가 존재하는지를 감지하기 위한 충돌 경계이며, 앞에서 평면으로 선언해둔 것이 바로 이것이다.