Render Target으로 미니맵 구현하기

Perspective Projection VS Orthogonal Projection

시작에 앞서서 사영 종류에 대해 알아보자. 우리가 게임을 할때 컴퓨터가 3차원 공간에서 연산을 한다음에 최종적으로 2D Viewport에 사영하는 과정을 거친다. Computer Graphics의 Rendering Pipeline을 살펴보면 Clip Space에서 NDC로 물체를 사영시킬때 Perspective Projection에 의거하여 수학 연산을 하게 된다. 보통 대부분의 경우에 멀리 있는 물체가 작게 보이는 원근법(Perspective Projection)을 사용한다.

하지만 사영 방법에는 Perspective Projection 외에도 Orthogonal Projection 기법이 있는데, 이 기법은 물체의 거리와 무관하게 물체 순수 크기에 따라 사영 결과가 달라지게 된다. 미니맵은 물체가 상/하로 얼마나 이격되었는지에 대해 알고 싶은 것이 아닌 추상적인 지도의 개념이므로 Perspective Projection보다는 Orthogonal Projection 사영 방법이 더 적합하다.

Render Target과 Sprite의 콜라보

처음에 미니맵을 구현하려고 했을때 Scene Capture Component의 개념을 모르면 어떻게 구현하여야 할지 굉장히 막막했을 것이다. 반대로, Scene Capture와 Paper Sprite를 사용하게 되면 아주 간단하게 미니맵을 구현할 수 있다.

캐릭터에 수직 방향으로 Scene capture camera를 부착하고, 여기에 연동된 Render Target Texture를 Material로 가공해 UI에 보여주기만 하면 된다!

미니맵에는 Player Character가 그대로 나오면 이상하니 Scene capture camera와 Character 사이에 Character를 표현하는 Paper Sprite를 하나 넣어두어서 미니맵에 랜더링 되게 만든다.

Player Character 위에 paper sprite 매달기

캐릭터에 바로 Scene Capture Camera를 부착하면 Character Rotation에 따라 Camera가 같이 회전하게 된다. 이게 싫다면 캐릭터에 spring arm을 붙이고 이 spring arm에 Scene Capture Camera를 연결하자. 그런 다음, spring arm에서 Character yaw 상속을 해제하면 Camera는 항상 고정된 Rotation 값을 가지게 될 것이다.

Scene Capture Component 플래그 설정

Scene Capture Component에는 아주 편리한 기능이 있는데, 이는 바로 플래그에서 카메라가 랜더딩할 요소들을 toggle 버튼으로 끄고 킬 수 있다는 점이다. Paper Sprite를 캐릭터 위에 매달아도 Scene Capture 카메라에 의해서 부분적으로 보일 수가 있는데 이를 플래그를 통해서 안보이게 만들 수 있다.

또 필자는, 미니맵에 그림자를 없애기 위해서 custom depth stencil을 통해 시도를 해보았으나 친절하게 언리얼에서 Scene Capture Component에 Dynamic Shadow에 대한 플래그 또한 준비를 해두었다.

다만 Scene Capture Component에 플래그를 설정하는 과정에서 언리얼 엔진의 고질적인 문제가 있는데 그것은 에디터를 시작한 다음에 플래그 관련 설정을 한번 껐다 켜주어야 한다는 사실이다. 계속 처음에 설정이 안되길래 검색을 해봤더니 나와 같은 문제를 겪는 사람들이 많았다.

Flag 설정 X

Skeletal Mesh, Dynamic Shadow Flag Off


미니맵을 구현하는 2번째 방법

미니맵을 Render Target으로 구현하면 비교적 쉽고 빠르게 만들 수 있지만, 매번 Scene Component로 Render Target Texture를 만들어야 한다. 이는 연산에 많은 로드가 걸리므로 지향하는 방법은 아니다.

Texture위에 구현하기

다른 방법으로는 맵 자체를 스크린샷을 떠서 Player의 위치가 그 맵 위에 어디있는지 직접 계산하는 방법이 있다.

상단 뷰 & 언릿으로 맵 텍스쳐 가져오기

Character의 Rotation에 따라 회전될 Material

위 그림은 간단하게 Figma로 만들었다. 여기서 핵심은 밑에 맵이 보일 부분은 흰색, 나머지 회전될 부분은 검은색이라는 점이다. Opacity값에 이 Material과 맵을 곱하면 검은색 부분만 overlap 되어서 보일 것이다.

비율대로 UV map에 곱해주기

우리는 3000x3500 크기의 스크린샷 이미지를 1X1크기의 UV맵에 매핑시켜야 한다. 따라서 캐릭터가 레벨상에서 움직인 거리에 X축, Y축 각각 3000, 3500으로 나누는 계산이 필요하다.


적군 Marker를 미니맵 위에 표시

적군과 Main Character의 상대 위치 계산

적군 BP의 BeginPlay에 이들이 Spawn 될때 Create Widget -> Add To Viewport를 통해서 자신을 표시하는 마커가 Main HUD위에 그려지게 한다.

자신의 위치에서 메인 캐릭터의 위치를 빼면 메인 캐릭터 입장에서의 상대적인 벡터가 나오게 되고 이를 통해 위젯으로 적군이 메인 캐릭터와 어느 각도로, 얼마나 멀리 떨어져 있는지 알 수 있다.

범위를 벗어난 벡터는 Clamp

이제 성공적으로 적군의 위치를 표시할 수 있게 되었다. 한 가지 문제점이 있다면, 이 방법은 너무 정직하게 적군에 대한 모든 정보를 다 알려 준다는 것이다. 적군이 너무 멀리 있으면 미니맵 범위를 뚫고 저 멀리 마커가 표시된다. 이를 방지하고자 미니맵 범위를 넘어가면 길이는 clamp해주고 방향만 표시해 주는 방법을 사용해보자.

Vector Length 노드로 상대 Vector의 Norm을 가져오고, Normalize 2D라는 노드로 크기가 1인 순수 방향 벡터를 가져온다. Vector Length가 너무 멀면 아예 그리지 말고 어느 정도 멀다면 Clamp의 과정을 통해 미니맵의 가장자리에 걸치게 그려준다.

Clamp 범위를 벗어난 오브젝트들은 방향만, 캐릭터와 거리가 가까운 오브젝트들은 정확한 위치와 방향이 표시 가능한 미니맵이 탄생했다!

Material Parameter Collection

일반 BP의 Event Graph에서는 외부에 있는 객체에 대해서 접근하는 다양한 방법이 존재한다. 하지만, Material은 지원하는 노드들이 대부분 Rendering에 관한 것들일 뿐 외부의 객체들에 대해서 알 수 있는 방법이 없다. 이를 위해서 Material Parameter Collection이라는 별도의 객체가 존재하는데 이것의 역할은 Material과 이 Material이 소통하고 싶은 외부 객체 중간에 위치해서 정보를 전달한다.

MPC에 존재하는 Scalar & Vector Parameter Field

MPC(Material Parameter Collection)을 생성하면, 변수를 등록하는 field가 있는데 이 변수가 scalar인지 vector인지 나누어서 등록을 해야한다.


다시 Render Target으로...

미니맵 시스템을 메인 프로젝트에 적용하려고 했는데 엄청난 문제에 직면하게 되었다. 맵 크기가 크다보니 맵 텍스쳐를 고해상도로 받을 수 없었다.

위에서 바라본 화성 기지

확대샷

확대를 하니 답이 없는 수준으로 사진이 깨지게 되어서 어쩔 수 없이 Render Target을 사용하여서 최종 미니맵을 만들게 되었다. UI에 올려지는 적군 마커는 동일한 방법으로 상대위치 + clamping을 하여서 위젯에 올리게 만들었다.

미니맵을 작업한 레벨에 올려보았다. 바닥을 Nanite로 만들었는데 Scene Capture에 이 flag가 꺼져있어서 다시 키는 과정을 거쳤다.

profile
🏦KAIST EE | 🏦SNU AI(빅데이터 핀테크 전문가 과정) | 📙CryptoHipsters 저자

0개의 댓글