Greedy Meshing과 Texture Atalas 같이쓰기

2019-11-05

Main 텍스쳐를 입혀봅시다!

목차

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

문제점

Full Texture UV가 정상적으로 적용된 모습

Greedy Meshing을 적용해서 최적화된 메쉬는 인접한 면들이 병합되었기 때문에 uv를 만들 때 고려해야할 점이 많습니다. 만약 큐브의 한쪽 면의 uv를 (0,0) ~ (1,1) 까지 정해 텍스쳐 전부를 그리는 상황에서는 문제가 되지 않습니다. 바로 위 이미지처럼 말이죠. 왜냐하면 그냥 단순히 uv를 병합된 면의 가로와 새로 길이만큼 늘리고, 텍스쳐의 Warp 모드를 Repeat으로 설정하면 됩니다.

Full Texture but Many Problem UV는 정상이지만, 문제가 많아보인다

하지만 이렇게 할 경우에는 만든 복셀 메쉬를 그릴 때 텍스쳐를 복셀 종류마다 딱 한장의 텍스쳐만 쓸 수 있다는 단점이 있습니다. 물론 그것도 2d Texture Array를 이용하면 가능할 수 있겠지만, 여기서는 Texture Atlas를 사용하는 방법을 다루도록 하겠습니다.

Texture Atlas

Atlas 공기, 풀, 흙, 돌

public enum VoxelType { Air, Grass, Dirt, Stone } // Voxel의 타입
public enum Direction { Right, Left, Top, Down, Front, Back } // 큐브의 Face 방향

본 글에서 사용할 텍스쳐 아틀라스입니다. 0부터 5까지는 위 코드에서 Direction을 의미하고, VoxelType과 같게 비어있는 이미지부터 Air, Grass, Dirt, Stone 순으로 배치하였습니다.

float4 uv = new float4
{
    x = CubeUVs[i].x * width, 
    y = CubeUVs[i].y * height,
    z = atlasPosition.x, 
    w = atlasPosition.y
};

uv를 전달할 때는 병합되어 만들어진 면의 크기를 곱한 uv를 넘겨주고 나머지 z와 w에 Atlas에서 사용할 이미지의 좌표를 저장합니다. 그럼 Shader에서 그 정보를 이용해 타일링을 해주는거죠.

MipMap 문제

Before After

MipMap문제 해결 전/후 비교

Shader에서 이를 구현하기 위해서는 uv를 모듈러 연산을 하거나 frac 연산을 이용해 타일링을 하게됩니다. 하지만 이는 MipMap이 작동할 때 문제가 되는데, 작게 생성된 MipMap텍스쳐의 가장자리에서 이웃 픽셀을 샘플링하게되어 위 이미지처럼 이음새가 생깁니다. 이를 해결하기 위해 tex2Dgradddx, ddy를 이용해 텍스쳐를 샘플링할 때 GPU가 옳바른 Mip Level을 결정할 수 있도록 해줍니다.

결과

Result 최종 결과

유용한 링크

코드 (Git Repository)