마인크래프트 같은 Voxel 게임에서 메쉬 최적화하기

2019-11-04

Main Voxel을 최적화해봅시다

목차

  1. 마인크래프트 같은 Voxel 게임에서 메쉬 최적화하기
  2. Greedy Meshing과 Texture Atalas 같이쓰기
  3. 마인크래프트 같은 Voxel 메쉬에 Ambient Occlusion 추가하기

바보 같은 방법

Stupid 무척 낭비가 심합니다!

먼저 32*32*32 크기의 청크가 있고, 모든 복셀이 비어있지 않다고 합시다. 이때 마인크래프트 같은 메쉬를 생성하기 위한 가장 쉽고 편한 방법은 각 복셀을 순회하면서 복셀당 6면의 큐브를 만드는 것이죠.

큐브는 삼각형이 12개씩 필요하니 총 Vertex와 Index수는 24, 36개로, 최악의 경우에 모든 청크에 대해서 Mesh를 만들면 786,432개의 Vertex1,179,648‬개의 Index가 필요하게 됩니다. 보통 한번 맵을 생성할 때 150개 정도 청크를 만들게 되는데, Editor 상에서 아무것도 하지 않고 Mesh만 생성해놓은 상태에서 25FPS가 나왔습니다. 쉽게 구현한 것에 비해 매우 행복하지 못할 결과죠.

보이지 않은 면 Culling

Culling 더 좋아질 수 있어요!

각 복셀을 순회하면서 면을 만들 때, 인접한 복셀이 존재한다면 그 면은 어디에서 봐도 보이지 않습니다. 물론 인접한 복셀이 투명하지 않다는 가정이 있어야겠지요. 아무튼 면을 만들 때 인접한 복셀이 있는지, 그 인접한 복셀은 투명하지 않은지 검사해서 만약 그렇다면 그냥 건너뛰면 삼각형의 수를 줄일 수 있습니다.

사실 저 청크는 하나의 거대한 큐브라고 생각할 수 있습니다. 그럼 아직도 불필요한 삼각형들이 존재한다는 사실을 알 수 있죠. 이를 해결하기 위해서 Greedy Meshing 방법을 사용하겠습니다.

Greedy Meshing

Greedy Meshing 이 경우에는 최적의 Mesh입니다

각 복셀을 순회하면서 6개의 면을 추가하는 것이 아닌, 6 방향(X-, X+, Y-, Y+, Z-, Z+)을 순회하면서 인접한 Quad를 서로 병합하는 방법이죠. 일종에 2D 단면에 대해서 병합할 수 있는(Voxel의 Type이 같은 등) 면을 서로 합치는 알고리즘입니다. 이 글에서 알고리즘을 자세하게 설명하진 않겠습니다. 한 면에대한 Greedy Meshing은 이전 글 Marching Squares - Part 3 | Optimization에서 다루었으니 참고하시면 좋습니다.

Y축에 대해서만 Greedy Meshing

Greedy Meshing Only Y Axis 이런 변형도 가능합니다

2D 단면에 대해서 X, Y 두 축으로 Quad를 병합하는 작업은 조금 시간이 걸릴 수 있습니다. 따라서 알고리즘을 조금 변형해 한 축에 대해서(여기에서는 Y축)만 Quad를 병합할 수도 있죠. 이때는 메쉬를 만드는 시간이 Culling보다는 느리지만, Greedy Meshing보다는 빠르게 됩니다.

결과

Before After

Culling과 Greedy Meshing 비교

위와 같은 Scene을 구성해놓고 결과를 비교해보았습니다.

유용한 링크

Meshing in a Minecraft Game

코드 (Git Repository)