[게임 수학] #15 | 게임 엔진(Game Engine)

2023. 11. 28. 22:02Computer Sciences/Game Mathemathics

 *인프런 <게임 엔진을 지탱하는 게임수학, 이득우 교수님> 강의를 듣고 공부한 글입니다.

 

 

1. 로컬 공간과 월드 공간

로컬 공간(Local Space)

"하나의 물체를 그리는 데 사용하는 공간" 입니다. 어떤 하나의 물체는 여러 개의 점들이 모여서 형성되는데, 이러한 물체를 그리기 위해서는 원점을 중심으로 각 점의 상대적인 위치를 지정해야 합니다. 2차원 평면을 기준으로 하게 된다면 원점은 \((0, \ 0, \ 1)\) 이 될 것이고, 물체를 형성하는 점들은 이 원점을 기준으로 상대적인 위치에 배치되겠지요. 즉, 물체가 곧 세상의 중심인 셈입니다.

 

출처 : Unity Documentation (https://docs.unity.cn/kr/2018.4/Manual/EditingInPrefabMode.html)

 

 

월드 공간(World Space)

"게임 스테이지를 구성하는 데 사용되는 별도의 공간"입니다. 게임은 하나의 공간에 여러 물체가 배치되어 있고, 주인공이 움직이면서 공간을 탐험해야 합니다. 그런데 로컬 공간만을 사용하게 된다면 모든 오브젝트들은 원점 부근에 그려져서 겹치게 그려지겠죠. 그래서 이를 방지하고자, 각 물체를 구분할 수 있게 해주는 별도의 공간으로 월드 공간 개념이 나온 겁니다.

월드 공간은 각 물체들을 구성하는 로컬 공간들이 모여 있는 하나의 중심 공간이라고 볼 수 있습니다. 이를 수학적으로 설명한다면, 월드 공간을 기준으로 로컬 공간들이 선형 변환되어 위치회전, 크기에 대한 값을 가지고 배치되는 것으로 볼 수 있습니다. 

 

월드 공간은 로컬 공간들이 배치된 중심 공간이다.

 

이렇게 월드 공간에 배치된 로컬 공간들은 각각을 식별해주는 고유한 값이 부여됩니다. 일반적으로 위치, 회전, 크기와 같은 세 가지가 사용됩니다. 이들을 종합하여 트랜스폼(Transform)이라고 부릅니다.

 

그리고 위치(이동)과 회전, 크기는 아핀 변환에서 봤던 행렬이므로, 이 3가지 아핀 변환을 적용하여 로컬 공간에서 월드 공간으로 변환할 수 있는 트랜스폼 행렬을 구성할 수 있씁니다.

 

 


2. 트랜스폼 행렬(Transform Matrix)

크기(S), 회전(R), 이동(T) 이렇게 3가지 아핀 변환을 순서대로 적용한 결과는 다음과 같은 6가지입니다. 행렬은 교환법칙이 성립하지 않기 때문에, 다음 6가지의 각 결과는 모두 다릅니다.

 

$$ (S \cdot R \cdot T), \ (S \cdot T \cdot R), \ (T \cdot S \cdot R), \ (T \cdot R \cdot S), \ (R \cdot T \cdot S), \ (R \cdot S \cdot T) $$

 

그런데 이때, 만약 중간에 이동(S) 변환을 적용하는 경우에는 다음 그림과 같이, 최종 결과로 나타난 위치가 사용자 입장에서는 식별하기 어려워집니다. 따라서, 이동 변환은 가장 마지막에 적용하는 것이 편리합니다.

 

(0, 1)만큼 위로 올라가라고 명령을 했는데, 이동 변환을 중간에 하게 되면 엉뚱한 위치에 서 있게 된다.

 

이러한 이유로, 고려할 변환은 이동(S) 변환이 가장 마지막에 적용되는 다음 2가지입니다.

 

$$ (T \cdot S \cdot R), \ (T \cdot R \cdot S)$$

 

위 두 변환에서 아래 상황을 감안하면, 크기(S) 변환 이후에 회전(R) 변환을 적용하는 것이 바람직 할 것 같습니다.

회전 변환을 먼저 적용하고 크기 변환을 하게 되면, 원래 의도했던 모양이 아닌 전혀 다른 모양이 나올 수 있습니다.

 

회전변환 후 크기 변환을 하게 되면, 사각형을 좌우로 늘렸는데 마름모가 늘린 형태로 나오는 기이한 현상이 발생할 수 있다.

 

따라서, 게임 개발자들을 위해 적용해야 할 최종 트랜스폼 행렬은 \(T \cdot R \cdot S\) 이며, 이를 모델링 행렬이라고 부릅니다.

 

$$ M = T \cdot R \cdot S $$

 

게임 엔진은 개발자가 행렬을 몰라도 명령을 줄 수 있도록 데이터를 통해 트랜스폼 행렬을 관리하는 방식을 사용합니다. 이동(T)크기(S)벡터, 회전(R)각도 정보만 주면, 엔진 내부에서 알아서 변환 행렬을 만들어 적용해 주는 것이죠. UnityUnreal에서도 트랜스폼 컴포넌트란 것을 통해, 이러한 방식을 사용하고 있습니다.

 

Unreal Engine의 트랜스폼 설정

 

이와 같이 입력된 정보는 게임 엔진이 내부적으로 다음과 같이 모델링 행렬으로 변환합니다. 2D 공간이라고 가정했을 때,

 

$$ S = \begin{bmatrix} s_x & 0 & 0 \\ 0 & s_y & 0 \\ 0 & 0 & 1 \end{bmatrix} $$

$$ R = \begin{bmatrix} cos\theta & -sin\theta & 0 \\ sin\theta & cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} $$

$$ T = \begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \end{bmatrix}$$

 

와 같은 행렬로 구성할 수 있고, 이를 모델링 행렬로 만들면 다음과 같습니다.

 

$$ M = TRS = \begin{bmatrix} cos\theta \cdot s_x & -sin\theta \cdot s_y & t_x \\ sin\theta \cdot s_x & cos\theta \cdot s_y & t_y \\ 0 & 0 & 1 \end{bmatrix}$$

 

위 식에다가 입력한 데이터를 그냥 넘겨주기만 하면 최종 트랜스폼 변환이 완성되며, 이것이 행렬곱이 가지는 장점입니다.

 

 


3. 게임 렌더링의 진행 과정

게임에는 월드 공간에 수많은 물체들이 배치되므로, 이를 효율적으로 관리할 수 있는 체계가 있어야 게임이 원활하게 동작할 수 있을 것입니다. 게임 스테이지를 전체적으로 관리하기 위한 객체를, 개발 단계에서는 씬(Scene) 또는 레벨(Level)이라고 부릅니다.

 

* 유니티 엔진에서는 씬, 언리얼 엔진에서는 레벨이라고 부릅니다.

 

이러한 씬에 속한 각 물체들은 월드 공간을 기준으로, 어디에 배치되어 있는지를 알려주는 트랜스폼 정보가 반드시 지정됩니다. 이러한 트랜스폼 정보는 물체 단위로 관리되기 때문에 물체를 관리할 단위가 필요한데, 이를 게임 오브젝트(Game Object) 혹은 액터(Actor)라고 합니다.

 

*게임 오브젝트란 용어는 유니티 엔진에서, 액터란 용어는 언리얼 엔진에서 사용하는 용어입니다.

 

씬은 이러한 게임 오브젝트들을 묶어서 관리하는 구조인 것이죠.

 

씬과 게임 오브젝트의 구조

 

게임은 프레임(Frame)이란 단위로 진행되며, 매 프레임마다 씬에 속한 게임 오브젝트들의 최종 트랜스폼 정보가 확정되어야, 로컬 공간에 있는 메시 정보로부터 점을 변환해 최종 삼각형이 그려집니다.

 

그런데, 사실상 게임 오브젝트의 트랜스폼을 바꾸는 것과 메시의 삼각형을 그리는 것과 관련이 없습니다. 이 둘은 다른 일이라는 것이죠. 게임 오브젝트의 트랜스폼이 먼저 확정이 된 후, 그 뒤에 메시의 삼각형을 그리니까요.

 

따라서, 게임은 1)게임 오브젝트의 최종 트랜스폼을 확정하는 단계2)최종 트랜스폼으로부터 행렬을 생성 후, 메시의 정점을 곱해 변환하는 단계, 이렇게 2가지로 나뉘어 진행됩니다. 이를 각각 게임 로직렌더링 로직으로 구분할 수 있습니다.

  • 게임 로직
    게임 오브젝트의 트랜스폼을 확정하는 작업. 실제 렌더링 관련 작업은 발생하지 않는다.
  • 렌더링 로직
    로컬 공간을 기준으로 저장된 메시의 정점 정보를 월드 공간을 기준으로 재배치하고, 각 삼각형을 그리는 작업. 메시의 삼각형 위치를 확정하고 렌더링과 관련된 작업을 진행한다.

 

게임 엔진 작업

 

 


4. 게임 리소스(Resource) 관리

게임에는 씬 뿐만 아니라, 게임에 사용되는 데이터를 관리하기 위한 효율적인 체계 시스템이 필요합니다. 게임에 사용되는 데이터를 게임 리소스(Game Resource) 혹은 게임 에셋(Game Asset)이라고 부르죠.

 

게임 오브젝트마다 동일한 메시 정보를 중복해서 사용하는 일이 많기 때문에, 하나의 메시 정보를 공유해서 사용하는 것이 메모리를 많이 아낄 겁니다. 100마리의 몬스터 메시 정보를 모두 메모리에 로드하는 것이 아닌, 하나만 로드하여 100마리가 이 메시 데이터를 공유해 사용하도록 말이죠.

 

이를 위해서는 게임 리소스는 씬과 별개로 구성되어야 합니다. 이에 따라, 게임 리소스마다 고유의 키를 부여하고, 게임 오브젝트는 부여된 키 정보를 가지고 접근하는 형태로 관리하는 것이 일반적입니다. 이렇게 하면, 게임 오브젝트는 에셋을 소유할 필요가 없기 때문에 크기가 작아질 겁니다. 이와 같은 방식을 통해, 게임 에셋의 정보를 저장한 저장소를 리소스 리파지토리(Resource Repository)라고 부릅니다.

 

Resource Repository

 

리소스 리파지토리에는 모델링, 이미지, 음악 등등 게임에서 공유되어 사용되는 모든 데이터들이 다 들어가게 됩니다. 그리고 이 리소스 리파지토리는 유형에 따라서 1차적으로 카테고리 분류를 하게 되고, 그 다음에 각각에 대해서 고유 키를 통해 식별하게 됩니다.

 

종합해보면, 1)씬과 리소스 리파지토리는 분리되어 있고, 2)씬 내의 게임 오브젝트에게 리소스 키 정보만 제공하여 씬의 크기를 최대한 낮추고 데이터들을 효과적으로 관리하는 체계를 가지고 있다고 정리할 수 있습니다.

 

게임 엔진의 구조

 

 

 

728x90
반응형