https://m.youtube.com/watch?v=0L5Azq6ugyo

오늘은 언리얼 엔진과 유니티에서의 레이어 및 블렌딩을 포함한 테라인 셰이더 구조에 대해 알아보겠습니다.
지금까지 텍스처 패킹과 간단한 레이어 셰이더 생성에 대해 다루었습니다. 이번 영상에서는 지난번 다루었던 내용에 이어 테라인 셰이더의 구조를 살펴보겠습니다.
먼저 언리얼 엔진에서 시작하여 곧 유니티로 전환할 예정입니다. 셰이더 구조를 살펴보기 전에, 지난 영상에서 빠뜨렸던 스페큘러(specular)에 대해 간략히 짚고 넘어가겠습니다.

이전 시간에 제작한 terrain shader에서 메인 노드의 specular 입력 값을 보면 기본값 0.5로 설정되어 있습니다. 이 값은 재질 표면의 반사 강도를 조절합니다. 0.5는 약 4%의 빛이 반사됨을 의미합니다. 빛이 표면에 닿을 때 약 4%가 되돌아오는 것입니다. 이는 비금속 재질의 약 90%에 해당하는 일반적인 반사율입니다.
specular 값을 0으로 설정하면 미리보기 창에서 객체가 더 어두워지는 것을 확인할 수 있습니다. 이는 모든 반사가 제거되고 확산광(diffuse light)만 반사되며, specular light나 반사광은 0으로 설정되었기 때문입니다. 기본값 0.5에서는 하늘의 푸른빛이 재질에 반사되는 것을 볼 수 있습니다. 값을 0으로 바꾸면 반사가 사라져 검게 보입니다. 이는 ambient occlusion과 유사하지만, 확산광 대신 specular light나 반사광을 차단하는 역할을 합니다.
기본값 0.5에서 specular 값을 1로 올리면 빛 반사율이 8%로 매우 높아집니다. 실제로 이 정도로 반사하는 객체는 드물며, 대부분의 객체는 0.5 주변의 값을 가집니다.
specular 값을 그대로 두거나 0.5의 하드코딩된 값으로 유지하면 재질은 문제없이 작동하며, 약간 더 저렴하게 구현할 수 있습니다. 하지만 언리얼 엔진에서 더 정교한 효과를 위해 specular 값을 specular occlusion에 활용할 수 있습니다. 이를 위해 cavity map을 사용할 수 있습니다. 몇 주 전 Substance Designer를 사용하여 텍스처를 패킹하는 작업을 진행했습니다.

본 섹션에서는 Material의 텍스처를 두 개로 줄이는 방법을 보여드립니다. 현재 Color, Normal Map, Roughness 맵과 함께 Cavity 맵을 사용하고 있습니다. Cavity 맵은 Material의 모든 갈라진 부분에 어두운 값을 나타내며, 빛이 닿기 어려운 영역을 표현합니다. 이는 Ambient Occlusion과 유사하지만, Ambient Occlusion이 Diffuse Light에 대한 것이라면 Cavity 맵은 Specular 또는 Reflected Light에 적용된다는 차이가 있습니다.
Cavity 맵과 Ambient Occlusion 맵을 사용하여 두 개의 텍스처를 생성합니다. Unreal Engine에 적용되는 Make CRNOH 노드를 살펴보면, Cavity 맵은 Specular Level로, Ambient Occlusion 맵은 해당 노드로 입력되는 것을 확인할 수 있습니다. Blend 노드는 Multiply로 설정되어 있습니다.

우리는 cavity map과 specular level map을 함께 곱합니다. 이 두 가지를 NOH texture의 blue channel에 넣습니다. shader에서는 다시 분리하기만 하면 됩니다. 이는 매우 쉬운 작업입니다.

언리얼로 돌아가겠습니다.

Simple Layer shader를 열면 NOH texture를 샘플링하는 것을 볼 수 있습니다. 파란색 채널에는 ambient occlusion과 cavity map이 곱해져 있습니다. 이것이 ambient occlusion 슬롯으로 전달됩니다.
specular 슬롯을 사용하려면 crevice map을 분리해야 합니다. 간단히 cheap contrast 노드를 추가하여 contrast 값을 2로 설정한 후 0.5를 곱합니다. 이 값은 specular의 기본값이었으며, reflection이 과도하게 밝아지지 않도록 crevice map을 0.5로 곱합니다.
ambient occlusion과 crevice map을 곱하고 cheap contrast를 2로 설정한 뒤 0.5를 곱하면 specular level이 됩니다. 이제 이를 위한 output을 생성해야 합니다.
set material attributes 노드에서 specular 입력을 추가하고 specular 값을 전달했습니다. 또한 output specular이라는 새로운 출력을 생성하여 값을 전달했습니다. 순서를 올바르게 지정하기 위해 각 입력의 순서를 0부터 7까지 설정했습니다.
이제 Simple Layer를 저장할 수 있으며, shader로 돌아오면 specular 입력을 연결할 수 있는 specular 출력이 표시됩니다. 결과는 매우 미묘합니다.

이 장면을 전혀 볼 수 없을 것이지만, 우리의 씬으로 다시 전환할 것입니다.

이리로 오셔서 제가 무엇을 할지 살펴보십시오. 셰이더 탭을 분리하여 specular와 specular 없음 사이를 전환할 수 있도록 하겠습니다. 제 terrain을 보시면 여기서 분리하겠습니다.

specular 값을 저장하고 적용해 보았으나, 큰 차이를 느끼지 못했습니다. 하지만 specular를 다시 연결하면, 재질의 작은 틈새나 표면에 반사가 생기는 것을 방지할 수 있습니다. 셰이더를 더 가볍게 만들고 싶다면 이 부분을 생략해도 되지만, specular 표현에 약간의 사실감을 더해줄 것입니다.
이 효과를 제대로 확인하기 위해 lit 메뉴에서 buffer visualization을 선택하여 비교해 볼 수 있습니다. 이를 통해 specular 값의 변화를 더욱 명확하게 파악할 수 있습니다.

Specular를 선택하면 방금 생성한 specular channel이 표시됩니다. 균열을 제외한 모든 영역에서 0.5의 값을 가지는 것을 볼 수 있으며, 균열 부분은 더 어둡게 마스크를 설정하여 반사가 일어나지 않도록 하였습니다. 이는 오브젝트가 self-occluding 되는 균열 및 틈새로 보입니다.
다음으로, 이어서 설명하도록 하겠습니다.

가장 기본적인 terrain shader의 구조를 살펴보겠습니다. 현재는 하나의 simple layer만 존재합니다. 이 simple layer를 네 개로 복제하여 네 개의 layer를 만들 것입니다. 이를 위해 simple layer node를 네 개 복사하고, 각 layer에 해당하는 texture object를 가져와야 합니다.
각 layer를 준비한 후에는 layer들을 함께 blend해야 합니다. simple layer material function을 만들 때, 모든 material parameter를 material input으로 전달하고, 별도로 output했습니다. set material attributes node를 사용하여 모든 input을 하나의 wire로 결합하며, 이는 output material에서 나옵니다. material parameter를 전달할 때는 이 output 하나로 처리합니다.
이제 landscape node를 생성할 것입니다. Unreal Engine에서는 landscape라는 용어를 사용하며, terrain과 혼용하기도 합니다. Unreal은 landscape를, Unity는 terrain을 선호하지만 의미는 같습니다. Unreal에서 terrain을 다루는 node는 landscape라는 이름으로 불립니다. landscape layer blend node를 생성하여 layer들을 blend할 것입니다.
먼저 layer의 개수와 타입을 정의해야 합니다. landscape layer blend node에서는 layer를 나타내는 array를 사용합니다. 현재 array는 비어 있습니다. landscape layer blend node를 선택한 상태에서 layer를 추가할 수 있습니다. 하나, 둘, 셋, 네 개의 layer를 추가합니다. 이제 네 개의 layer가 준비되었으므로, 각 layer에 terrain에 설정된 이름과 동일한 이름을 부여해야 합니다. 텍스트 문자열은 terrain의 이름과 일치해야 합니다.

제 테레인으로 전환하겠습니다. 이것은 제가 오랫동안 사용해 온 테레인입니다. 이전 튜토리얼에서 사용했던 이름들이 남아있습니다. 이 테레인이 가지고 있는 이름들을 살펴보겠습니다.

selection mode 대신 landscape mode으로 전환하겠습니다. 그러면 왼쪽 패널이 나타납니다. 탭이 'paint'로 설정되어 있는지 확인해야 합니다. 'sculpt'나 'manage'로 설정되어 있으면 작동하지 않습니다. 'paint'를 선택하고 아래로 내려보면 이미 여섯 개의 레이어가 설정되어 있는 것을 볼 수 있습니다. 이 레이어들의 이름은 Forest Roots, Forest Ground, Moist Stone, River Stone, Mossy Stone, Mossy Ground입니다. 이 이름들을 terrain에 있는 레이어 이름과 동일하게 Landscape Layer Blend 노드에 입력해야 합니다. 'forest ground' 레이어를 사용하겠습니다.

여기서 레이어 이름을 'forest ground'라고 입력하겠습니다. 레이어를 배치하는 순서는 중요하며, 가장 낮은 레이어부터 가장 높은 레이어 순서로 쌓아 올려야 합니다. 제 경우, 제 스트림 베드는 (가장 낮은 레이어에 위치할 것입니다).

이는 가장 낮은 레이어(lowest layer)가 됩니다. 따라서 어떤 이름이든 'Riverstone'으로 불리게 됩니다.

우선 첫 번째로 해당 기능을 사용하겠습니다. 다음으로는 Moiststone을 사용할 예정입니다.

여기 더 살펴보겠습니다. 'forest ground'이 아닌 'river stone'이 있습니다. 이어서 'moist stone'이 있고, 마지막으로 'mossy stone'이 있습니다. 이 'mossy stone'을 다음으로 다룰 예정입니다.

또한, 저는 최종 단계에서 이름들이 정확하게 일치하는지 확인하고자 합니다.

이끼 낀 지형을 사용하겠습니다.

다음으로 Blending 타입을 설정합니다. 첫 번째 레이어는 Base Layer이므로 LB alpha blend를 유지합니다. 나머지 레이어는 Height Blending을 사용하기 위해 LB height blend로 설정합니다. 이렇게 하면 Height Input이 활성화되어 해당 레이어의 Height 정보를 Layer Blend 노드로 전달할 수 있습니다. 모든 레이어를 LB height blend로 설정합니다.
Layer Blend 노드 설정 후, 각 레이어의 Material을 전달합니다. 첫 번째부터 네 번째 레이어의 Material을 순서대로 입력합니다. 이후 각 레이어의 Height 정보를 입력해야 합니다. 두 번째, 세 번째, 네 번째 레이어의 Height를 각각 연결합니다. 이 과정은 Landscape Shader 구성 시 레이어가 Layer Blend 노드를 거쳐 Material Output으로 나오기까지의 구조를 보여줍니다.
Material을 개별 Parameter로 분리하기 위해 Get Material Attributes 노드를 사용합니다. Array 옆의 '+'를 눌러 원하는 Attribute를 추가합니다. Base Color, Metallic, Specular, Roughness, Normal, Ambient Occlusion을 선택합니다. Anisotropy는 사용하지 않습니다.
각 Attribute를 해당되는 Material Parameter에 연결합니다. Color, Specular, Roughness, Normal을 연결하고, 마지막은 Ambient Occlusion으로 설정합니다. 이로써 모든 레이어를 Blending하고 Parameter를 Unpack하여 최종 Layer로 전달하는 구조가 완성되었습니다.
마지막으로 Texture Object들을 연결합니다. Stone Ground, Fels Mossy, Dry Soil, Rock Grass의 CR 및 NOH 텍스처를 각 레이어에 할당합니다. 흥미롭게도 각 레이어에 Scale 값을 추가로 입력했습니다. 다양한 Scale 값을 지정했으며, Landscape Texture Scale에 대한 독특한 기법을 적용했습니다.

이 텍스처들이 배치될 위치에 마스크를 미리 칠해 두었음을 보실 수 있습니다. 이는 환경 제작 튜토리얼이 아니므로 자세히 다루지는 않을 것이며, 주로 셰이더 제작에 집중할 것입니다. 보시는 바와 같이 스트림 아래에 바위들이 배치되어 있습니다.

그리고 여기 강둑에는 이런 식으로 진흙이 있고, 또 이어서 이런 식의 바위가 있습니다.

지형과 이끼가 어우러지는 자연스러운 블렌딩을 구현하고 있습니다.

바위는 물살이 흐르는 개울 바닥에 있으며, 가장자리에는 진흙과 약간 곰팡이가 낀 듯한 이끼 낀 바위가 있습니다.

그리고 이쪽 강둑의 이끼는 제가 작업했던 부분입니다.

제가 좋아하는 스케일을 형성하기 위해 이러한 수치를 조정하고 사용했습니다.

저는 사용자가 원하는 크기의 material을 얻기 위해 prime number를 활용했습니다. prime number를 사용하는 이유는 tile grid가 반복되지 않도록 하기 위함입니다.

레이어를 정렬하는 이유는 한 레이어와 다른 레이어 간의 블렌딩을 통해 하나의 레이어 타일링을 다른 레이어 타일링으로 덮어 가릴 수 있기 때문입니다.

레이어 페인팅이 이루어지는 부분에는 반복과 타일링 아티팩트가 훨씬 적게 나타납니다. 이는 각 레이어가 서로의 타일링을 깨뜨리는 방식으로 작동하기 때문입니다. 한 레이어의 타일링은 다른 레이어의 타일링으로 덮이며, 서로소인 소수를 사용하여 그리드가 정렬되지 않도록 합니다.

그리드의 크기를 조정하기 위해 소수(prime number) 목록을 구글에서 검색하여 원하는 스케일 값에 가장 가까운 소수를 찾았습니다. 300, 400, 600 범위의 값들을 사용했으며, 이 값들은 서로 다른 스케일과 그리드 크기를 가지면서도 소수이므로 서로 나누어 떨어지지 않아 절대 같은 선상에 놓이지 않습니다.
이러한 방식으로 테레인 셰이더의 전체 구조를 생성했습니다. 개별 레이어들을 만든 후, 'Landscape Layer Blend' 노드를 사용하여 이 레이어들을 혼합합니다. 그런 다음 'Material Attributes'를 분리하여 'Terrain Base' 노드로 다시 전달합니다. 매우 간단한 구조이지만, 이것이 가장 기본적인 테레인 셰이더의 형태입니다.

소재가 반복적으로 보이는 문제가 발생하며, 타일이 계속해서 반복되는 것을 확인할 수 있습니다. 다음 시간에는 이러한 tiling을 깨뜨리는 방법에 대해 논의할 예정입니다. 이를 위한 여러 방법이 있지만, 특정 한 가지 방법에 집중하여 살펴보겠습니다.

Unity로 전환하여 유사한 것을 만드는 방법을 보여드리겠습니다.

Unity에서 terrain shader structure를 살펴보겠습니다. 현재 Unity 에디터에서 해당 부분을 살펴보고 있습니다.

지난주에 생성한 레이어 서브그래프를 복제하여 여러 개를 만들고 레이어를 블렌딩하는 과정을 설명합니다. 각 레이어는 고유한 머티리얼을 가지며, 높이(height) 기반 블렌딩을 사용하여 다음 레이어와 혼합됩니다. Unity에서 높이 기반 블렌딩을 적용하기 위해 'height based splat modify' 노드를 사용하며, 텍스처의 높이 데이터를 준비하고 프리멀티플라이(pre-multiply)에 활용합니다.
레이어 마스크 텍스처를 가져와 샘플링하고, 이 마스크는 각 레이어의 높이 데이터와 결합되어 블렌딩에 사용됩니다. 준비된 블렌드 결과를 'pre-multiply layer' 노드를 통해 각 레이어별로 프리멀티플라이하며, 이후 'blend layers add' 노드를 사용하여 모든 레이어를 순차적으로 더해 최종 블렌딩을 완성합니다.
프리멀티플라이된 레이어들의 최종 결과물을 마스터 스택(master stack)으로 보내 트레인 셰이더를 완성합니다. 씬에 적용된 셰이더를 사용하기 위해 트레인에 여러 레이어를 추가하고, 페인팅 탭에서 원하는 레이어를 선택하여 칠할 수 있습니다.
트레인 설정 패널에서 컨트롤 텍스처 해상도를 높여 레이어 마스크의 해상도를 개선할 수 있습니다. 이를 통해 더 높은 텍셀 밀도로 페인팅이 가능해져 각 레이어를 원하는 위치에 칠할 수 있습니다.
마지막으로, 셰이더에서 발생하는 타일링(tiling) 현상에 대해 언급하며, 다음 시간에는 이를 개선하기 위한 다양한 기법을 다룰 예정임을 안내합니다. 이번 튜토리얼에서는 트레인 셰이더의 전반적인 구조, 레이어 관리, 높이 기반 블렌딩, 프리멀티플라이 및 가산 블렌딩 기법을 Unity와 Unreal Engine을 비교하며 설명했습니다.