[Cloud #1]
번역 -
Tiling within subUV or pseudo-volume textures

Seangchan Jung
11 min readNov 27, 2020

--

한 줄 요약 : mip map 생성시에도 문제 없는 SubUV용 Texture를 만들어 보자!

다시 말해 : mip map 안 쓸꺼면 필요 없는 단계..

This post is a translation of original post (link above). Please let me know if there any issues about this post.

이 포스트는 레이마칭을 이용한 volumetric 효과를 만드는 것의 초석이 되는 포스트입니다. subUV 텍스쳐와 volume 텍스쳐는 공통점이 많습니다. subUV 텍스쳐나 volume 텍스쳐를 하드웨어가 지원하는 3d 텍스쳐를 사용하지 않고 만들길 원한다면 각 타일의 모서리 부분에서 artifact가 생기게 됩니다. 나중에 추가 될지는 모르겠지만 지금 현 시점에서 UE4에서 3D 텍스쳐 포맷이 없기 때문에 이 문제는 중요합니다. 만약 그런 3D 텍스쳐 기능이 UE4에서 지원되게 되더라도 하드웨어의 mip map 생성 및 가속을 지원 받을 수 없기 때문에 해당 기능을 사용하는 것이 이상적이지 않을 수 있습니다. 다행히도 아주 약간의 머티리얼 작업을 통해 이러한 이슈를 해결할 수 있습니다.

예제는 타일들로 이루어지는 물결 애니메이션 텍스쳐를 만드는 것입니다.

SubUV와 ‘Pseudo 볼륨 텍스쳐’는 타일링시 각 프레임이 서로의 바로 옆에 위치하기 때문에 문제가 발생합니다. 이는 각각의 프레임이 개별로 타일링 될 때는 타일링이 seamless 하더라도 하나의 프레임에서의 texel이 다음 프레임의 잘못된 값과 블렌드되면서 edge artifact가 발생한다는 뜻입니다. 아래는 subUV 애니메이션으로 물결을 렌더링 하는 예제입니다. 텍스쳐 내의 각 프레임은 seamless하게 타일링 되도록 만들어져 있습니다. 텍스쳐는 아래와 같습니다.

http://storage.googleapis.com/wzukusers/user-22455410/images/57f8326b7740bDzH04F1/SubUV.jpg

그리고 SubUV function을 이용해서 위를 나타내보면, 모서리 부분에서 edge artifact를 확인할 수 있습니다.

http://storage.googleapis.com/wzukusers/user-22455410/images/57f832516c637Bh4wl3L/Tiling_Artifact.gif

해결법을 찾기 위해 Monster Truck Madness 에서 사용된 오래된 기술을 기억해냈습니다. Monster Trcuk Madness 2의 모드 작업을 했을 때가 있었는데, 그 때 이 issue를 다뤘습니다.

http://storage.googleapis.com/wzukusers/user-22455410/images/57f80168df660zdResz1/Mtm.jpg

그때 당시에는 GPU에서 지원하는 기능이 매우 적었습니다. Monster Truck Madness 에서는 terrain 시스템이 있었는데 terrain을 구성하는 quad들이 하나의 텍스쳐를 참조했습니다. Blending 같은 기능이 없었으므로 terrain 은 여러 개의 작은 고유의 텍스쳐들로 만들어졌습니다. 길 모퉁이는 6x6 작은 텍스쳐들로 만들어졌고, 각각의 텍스쳐는 64x64 해상도 였습니다. 개발자들은 terrain이 자연스럽고 세밀해 보이길 원했습니다. (실제로도 그랬구요.) 커스텀된 바위 혼합물 세트가 몇몇 레벨들에 포함되었는데 이러한 혼합물 세트는 많은 텍스쳐 배치들로 나누어져 재사용이 어려웠습니다. (역자 : batch는 draw call과 관련 되어 있는 용어입니다. draw call을 호출 할 때 필요한 데이터의 세트를 batch라고 합니다.) 예를 들면, 아래는 90도 코너가 어떻게 나누어지는지 보여줍니다.

http://storage.googleapis.com/wzukusers/user-22455410/images/57f850cf1c3f13yNwhWJ/MTM2ED.jpg

이 텍스쳐들은 원래 하나의 큰 고유의 텍스쳐가 잘려서 작은 텍스쳐가 된 것으로, 텍스쳐 filtering에 문제를 일으킵니다. 만약 텍스쳐를 64x64 사이즈로 단순히 나누어 버리면 필터링시 필터링의 경계선 (seam)이 텍스쳐 사이에 생기게 됩니다. 각각의 텍스쳐는 필터링시 경계 부분에서 이웃 텍스쳐의 텍셀을 사용할 수 없습니다.

(역자 : 이웃하는 텍스쳐가 서로 연속하는 부분이 아니므로 filtering(가우시안 blur 등과 같은 convolution) 시 edge 부분에서 옆의 텍스쳐의 텍셀을 사용하면 안 된다는 이야기 )

개발자들은 만족스러운 해결방법을 만들어 냈습니다. 텍스쳐를 쪼갤 때 이웃 텍스쳐의 2 texel 을 반복하도록 만들었습니다. 이 방법은 유효 해상도를 64 에서 60으로 감소시킵니다. 아래는 monster truck madness 2의 텍스쳐 2장을 확대한 것입니다. 각각의 텍스쳐는 64x64 사이즈이지만 60x60 부분인 파란 정사각형 안쪽 부분만 고유(unique)합니다. 외곽의 2 texel 부분은 이웃하는 텍스쳐로 부터 채워집니다.

http://storage.googleapis.com/wzukusers/user-22455410/images/57f846b21bbacmjmaMBb/mtm2_tex.gif

UE4에서도 이와 같은 해결방법을 사용하기가 간단하고 비용이 저렴합니다.

Method And Limitation

기본 아이디어는 subUV의 각 작은 텍스쳐의 경계를 padding 시키는 것입니다. 이 경계를 몇 픽셀로 정할 지는 seamless한 mip map 이 몇 개가 필요한지에 따라 결정됩니다. 예를 들어 각 모서리에 1개 pixel만 border로 사용하면 (total 2 pixel) 첫 번째로 생성되는 mip map 만 seamless하게 됩니다. Mip 1 과 그 위의 mip map들에는 seam이 생길 수 있습니다. 2개 pixel (total 4 pixel) 을 사용하면 두 번째 mip map 까지, 4개 pixel (total 8 pixel) 을 사용하면 세 번째 mip map 까지 가능합니다. mip map을 한 번 만들 때 마다 size가 1/2가 되므로 이렇습니다.

실험적으로, 처음 세 개의 mip까지 seamless하게 만드는 것이 좋은 타협점으로 보입니다. 이 이상으로 seamless하게 만들 경우 소비되는 pixel이 너무 과도하며 실제로 보이는 seam은 눈치채기 어려울 정도로 작습니다.

The Math

Padding을 주면서 Texture 저장하기

Padding을 만들기 위한 수학은 직관적입니다. 텍스쳐를 인코딩할 때 UV를 다음과 같이 조정합니다. (UV에 UVScale을 곱합니다)

UVScale = (EdgePadTexels + TexelsPerFrame) / TexelsPerFrame

이렇게 UV를 조정하는 것은 추가되는 texel을 우측 하단에 전부 집어 넣게 됩니다, 하지만 우리가 원하는 것은 절반은 반대편 모서리에 넣는 것입니다. 이렇게 하기 위해서는 추가되는 사이즈의 절반 만큼 offset을 주면 됩니다.

UVOffset = -0.5 * EdgePadTexels * (1 / TexelsPerFrame ) * TilingPerFrame

TilingPerFrame 항목은 Volume textures에서 나중에 참조하기 위해 두었습니다. subUV에서는 이 항목은 1로 고정되어서 계산시 고려할 필요가 없습니다.

머터리얼 노드로 표현했을 때, 단일 frame이 subUV animation으로 encoding되는 것을 보여줍니다. 이 경우, TextureRes와 Frames가 단일 frame 텍스쳐를 이용해 새롭게 생성되는 subUV 텍스쳐를 위한 것이라는 것을 주의하셔야합니다. (역자 : 여러 고유 텍스쳐로 만들어진 큰 텍스쳐를 입력으로 넣는게 아니라, 작은 텍스쳐 하나 하나를 넣어야 한다는 것을 강조하는 것 같습니다.)

http://storage.googleapis.com/wzukusers/user-22455410/images/57f8585aaefa2hGS11mn/graph.gif

subUV 텍스쳐의 frame 들을 보면 각 frame들이 조금 축소되고, 추가된 부분에서 반복이 보이는 것을 볼 수 있습니다. (!. frame과 tile이라는 용어를 너무 혼재해서 쓰는 것 같은데 다시 읽어봐야 할 듯)

http://storage.googleapis.com/wzukusers/user-22455410/images/57f8480f8016dgFyf4yw/padpixels.gif

Padding 준 Texture 읽기

모서리 부분에 padding을 준 texture를 읽기 위해서는 encoding할 때 해주었던 일의 반대의 작업을 해주어야 합니다.

UVScale = TexelsPerFrame / (TexelsPerFrame + EdgePadTexels)

그리고 전체 크기의 절반의 offset을 더해 줍니다.

UVOffset = 0.5 * EdgePadTexels * (1 / TexelsPerFrame )

SubUV_Function 노드를 사용하면 아래와 같이 됩니다.

http://storage.googleapis.com/wzukusers/user-22455410/images/57f85f8de9382Zn4MXaC/SubUVGraph.gif

SubUV_Function 노드에 새로운 입력 부분이 있는 것을 주의해야 합니다. :Seamless UVs. 일반적인 UV 입력에 들어가는 타일링 인자랑 같은 UV 입력입니다. 크기를 조절하는 부분이 frac 노드 이전, Tiling Per UV 스칼라 값이 있는 부근에 자리를 잡는 것을 주의하세요.

이제 남은 일은 SubUV_Function 노드에 추가한 Seamless UV 입력을 사용하는 일입니다. SubUV_Function의 두 Texture Sample node를 선택해서 MipValueMode를 Derivative로 설정해주세요. 이는 hlsl에서 ddx와 ddy를 명시해서 사용하는 SampleGrad 명령과 같습니다. ddx와 ddy 노드를 추가하여 Texture Sample 노드에 연결해주세요.

http://storage.googleapis.com/wzukusers/user-22455410/images/57f86071778ba9OLXMgo/ddxddy2.gif

이제 우리는 seamless한 타일링 subUV 머티리얼을 생성하고 샘플링 할 수 있습니다. 기존의 결과에서 볼 수 있었던 seam부분을 subUV에서 확인하면 seamless한 것을 확인할 수 있습니다.

What’s Next

다음 부분은 이 방법을 Volume texture에 적용하는 부분입니다.

http://storage.googleapis.com/wzukusers/user-22455410/images/57f84a2aed48fyJR4bkN/vol.gif

후기

… ? 문제 케이스를 reproduce 하지 못해서 이게 꼭 필요한 작업인가..? 싶었습니다. 일단 포스트에서 제시하는 texture가 설명에서 나오는 것 처럼 2048 size가 아니고 500 size로 제공됩니다. 이것 가지고 8x8로 나눠서 SubUV 애니메이션 시켜봐도 gif 같은 결과를 확인하기 어렵구요..

결국 Raymarching을 이용해서 Volume Texture를 Sampling 한다라는 큰 주제면에서 그렇게 많은 비중을 차지하는 부분은 아닌 것 같습니다. 일단 넘어가도록 하려구요.

문제가 있거나, 질문이 있으시다면 코멘트 달아주세요!

--

--

No responses yet