Using Texture2darray In Unity
Unity 에서 렌더링에 관련된 최적화를 할때는 TextureArray 를 사용할 수 밖에 없다. 이는 Unity 에서 DrawCall 을 줄이기 위해 써먹는 Batching 이라는 개념 때문인데 단순하게 말하면 그리는 새로운 매터리얼과 메쉬의 종류가 많으면 많을 수록 DrawCall 을 많이 하게 된다. 하지만 이 DrawCall 의 비용은 싼편이 아니기 때문에 CPU 의 성능을 꽤나 잡아먹게 된다. 그래서 Unity 는 자동으로 Batching 을 해주게 된다. 같은 메터리얼을 쓰면 자동으로 묶어주고, 같은 메쉬를 쓰면 또 자동으로 묶어준다. 결국 Batching 이 DrawCall 의 횟수와 같은 개념이 되는 것이다.
그래서 Batching 의 횟수를 줄이기 위해 매터리얼을 줄이는 방법에 대한 것이 TextureArray 다. 이것보다 일반적으로 알려진 기법은 TexutreAtlas 인데, 이 방법은 상당히 단순하다. 그냥 텍스쳐 한장에 모든 그림을 때려박고 UV 를 수정해주는 작업을 할때 쓰인다. 보통은 UI 이미지에서 스프라이트를 설정할 때 쓰이며, Unity 는 UGUI 기능에 Sprite 들을 합쳐서 TextureAtlas 로 만들어주는 기능이 있다. 하지만 3D 오브젝트의 UV 에서는 말이 조금 달라진다. UV 좌표는 0과 1사이의 값으로 이루어지는데 텍스쳐 여러장과 세팅되어 있던 UV 좌표들을 한장으로 통합해 다시 세팅하려면 굉장히 귀찮아진다. 그리고 합쳐지기전의 텍스쳐의 갯수가 합쳐진 후에 추가된다면 그것또한 굉장히 귀찮아질 것이다. 결국 생산성의 문제가 된다. 그래서 다른 방법을 쓸 수 있는데, 이 방법이 바로 TextureArray 다.
TextureArray 의 개념은 단순하게 텍스쳐를 배열로 묶은 것으로, 인덱스만 있으면 그냥 하나하나 참조하여 사용가능하다. 즉 UV 의 2차원 좌표와 함께 인덱스 한개만 더 있으면 된다. 그리고 TextureArray 의 장점은 TextureAtlas 마냥 합쳐주고 UV 를 수정할 일이 없고, 메쉬별로 인덱스를 따로 설정해주는 작업만 해주면 상당히 편하게 할 수 있다. 또한 텍스쳐 갯수가 몇개가 되던간에 메터리얼을 한개로 유지할 수 있기 때문에 굉장히 편하다. 근데 Unity 에서 사용하려면 몇가지 단점이 있다. Asset 생성을 지원하지 않기 때문에 굉장히 불편하고, 보여주는 GUI 또한 Unity 내부에서 지원하지 않는다. 편하게 사용하기 위해선 에디터 코드를 직접 만져야 한다. 물론 직접 생성해주는 것도 상관없지만 생산성 자체만 놓고보면 그다지 좋은 편은 아니다. 또한 Shader 코드들도 직접 바꿔주어야 하기 때문에 이것저것 세팅해줘야 할것이 많다. 즉 사용하기에 비용이 많이 든다.
이제 직접 Unity 에서 적용시켜보자.
할것들은 세가지다.
- Mesh 안의 단순한 2차원 UV 좌표를 (UV + 텍스쳐 인덱스)를 좌표로 가진 3차원 좌표로 바꾸기
- TextureArray 생성 및 적용하기
- Shader 코드에서 TextureArray 를 사용하고 UV 좌표를 3차원 좌표로 바꾸기
예제를 짜놓았으니 볼사람들은 참조하길 바란다.
UV 좌표를 바꾸는 것은 경우에 따라 다르다. 보통은 2차원 UV 좌표로 설정해놓았으니 프로그래머가 합치는 것만 생각하면 Mesh 별로 텍스쳐 인덱스를 심어주는 컴포넌트를 넣어주는게 편할 것이다. 시작시에만 UV 정보를 수정해주면 되니 로딩시간이 길어지는 것과 GPU 메모리를 조금 더 먹는 것 외에는 문제 될것은 없다. 초기 로드 시간이 걱정된다면 에디터에서 넣어주면 된다. 예제에서는 처음에 전부 생성하기 때문에 Vector2 로 저장하던 UV좌표를 Vector3 로 바꾸고 텍스쳐 인덱스만 끼워 넣었다.
TextureArray 부분을 작업하는게 제일 귀찮다. 그냥 컴포넌트에서 동적으로 생성해주면 장땡이긴 하지만 그런식이면 매번 컴포넌트를 건드려야하니 여간 귀찮은게 아니다. 그래서 예제를 보면 알겠지만 간단하게 래핑한 에셋을 만들었다. 근데 귀찮은 점이 하나 있다. 생성후에 에디터에서 텍스쳐 갯수나 여러것들을 수정하는게 안되서 몇가지 조건 중 하나가 문제면 다시 생성한다. 그러면 매터리얼과 연결이 끊기는데.. 혐오스럽지만 여기까지만 해놓았다. 혹시 이 방법 해결책을 아시면 댓글 부탁드립니다.
그리고 Texture2D 클래스와 Texture2DArray 에 SetPixel 류 함수들은 픽셀별로 접근하기 때문에 여러 텍스쳐 압축포맷이 먹힌 텍스쳐 복사는 안된다. Graphics.CopyTexture 로 하라고 여러 포럼들에 적혀있었다. 조건에 맞아야 사용이 가능하니 레퍼런스에 있는 상세한 설명을 참고바란다. 근데 우리가 사용하려는 간단한 전체복사는 전부 된다.
쉐이더 코드에서 TextureArray 를 사용하는건 굉장히 간단하다. Unity Manual : TextureArray를 참조하면 된다. 크게 어려울 것 없이 예제에 나온대로만 하면 된다.
전체적으로 사용하기에는 어렵지않다. 하지만 그에 비해 얻는 이득은 많으니 어떤 게임이든 당연히 쓰는게 좋을 것이다.