[KOR][30]Terrain-Tiling-Break-Up---Terrain-Shader

https://m.youtube.com/watch?v=qPmKFRUf-zM&pp=ugUHEgVlbi1VUw%3D%3D

Frame at 3.22s

오늘 우리는 terrain에서의 texture tiling artifact를 해결하는 방법에 대해 이야기하겠습니다. 시작하겠습니다.

Frame at 13.81s

현재 저희는 terrain shader 제작 시리즈 영상의 중간 지점에 있습니다.

Frame at 19.76s

지난주에는 개별 terrain layers를 결합하여 완전한 shader를 만드는 방법을 보여드렸습니다. 전체 시리즈의 재생 목록 링크는 설명란에 게시하였으니, 다시 시청하고 싶으신 분들은 참고하시기 바랍니다. 지난주 영상 말미에는 저희 결과물을 살펴보았습니다.

Frame at 37.81s

문제가 좀 있었다고 말씀드렸습니다.

Frame at 42.16s

이곳에서 보시다시피, Material이 반복되어 부자연스럽게 보입니다. 이를 해결하는 몇 가지 방법을 보여드리겠습니다. 텍스처 Tiling을 깨뜨리기 위해 셰이더에 추가하는 모든 것은 렌더링 비용을 증가시킨다는 점을 명심하십시오. 공짜 해결책은 없습니다. 따라서 이전 시간에 보여드렸던 기본적인 셰이더가 이미 플랫폼에 너무 복잡하다면, 셰이더로 해결하려 하기보다 다른 방법을 고려하시는 것이 좋습니다.

예를 들어, 지금 보고 계시는 것처럼 Material이 장거리에 걸쳐 반복되는 넓은 영역을 피함으로써 Tiling을 깨뜨릴 수 있습니다. 각 레이어가 다른 속도로 Tiling된다면, 레이어 블렌딩 자체를 활용하여 Tiling을 분산시킬 수 있습니다. 그리고 한 Material이 반복되는 곳에 다른 Material의 일부를 칠하여, 제가 이곳에서 보여드린 것처럼 다른 Material이 기본 Material을 깨뜨리는 효과를 낼 수 있습니다.

이렇게 하면 셰이더 비용을 추가로 지불하지 않고도 Tiling 아티팩트를 효과적으로 제거할 수 있습니다. 또한, Tiling 아티팩트를 깨뜨리는 다른 방법도 있습니다.

Frame at 139.03s

수풀과 나무로 지형을 덮었습니다.

Frame at 142.08s

이와 유사한 맥락으로 이해할 수 있습니다.

Frame at 147.00s

이곳에서는 지형에 다른 요소가 없기 때문에 반복되는 artifact가 매우 두드러집니다.

Frame at 153.35s

이곳으로 옮겨오시면, 이러한 ferns, rocks, trees를 볼 수 있습니다.

Frame at 157.13s

'상당히 많은 추가적인 복잡성이 더해졌습니다.'

Frame at 161.79s

장면의 다른 여러 요소들 덕분에 타일링(tiling) 아티팩트(artifact)가 보이지 않습니다.

Frame at 168.75s

terrain의 장식이 충분히 복잡하여 tiling을 숨길 수 있다면, fancy tile breakup을 하지 않는 shader로도 문제가 없을 수 있습니다.

Frame at 185.17s

타일링 아티팩트(tiling artifacts)를 줄이기 위한 시스템을 셰이더(shader)에 추가하고자 하신다면, 이제부터 해당 작업을 진행하겠습니다.

Frame at 192.56s

지난주에 제작한 셰이더입니다. color 및 roughness texture를 샘플링하고, normal, occlusion, height texture도 샘플링하는 것을 보실 수 있습니다. 이후 이 값들을 unpack하여 전달합니다. 네 가지 간단한 레이어로 제작한 terrain material도 있습니다. 내장 함수를 사용하여 이 레이어들을 블렌딩합니다.

이제 간단한 layer material function에 기능을 추가하여 tiling을 깨뜨릴 수 있습니다. Content Drawer로 이동하여 simple layer material function을 복사하여 붙여넣겠습니다. 이름은 rotation layer로 변경할 것입니다. 이 기법에 부여한 이름입니다.

이어질 내용은 재질의 tiling을 깨뜨리기 위해 수행할 작업을 시각적으로 보여주는 것입니다.

Frame at 269.45s

장면으로 돌아와, 씬에 몇 개의 plane을 추가하도록 하겠습니다.

Frame at 275.73s

여기 두 개의 plane을 보실 수 있으며, 제가 이것들을 생성한 이유는 귀하께

Frame at 287.81s

이 기법이 어떻게 작동하는지 간단한 시각적 설명을 드리겠습니다. 아래쪽 평면은 현재 테레인에 적용된 material을 나타냅니다. 위쪽 평면은 동일한 material의 두 번째 텍스처 샘플을 나타냅니다.

먼저 현재처럼 material을 한 번 샘플링합니다. 이후 텍스처를 다시 샘플링하되, 이번에는 UV 좌표를 90도 회전시킬 것입니다. 따라서 아래쪽 버전은 일반 버전이며, 그 위 버전은 90도 회전된 버전이 됩니다.

이 두 평면, 즉 두 세트의 텍스처 샘플을 mask를 사용하여 블렌딩할 것입니다. 빠르고 시각적인 설명을 위해 이 과정을 보여드리고자 합니다.

Frame at 340.41s

그곳에서 무슨 일이 일어나는지 볼 수 있었습니다. 자, 여기 우리의 rotation layer가 있습니다.

Frame at 348.50s

텍스처 샘플링을 두 번 수행해야 합니다. 셰이더에서 텍스처를 언패킹하는 부분을 옮기겠습니다. 첫 번째 텍스처 샘플을 복제하여 두 번째 샘플을 생성합니다. 같은 텍스처 에셋을 샘플링하되, 좌표는 Swizzle 노드를 사용하여 YX로 변경합니다. 이는 XY 좌표를 YX로 바꾸면서 텍스처를 90도 회전하는 간단한 방법입니다.

첫 번째 텍스처 샘플(RGBA)과 회전된 두 번째 텍스처 샘플을 Lerp 노드로 블렌딩할 것입니다. NOH 텍스처를 위한 두 번째 샘플을 먼저 생성하겠습니다. 텍스처 샘플을 복사하여 붙여넣고, Swizzled UV 좌표와 NOH 텍스처를 연결합니다. 첫 번째와 두 번째 샘플을 Lerp 노드로 블렌딩합니다.

이제 분리해야 합니다. Base Color는 그대로 연결하고, Alpha 채널은 Component Mask 노드를 사용하여 추출합니다. 추출된 Alpha는 Set Material 노드의 Roughness에 연결됩니다. Color와 Roughness 설정이 완료되었고, Normal, Occlusion, Height를 설정하겠습니다. Normal에는 RG 채널, Ambient Occlusion에는 B 채널을 사용합니다. Alpha 채널은 Component Mask 노드를 사용하여 Height에 연결합니다. Color, Roughness, Normal, Occlusion, Height 모두 두 텍스처 샘플 간 블렌딩이 완료되었습니다.

마지막으로, 두 가지 다른 머티리얼(일반 텍스처와 90도 회전된 텍스처)을 블렌딩하는 데 사용할 마스크 텍스처를 샘플링합니다. Blend Mask라는 이름의 Texture Input 2D를 생성하고 텍스처 오브젝트를 연결합니다. 기본 텍스처로 'masks'라는 이름의 텍스처를 사용합니다.

Frame at 646.29s

이것을 열어보면 red, green, blue 채널을 살펴볼 수 있습니다. 현재 다양한 크기의 mask들이 준비되어 있습니다. red mask는 비교적 큰 편입니다.

Frame at 658.37s

중간 채널(medium mass or the green channel)은 중간 정도의 마스크(mask)를 가지고 있으며, 파란색 채널(blue channel)은 작은 마스크를 가지고 있습니다. 따라서 파란색 채널을 사용하는 것이 좋겠습니다. 물론 원하는 마스크 텍스처(mask texture)를 직접 생성할 수도 있지만, 이 경우에는 파란색 채널을 사용하겠습니다.

Frame at 678.49s

저희는 mask를 샘플링하고 이를 `NOH` 텍스처 및 color, roughness 텍스처 간의 블렌딩에 사용할 예정입니다. `blue channel`을 사용하여 두 가지 샘플을 혼합합니다.

UV 좌표를 위한 `multiplier`를 생성해야 합니다. `absolute world position`을 사용하여 UV를 스케일링합니다. 이 `input scale` 값을 `blend mask multiplier`로 사용할 것입니다.

이 `blend mask multiplier`로 UV 좌표를 나누어 `blend mask scale multiplier`를 생성합니다. 기본값을 설정하고 텍스처 스케일에 맞춰 마스크 스케일도 자동으로 조정되도록 합니다.

이제 씬에 셰이더를 적용하고 mask 스케일을 조정할 차례입니다. `terrain shader`에서 `simple layer material function`을 `rotation layer`로 교체합니다. `rotation layer`는 더 많은 입력을 가집니다.

`mossy texture`를 `rotation layer` 버전으로 교체하고 `height`도 연결합니다. `simple layer`는 삭제합니다. `material function` 자체에 `blend mask`를 추가했으므로 여기서는 추가할 필요가 없습니다.

`texture object`에 대해 `use preview value as default`를 체크하여 `mask texture`를 기본값으로 사용합니다. 다른 마스크를 전달할 수도 있지만, 여기서는 비워둡니다. 마지막으로 `blend mask scale multiplier`를 `float` 값 1로 설정합니다.

Frame at 966.39s

Multiplier 값이 1로 설정되었음에도 불구하고, 추가한 기법이 상당히 좋은 결과를 보여주는 것을 확인할 수 있습니다. 이전보다 artifacting이 줄어들었습니다. 멀리 있는 장면에서는 반복이 보이기 시작하지만, 가까운 전경에서는 반복이 덜 발생하는 것을 볼 수 있습니다. Normal Map을 회전시킬 때 특별한 처리가 필요하다는 점을 방금 깨달았습니다. 셰이더로 돌아가 수정하겠습니다. 현재 Color와 Roughness 값을 확인하고 있습니다.

Frame at 1018.34s

샘플을 사용하여 normal, occlusion, height 데이터를 가져옵니다. 이곳에서 normal 데이터가 출력됩니다. 두 번째 샘플 데이터를 첫 번째 샘플과 결합하기 전에, 데이터를 red, green, blue, alpha 순서에서 green, red, blue, alpha 순서로 변경하는 swizzle 작업이 필요합니다.

swizzle 노드에 red, green, blue를 입력하여 green, red, blue, alpha 순서로 출력되도록 설정합니다. alpha 채널을 붙여 lerp 노드로 전달합니다. 이렇게 하면 normal 처리가 완료됩니다.

이제 mask texture를 시각화할 차례입니다. mask의 blue 채널을 샘플링하고, 디버깅을 위해 임시로 height 출력으로 사용합니다. 셰이더로 돌아와 height 값을 base color에 직접 연결하여 terrain에 mask가 어떻게 적용되는지 확인합니다.

Frame at 1119.89s

이것이 두 가지 다른 재질 레이어(material layer) 버전 간의 블렌딩(blending)에 사용하는 마스크(mask)입니다.

Frame at 1126.89s

이와 같은 debug view를 활용하여 원하는 정확한 scale을 얻을 수 있습니다.

Frame at 1134.05s

이제 지형(terrain) 레이어로 전환하겠습니다. multiplier가 15.97로 설정된 것을 확인하실 수 있습니다. 만약 이 multiplier를 방금 전과 같이 1로 설정한다면,

Frame at 1147.99s

저희가 준비한 material에 비해 이 부분이 너무 작다는 것을 확인하실 수 있습니다.

Frame at 1152.52s

이 두 레이어를 블렌딩하여 타일링을 깨뜨리는 값을 찾고자 합니다.

Frame at 1165.79s

15.97이라는 값을 사용하겠습니다. 이 값은 상당히 적절하다고 생각하는데, 왜냐하면

Frame at 1175.00s

레이어가 한두 번 반복되는 것을 보실 수 있으며, 그 후에 전환됩니다.

Frame at 1183.64s

회전된 버전은 괜찮습니다. 이제 색상을 다시 원래대로 되돌리도록 하겠습니다.

Frame at 1190.80s

결과물을 살펴보겠습니다. 여기에서 보시는 것처럼

Frame at 1196.46s

재질(material)의 두 번째 버전을 샘플링하고 90도 회전시킨 후 마스크(mask)로 다시 블렌딩(blending)하여 타일링(tiling)을 상당히 효과적으로 해소하였습니다. 다만, 이 방법으로는 먼 거리의 타일링을 완전히 해결하지는 못하며, 멀리 볼 경우 여전히 타일링이 보입니다. 하지만 가까이서는 표준 버전과 회전된 버전의 재질을 블렌딩하는 방식을 사용하여 재질 타일링을 줄일 수 있습니다.

Frame at 1235.72s

그래요, 이건 material tiling을 분산시키는 꽤 멋진 기법입니다.

Frame at 1241.52s

이제 Unity로 전환하여 유사한 작업을 수행하는 방법을 보여드리겠습니다.

Frame at 1247.84s

네, Unity에서 진행 중입니다. 지난주 댓글에서 height-based blending이 전혀 작동하지 않는다는 의견이 있었습니다. 현재 height-based blending이 적용되지 않고 부드러운 블렌딩만 보입니다. 이를 확인한 결과, 문제가 무엇인지 파악했습니다. 셰이더를 살펴보겠습니다. 타일 분할 작업을 시작하기 전에 이 문제를 신속하게 해결하겠습니다.

Frame at 1280.20s

여기서 셰이더를 살펴보면, 하단에 'Height-Based Splat Modify'라는 노드가 있음을 알 수 있습니다. 이 노드는 Unity 6.3 버전에 포함되어 있습니다. 이 노드를 열어 자세히 살펴보겠습니다.

이곳에서 'height transition value'라는 이름의 커스텀 함수 노드를 확인할 수 있습니다. 이 노드가 어떤 작업을 수행하는지 열어서 살펴보겠습니다.

생성된 코드를 보면, 우리는 기본적으로 'height transition'이라는 값을 얻으려고 합니다. 이 값은 Material에서 가져오는 값입니다.

Frame at 1325.19s

씬의 material을 살펴보면, 'height transition'이라는 material에 해당 값이 없는 것을 확인하실 수 있습니다. 이 node는 terrain material에 해당 값이 자동으로 추가될 것이라는 사실에 의존하고 있었으나, 현재 그 값은 존재하지 않습니다.

Frame at 1343.80s

기존의 height-based splat modify 기능을 수정하여 해당 값을 직접 추가해야 합니다. Blackboard를 열어 'height sharpness'라는 새로운 파라미터를 추가하겠습니다. Inspector로 이동하여 'Promote to Final Shader'를 선택하고 'Show in Inspector'를 체크하여 Material에서 값을 노출시킵니다. Mode를 Slider로 설정하고 0과 1 사이의 값을 조정할 수 있도록 합니다. Blackboard를 닫고 Inspector를 보면, 키워드 기반으로 height-based blending을 비활성화하는 값이 있습니다. 하지만 이 키워드가 제대로 작동하지 않는 것으로 보이므로, 해당 노드를 우회하여 삭제하겠습니다. 이 노드는 height-based blending을 켜고 끄는 역할을 하지만, 현재는 항상 꺼진 상태만 반환되는 것으로 보입니다. 이를 삭제함으로써 해당 기능을 건너뛰게 됩니다.

Frame at 1431.17s

이제 Material로 이동하여 height sharpness 값을 조정하여 terrain blending을 조절할 수 있습니다. 슬라이더를 움직이면 height sharpness가 작동하는 것을 볼 수 있습니다. 이 기능을 만들었지만 그래프에 추가하는 것을 잊었습니다. Blackboard에 있는 height sharpness를 그래프로 드래그하고, height transition 대신 height sharpness 값을 전달합니다. 이제 height sharpness 슬라이더를 사용하여 transition을 부드럽게 blending하거나 sharp하게 만들 수 있습니다. 슬라이더를 내리면 height blending에 100% sharpness가 적용되며, 높이면 부드럽게 blending됩니다. 이는 지난주에 원했던 결과이며, height-based splat modify node가 올바르게 설정되지 않아 얻지 못했습니다.

이제 tiling artifacts를 제거하기 위한 blending을 시작할 준비가 되었습니다. simple layer node, simple layer subgraph를 선택합니다. 이 subgraph를 복사하여 rotation layer라고 이름을 변경합니다. subgraph를 열어 작업을 시작합니다. subgraph에서 unpacking하는 부분을 보면, 좀 더 공간을 확보하기 위해 이동시킵니다. Unreal에서 했던 것처럼 texture sampling을 두 번 해야 합니다. 두 번째 layer, 즉 sample texture node를 복사합니다. 두 번째 버전은 coordinate의 X와 Y를 swap해야 하므로 swizzle node를 추가합니다.

Frame at 1571.40s

XY 좌표계를 YX로 변경하여 텍스처 샘플러를 설정하고, 두 샘플 간의 보간(lerp)을 위해 lerp 노드를 추가합니다. RGBA 값을 전달받아 color와 smoothness 채널을 분리하여 pack material subgraph와 output에 연결합니다. Alpha 채널은 swizzle 노드를 사용하여 분리하고 smoothness에 연결합니다.

NOH 텍스처 역시 동일한 방식으로 처리합니다. 텍스처 샘플러를 복사하고 swizzled 텍스처 좌표를 전달합니다. RGBA 값들을 보간하기 위해 lerp 노드를 추가하고, Normal 맵 회전을 위해 XY 좌표를 YX로 변경하는 swizzle 노드를 사용합니다. 보간된 값에서 Normal 맵을 위해 RG 채널을, Occlusion을 위해 B 채널을, Height를 위해 Alpha 채널을 추출합니다.

마스크 텍스처 샘플러를 생성하기 위해 Blackboard에 Texture 2D 타입의 blend mask 파라미터를 추가합니다. 기본값으로 masks texture를 할당합니다. 마스크 텍스처의 사용할 채널을 지정하기 위한 dropdown인 mask channel을 생성하고, Red, Green, Blue, Alpha 채널을 옵션으로 설정하며 기본값은 Blue 채널로 지정합니다.

생성한 dropdown을 샘플러에 연결하여 Red, Green, Blue, Alpha 채널 중 하나를 마스크로 선택할 수 있도록 합니다. 이 마스크 채널의 결과값을 사용하여 두 개의 다른 샘플(noh texture, cs texture) 간의 보간을 제어합니다.

마스크 텍스처의 타일링을 조절하기 위해 mask scale multiplier라는 Vector 2 타입의 파라미터를 추가하고, Blue 채널 마스크에 적합한 값인 15.97을 할당합니다. 텍스처 좌표를 mask scale multiplier로 나누어 마스크 샘플링에 사용합니다.

이 과정을 통해 텍스처 tiling artifact를 줄이기 위해 color와 smoothness를 두 번 샘플링하고, normal, occlusion, height를 두 번 샘플링하여 각기 다른 타일링 값을 가진 마스크를 기반으로 보간합니다.

마지막으로, 생성한 rotation layer subgraph를 terrain shader에 적용합니다. 네 개의 단순 레이어 중 하나 이상을 더 복잡한 version으로 교체할 수 있습니다. 모든 레이어에 해당 subgraph를 적용할 필요는 없으며, tiling이 심한 텍스처에만 적용하는 것이 효율적입니다.

Frame at 1983.10s

이 튜토리얼에서는 텍스처 타일링 문제를 해결하기 위해 간단한 레이어를 보다 복잡한 회전 레이어로 교체하는 방법을 보여드리겠습니다. 특히 모스(moss) 레이어에서 타일링 아티팩트가 가장 두드러지게 나타날 것으로 예상됩니다.

모스 레이어를 더 칠하여 타일링 아티팩트를 명확히 확인한 후, 'Fells Mossy' 레이어를 선택하고 넓은 영역에 모스를 칠해 반복적인 패턴을 관찰합니다. 현재 이 머티리얼은 상당히 반복적입니다.

이 간단한 레이어를 회전 레이어로 교체하기 위해, 'terrain sample'에서 해당 부분을 더 복잡한 버전으로 변경합니다. 'layer two'로 설정하고 머티리얼을 패스합니다. 'sampler state'를 연결하고 기본 마스크 및 스케일 멀티플라이어가 설정되었으므로 추가 작업은 필요하지 않습니다. 'height blending'에도 패스해야 합니다.

간단한 레이어를 회전 레이어로 성공적으로 교체했습니다. 저장 후 씬으로 돌아가면 타일링 아티팩트가 크게 줄어든 것을 확인할 수 있습니다. 머티리얼을 반복적으로 사용했기 때문에 여전히 타일링이 보이지만, 확대해보면 이전보다 타일링이 감소한 것을 알 수 있습니다.

앞으로는 멀티플라이어 값을 조정하여 더 나은 결과를 얻을 수 있습니다.

Frame at 2129.50s

문제를 발견하였습니다. Blend Mask input을 생성하였으나, Sampler에 연결하지 않아 잘못된 샘플링이 발생하고 있었습니다. 이 문제를 파악하기 위해 Mask Channel을 Height로 출력하고, 이 Height 값을 직접 출력하여 색상으로 표시하는 방식을 사용했습니다. 이제 이 부분을 수정하겠습니다.

Frame at 2161.23s

이전 방식으로는 더 이상 mask를 출력하지 않습니다. 하지만 mask를 올바르게 sampling하게 되면서 texture tiling이 훨씬 효과적으로 해소되었습니다. 이 결과가 매우 만족스럽습니다.

value 값을 15.97로 정확히 설정합니다. 이제 tile breakup 효과가 제대로 적용되어 이전보다 훨씬 나은 결과를 얻었습니다.

현재 textures를 두 번 sampling하고 있습니다. 하나는 회전된 버전이고, 다른 하나는 일반 버전이며, mask texture를 기반으로 두 texture를 blend합니다. 이 기법은 repetition 문제가 있는 layer에만 적용하면 효과적입니다.

예를 들어, 해당 dirt layer는 단순한 방식을 사용하지만, contrast가 강한 detail이 적기 때문에 texture tiling이 눈에 띄지 않습니다.

Frame at 2244.22s

이 Moss Layer처럼 필요에 따라 기술을 적용하는 것이 중요합니다. 모든 것에 이 기술을 그대로 적용하는 것은 지양해야 합니다. 꼭 필요한 곳에만 적용하십시오.

이번 영상은 여기까지입니다. 시청해주셔서 감사합니다. 다음 주에는 텍스처 타일링(texture tiling)을 깨뜨리는 또 다른 기법을 보여드리거나, 오늘 보여드린 기법과 달리 더 적은 텍스처 샘플(texture samples)을 사용하는 효율적인 지형(terrain) 제작 기법을 개발하여 보여드릴 수 있습니다.

어떤 기법을 보고 싶으신지 댓글로 알려주시면 다음 주 영상으로 준비하겠습니다. 시청해주셔서 감사하며, 즐거운 한 주 보내시기 바랍니다.