TECHARTNOMAD TECHARTFLOW.IO

TECH.ART.FLOW.IO

[번역] UE5로 만드는 셀 셰이딩 6회: 엔진 수정 입문 (3) 셀 톤 라이팅 구현하기.

jplee 2024. 3. 5. 22:26

역자의 말.
SDF 로 얼굴 그림자 처리 하는 토픽 번역글에 이어서 실제 언리얼 엔진 코드를 수정 해서 툰 라이트 모델을 추가 하는 토픽을 번역 해 보려고 했어요. 
스파크 크리에이티브에서 근무 하시는 테크 어드바이저인 코바야시 아라타님의 정말 훌륭한 토픽 입니다.


원문

UE5でつくるセルシェーディング 第6回:エンジン改造入門(3)セル調のライティング実装

cgworld.jp

안녕하세요!!! 주식회사 스파크 크리에이티브의 고바야시입니다.

이번에는 셀톤으로 조정 및 수정한 라이팅을 구현해 보겠습니다. 내용은 지난번의 연장선상에 있지만, 지난번 구현 후 2개월 정도 시간이 흘렀습니다.

그 결과 일부 구현 방침을 잊어버린 부분이 있어, 뭔가 지난번과 다르지 않나? '라고 생각되는 부분은 이런 배경이 있다고 생각하고 넘어가 주시면 감사하겠습니다.

그럼 엔진 수정의 마지막 회를 진행해 보겠습니다.
 

조명 구현

4회와 같은 흐름으로 표현식별로 구현 방법을 소개합니다.

글에 담는 관계로 표현식별로 커밋을 나누었지만, 여러분들이 확인하실 때는 최신까지의 차이점을 일괄적으로 가져오는 것을 추천합니다.

이번과 같은 라이팅과 관련된 셰이더 파일은 한 번 작성할 때마다 약 1,200개 정도의 셰이더 컴파일이 발생하기 때문에 개발 환경이 취약한 경우 확인하는 것만으로도 수고가 많이 듭니다.

특정 표현이나 구현 방법만 확인하고 싶을 수도 있을 것 같으니, 그 부분은 자유롭게 하시면 됩니다. 다만, 컴파일에 시간이 소요될 수 있다는 점만 말씀드립니다.
 

마스터 및 인스턴스 머티리얼 생성

엔진 개조 마지막 회에 드디어 머티리얼이 등장했습니다. 이 먼 길을 돌아가는 느낌은 정말 엔진 모딩을 하고 있다는 것을 실감하게 합니다.

마스터 머티리얼(M_ToonBase)과 그 마스터 머티리얼에서 인스턴스 머티리얼(MI_ToonBase)을 생성합니다.

마스터 머티리얼(M_ToonBase)의 Shading Model은 당연히 Toon Lit으로 합니다.

이제 인스턴스 머티리얼(MI_ToonBase)에서 인스턴스 머티리얼을 생성해 주면 준비 완료입니다.

이번에는 타마모라는 모델 한 개만 사용하므로 이 정도의 부모-자식 관계로 충분합니다. 사용할 모델이 두 자리 수나 세 자리 수인 경우, 부위별(얼굴, 머리카락 등) 인스턴스 머티리얼을 추가로 생성하고 거기서 일괄적으로 조정하는 것이 더 편합니다.

사진은 이미 노드를 내장한 관계로 머티리얼에 색을 입혔으나, 초기 상태에서는 검은색이어야 합니다.

 

포스트 프로세스 파라미터 설정

언리얼 엔진은 좋든 나쁘든 포스트 이펙트의 영향이 매우 큽니다. 셀톤 표현에 있어서는 후자의 나쁜 영향, 불편함을 주는 요인이 될 수 있습니다.

엔진 튜닝의 본론이 아니기 때문에 자세한 설명은 생략하고, 이번에는 자동 노출과 비네팅을 비활성화했습니다. 톤 매퍼와 블룸은 필자가 개인적으로 좋아하는 효과라는 이유로 활성화했습니다.

톤매퍼가 번짐과 궁합이 안 좋기로 유명하긴 하지만, 화이트 플래시를 쉽게 방지할 수 있다는 장점이 있기 때문에 왠지 비활성화하기 어려운 것 같습니다.

자동 노출 비활성화
비네팅 비활성화

툰라이팅 기능 준비

표준 라이팅 함수 내에서 분기를 생성하는 것도 좋지만, 가독성을 고려해 툰 라이팅용 AcculateDynamicToonLighting 함수를 생성하고 있습니다.
https://github.com/kafues511/UnrealEngine/blob/a475e1bac7d12fc8138cb9a4a0c86ab1b10b9b0d/Engine/Shaders/Private/DeferredLightingCommon.ush#L427-L486
라이팅 계산을 하는 픽셀의 셰이딩 모델이 ToonLit인 경우, AccumulateDynamicToonLighting 함수를 실행합니다.
https://github.com/kafues511/UnrealEngine/blob/a475e1bac7d12fc8138cb9a4a0c86ab1b10b9b0d/Engine/Shaders/Private/DeferredLightingCommon.ush#L499-L507
링크된 구현을 열람하기 위해서는 4회 '엔진 얻는 방법'의 절차에 포함된 EULA에 동의하고 승인한 GitHub 계정이 필요합니다.
AccumulateDynamicLighting 함수와 가장 큰 차이점은 다음과 같습니다.

차이점이유
ToonLit 이외의 ClearCoat, Subsurface 등의 셰이딩 모델의 분기를 제거합니다.ToonLit 전용 라이팅 기능으로 다른 셰이딩 모델을 가정할 필요가 없습니다.
LightMask의 분기 반전가독성 중시
SHADING_PATH_MOBILE, NON_DIRECTIONAL_DIRECT_LIGHTING, REFERENCE_QUALITY 변형을 제거합니다.본 개조에서는 불필요한 변형을 위해...
Transmission의 영향 제거Subsurface 계열의 라이팅은 불필요
Shadow.SurfaceShadow + Shadow.TransmissionShadow의 조건부 분기를 제거합니다.표준 라이팅에서는 그림자(Shadow)가 Black이 되지만, 툰 표현에서는 그림자 색을 지정하기 때문에 조건 분기가 불편하다.

이 시점에서는 아무것도 표시되지 않고 새까만 상태입니다.

그늘 / Shade / Shade

다들 아시다시피 NoL입니다.

NoL, NoV 등 자주 사용되는 내적분은 BxDFContext에 정리되어 있으며, 계산은 Init 함수에서 일괄적으로 이루어집니다. 편리하죠?
라이팅 계산에 자주 사용되는 요소 계산 (L964-L967)
https://github.com/kafues511/UnrealEngine/blob/18d8e9e86c8a3e7df4ec5cd99ae95b80848d8ea3/Engine/Shaders/Private/ShadingModels.ush#L964-L967
 
NoL을 2값화한 ToonNoL 계산(L971)
https://github.com/kafues511/UnrealEngine/blob/18d8e9e86c8a3e7df4ec5cd99ae95b80848d8ea3/Engine/Shaders/Private/ShadingModels.ush#L971
 
ToonNoL을 Diffuse에 저장하여 결과 시각화(L973)
https://github.com/kafues511/UnrealEngine/blob/18d8e9e86c8a3e7df4ec5cd99ae95b80848d8ea3/Engine/Shaders/Private/ShadingModels.ush#L973
 

픽셀 셰이더를 전체적으로 보면 float와 half가 혼재되어 사용되고 있습니다.

이번 대상 플랫폼인 PC(HLSL)는 기본적으로 half형은 float형으로 취급하기 때문에 분위기상 그냥 대충 쓰는 정도라고 생각하시면 되고, Platform.ush를 들여다보면 어느 정도 파악할 수 있을 것 같습니다.

모바일 환경에서는 GPU에 따라 다르지만, 반정밀도로 움직여주기 때문에 의식적으로 작성하지 않으면 정확도 때문에 오류가 발생하여 미쳐버릴 수 있습니다.

얼굴 그림자 / FaceShadow

4회에서는 Face Shadow Texture라는 이름에 걸맞게 얼굴 음영의 모양을 텍스처로 지정했는데, 정확도가 부족하여 타원이 울퉁불퉁해져서 UV에서 런타임에 계산하는 형태로 변경했습니다.

모양 계산이 너무 대충 계산되어 V의 상단과 끝부분이 둥글어지는 불편함과 라이트 벡터가 바로 위쪽에 가까워지면 거칠어지는 두 가지 불편함이 있지만, 그냥 넘어가기로 했습니다. 둥근 모양을 만들기 위해 곡선 계수를 중앙과 가장자리에서 보간하고 있습니다.

얼굴 음영 표현 적용 여부는 Face Shadow Vector가 제로 벡터인지 아닌지로 판단하고, 제로 벡터인 경우 ToonNoL을 적용합니다.

또한, 얼굴 음영 적용 시에도 Face Shadow Texture의 R과 G가 모두 0인 경우에는 항상 밝은 색으로 설정되어 있습니다. 이는 흰자위 부분을 어둡게 하면 원래 색이 흰색이기 때문에 상당히 어두워지기 때문에 차라리 어둡게 하지 않아도 된다는 판단에서입니다.
얼굴 음영 계산 구현 (L971-L998)
https://github.com/kafues511/UnrealEngine/blob/6962bfce24465f8d3ff9c6fd461e8ebd6edbd463/Engine/Shaders/Private/ShadingModels.ush#L971-L998
Face Shadow Vector는 벡터이므로 0.0~1.0에서 -1.0~1.0까지의 값 범위를 디코딩해야 합니다.

하지만, 디코딩을 잘못하면 영점 벡터를 얻을 수 없습니다. 인코딩 시 제로 벡터는 0.5가 되지만, 8bit로 표현할 때는 0.5라는 것이 없고 127 / 255로 약 0.498로 처리됩니다. 너무 깊이 들어가면 대다수의 사람들은 재미없을 것 같으니, 뭔가 이상한 매너를 하고 있구나 하고 생각하시면 됩니다.
 
디코딩하기 전에 8비트 정밀도로 제로 벡터를 감지한 후 디코딩(L477-L485)합니다.
https://github.com/kafues511/UnrealEngine/blob/6962bfce24465f8d3ff9c6fd461e8ebd6edbd463/Engine/Shaders/Private/GBufferHelpers.ush#L477-L485
 

다음은 마스터 머티리얼 측의 노드 구성 방법입니다.

Face Forward Vector 계산
Face Shadow Texture 계산
UV.x = UV.x > 0.5 ? smoothstep(0.3, 0.5, 1.0 - UV.x) : smoothstep(0.3, 0.5, UV.x);
UV.y = UV.y > 0.5 ? smoothstep(0.1, 0.5, 1.0 - UV.y) : smoothstep(0.0, 0.5, UV.y);
return UV;

Face Shadow Texture 계산을 위한 커스텀 노드 1편

UV -= 0.5;
float Theta = atan2(UV.x, UV.y);
float Radius = sqrt(dot(UV, UV));
Radius *= 1.0 + Strength * (Radius * Radius);
UV = 0.5 + Radius * float2(sin(Theta), cos(Theta));
return UV.x;

Face Shadow Texture 계산을 위한 커스텀 노드 2탄
 
 

페이스 포워드 벡터와 페이스 섀도우 텍스처의 연결

 

그림자 / Shadow

UE는 섀도맵을 다루기 어렵기 때문에 그림자를 제거하기 위해 엔진을 개조했다고 해도 과언이 아닙니다.

Shadow Map 텍스처는 8bit 정확도인데, step으로 2치화하기에는 조금 부담스러워서 smoothstep으로 폭을 넓히는 정도로만 처리하고 있습니다. 직선형 그림자를 떨어뜨리는 부분은 step으로도 의외로 잘 견디는데, 곡선이 되는 순간 계단식으로 바뀌는 거죠.
smoothstep으로 2값에 가깝게 하기(L971)
https://github.com/kafues511/UnrealEngine/blob/6e2188355c81b7e79e556a23a6554b1246159ffc/Engine/Shaders/Private/ShadingModels.ush#L971
 
얼굴 그림자 대응(L989)
https://github.com/kafues511/UnrealEngine/blob/6e2188355c81b7e79e556a23a6554b1246159ffc/Engine/Shaders/Private/ShadingModels.ush#L989
 
음영 대응(L999)
https://github.com/kafues511/UnrealEngine/blob/6e2188355c81b7e79e556a23a6554b1246159ffc/Engine/Shaders/Private/ShadingModels.ush#L999

 

명색과 음영색 / Base Color & Shadow Color

음영이 정해지면 그에 따라 명색(빛이 닿는 부분의 색)과 음영색(빛이 닿지 않는 부분의 색)을 칠해 나갑니다.
GBuffer에서 명암색 획득 (L971-L972)
https://github.com/kafues511/UnrealEngine/blob/9b99586e99c9eb0425e4d4da79d5b204f1d5d7de/Engine/Shaders/Private/ShadingModels.ush#L971-L972
명암에 따라 색을 입히다(L992)
https://github.com/kafues511/UnrealEngine/blob/9b99586e99c9eb0425e4d4da79d5b204f1d5d7de/Engine/Shaders/Private/ShadingModels.ush#L992
FaceShadow의 마스크 부분은 항상 밝은 색(L996)
https://github.com/kafues511/UnrealEngine/blob/9b99586e99c9eb0425e4d4da79d5b204f1d5d7de/Engine/Shaders/Private/ShadingModels.ush#L996
명암에 따라 색을 입히다(L1002)
https://github.com/kafues511/UnrealEngine/blob/9b99586e99c9eb0425e4d4da79d5b204f1d5d7de/Engine/Shaders/Private/ShadingModels.ush#L1002
 

색이 들어가면 금방 그 느낌이 나네요. 여기부터는 256색으로 담으면 아티팩트가 발생해 본래의 모습을 보여주지 못하기 때문에 GIF가 아닌 정물이 더 많이 나오게 됩니다.

Base Color 계산
Shadow Color 계산
Base Color와 Shadow Color의 연결

 

스펙큘러 / Specular

의류에 적용하는 광택 표현입니다.

툰 표현은 기본적으로는 단색으로 칠하는 것이 기본이지만, 그러다 보니 그림이 너무 단조로워질 수 있습니다. 이럴 때는 적당히 스페큘러로 그라데이션을 주면 의외로 잘 표현할 수 있습니다.

스페큘러의 구현 (L1014-L1021)
https://github.com/kafues511/CGW6/blob/613dfb9ba4b3e0a54dc202859318cf3cb73c8ded/Engine/Shaders/Private/ShadingModels.ush#L1014-L1021
스페큘러 반사 (L481)
https://github.com/kafues511/CGW6/blob/613dfb9ba4b3e0a54dc202859318cf3cb73c8ded/Engine/Shaders/Private/DeferredLightingCommon.ush#L481

 

Roughness 계산
Roughness 연결

 

천사의 고리 / Angel Ring

Face Shadow Texture와 비슷한 이유로 Jitter Texture의 해상도가 낮고 준비하기가 귀찮아서(노이즈만 부여하는 것이지만) Hash 함수로 Jitter를 적용하고 있습니다.
헤어 하이라이트 구현 (L1014-L1027)
https://github.com/kafues511/UnrealEngine/blob/390044a777356d1c732227438c26f1a4f36b6f9f/Engine/Shaders/Private/ShadingModels.ush#L1014-L1027
저장 위치를 잘못 저장하여 수정 및 0.0~1024.0 범위를 저장(L215-L216)
https://github.com/kafues511/UnrealEngine/blob/390044a777356d1c732227438c26f1a4f36b6f9f/Engine/Shaders/Private/ShadingModelsMaterial.ush#L215-L216
0.0~1.0에서 0.0~1024.0으로 디코딩(L470)
https://github.com/kafues511/UnrealEngine/blob/390044a777356d1c732227438c26f1a4f36b6f9f/Engine/Shaders/Private/GBufferHelpers.ush#L470
 

Highlight Exponent 계산
Highlight Jitter Intensity 계산
#define Hash(Value) frac(sin(dot(Value, float2(12.9898, 78.233))) * 43758.5453)
 
// manual bilinear filtering
float4 Values00 = float4(Hash(floor(UV + float2(0.0, 0.0))),
                         Hash(floor(UV + float2(1.0, 0.0))),
                         Hash(floor(UV + float2(0.0, 1.0))),
                         Hash(floor(UV + float2(1.0, 1.0))));
float2 Fraction = fmod(UV, 1.0);
float2 HorizontalLerp00 = lerp(Values00.xz, Values00.yw, Fraction.xx);
return lerp(HorizontalLerp00.x, HorizontalLerp00.y, Fraction.y);

Highlight Jitter Intensity 계산을 위한 사용자 지정 노드
 

하이라이트 지터 강도와 하이라이트 지수 연결

 

라이트 컬러 반영

현재 명색과 음영색은 머티리얼에서 지정한 Base Color와 Shadow Color를 출력할 뿐인 딱딱한 처리입니다.

여기에 라이트 컬러의 색감을 반영하여 황혼이라면 주황색, 밤하늘이라면 푸른빛이 도는 등 환경과 장면에 맞게 출력할 수 있도록 합니다.
라이트 컬러의 최대 광량이 대략 (255, 255, 255, 255)에 맞도록 조정 (L481-L483)
https://github.com/kafues511/UnrealEngine/blob/4e4eb45db974ee68d6f1d6bfe20cbe9e8da3c5fa/Engine/Shaders/Private/DeferredLightingCommon.ush#L481-L483
라이트 컬러 반영(L485)
https://github.com/kafues511/UnrealEngine/blob/4e4eb45db974ee68d6f1d6bfe20cbe9e8da3c5fa/Engine/Shaders/Private/DeferredLightingCommon.ush#L485

왼쪽부터 LightColor(255, 229, 205) → (255, 229, 205) → (147, 180, 238)

 

스카이 라이트의 반영

라이트 컬러 다음에는 스카이 라이트 컬러를 반영하게 합니다. 두 라이트 컬러를 모두 고려하면 색감 반영이 극단적으로 편중되는 것을 개선할 수 있습니다. 정규화를 했기 때문에 색이 섞여 있는 것 같지만요.
라이트 컬러와 마찬가지로 스카이 라이트 컬러도 조정하여 반영 (L488-L489)
https://github.com/kafues511/UnrealEngine/blob/99cc45c54afa87f7f7d9fd6f13180ce67915b623/Engine/Shaders/Private/DeferredLightingCommon.ush#L488-L489
스카이 라이트 컬러를 반영하여 광량이 1.0을 초과하므로 최종 조정 (L492)
https://github.com/kafues511/UnrealEngine/blob/99cc45c54afa87f7f7d9fd6f13180ce67915b623/Engine/Shaders/Private/DeferredLightingCommon.ush#L492
 

상단: 라이트 컬러만, 중간: 라이트 컬러를 화이트&스카이 라이트 컬러로 설정, 하단: 라이트 컬러와 스카이 라이트 컬러 모두로 설정

 

추가 조명(Point, Spot, Rect) 반영

포인트, 스팟 등 추가 조명을 쉽게 반영할 수 있는 것도 엔진 개조가 갖는 장점 중 하나다.

디렉셔널 라이트와 달리 추가 라이트는 거리 감쇠를 고려해야 합니다. 이번에는 라이트 컬러를 정규화했기 때문에 크게 불편하지 않지만, 보통은 광량이 오버플로 상태로 비정상적으로 발광한다.

감쇠 없음

Diffuse와 Specular에 감쇠 적용 (L1029-L1030)
https://github.com/kafues511/UnrealEngine/blob/74d5a90825a2255365be19704f28055af0058280/Engine/Shaders/Private/ShadingModels.ush#L1029-L1030
추가 조명의 경우, 조명 색상 정규화 비적용 및 스카이라이트 색상 합산 미적용(L482-L496)
https://github.com/kafues511/UnrealEngine/blob/74d5a90825a2255365be19704f28055af0058280/Engine/Shaders/Private/DeferredLightingCommon.ush#L482-L496
거리 감쇠를 적용하면 이런 모습입니다.

비교해보면 셀톤 표현이라 정직하게 전자가 더 좋은 것 같기도 하네요. 라이팅의 규칙은 자유롭게 비틀어도 되니 마음에 드는 쪽을 채택하시면 됩니다. 일반적으로는 감쇠를 고려하기 때문에 후자로 진행하겠습니다.

감쇠 있음

 

림 라이트

림라이트는 내선법으로 계산하여 쓰고 있습니다.

림 라이트 계산
const uint MaxOffset = 8;
const float2 Offsets[MaxOffset] = {
    float2(-1.0, 1.0), float2(0.0, 1.0), float2(1.0, 1.0),
    float2(-1.0, 0.0),                   float2(1.0, 0.0),
    float2(-1.0,-1.0), float2(0.0,-1.0), float2(1.0,-1.0),
};
 
// Sky Mask.
const float SceneDepth = SceneTextureLookup(ClampSceneTextureUV(ViewportUVToSceneTextureUV(UV, PPI_SceneDepth), PPI_SceneDepth), PPI_SceneDepth, 0).r;
BRANCH if (SceneDepth > 100000.0) { return 0.0; }
 
// ToonLit Pixel Only.
const bool bIsToonLit = SceneTextureLookup(ClampSceneTextureUV(ViewportUVToSceneTextureUV(UV, PPI_ShadingModelID), PPI_ShadingModelID), PPI_ShadingModelID, 0).r == SHADINGMODELID_TOON_LIT;
BRANCH if (bIsToonLit == false) { return 0.0; }
 
float Weight = 0.0;
UNROLL for (uint i = 0; i < MaxOffset; ++i) {
    float2 NewUV = UV + Radius * Offsets[i] * TexelSize;
    float2 NewClampedUV = ClampSceneTextureUV(ViewportUVToSceneTextureUV(NewUV, PPI_SceneDepth), PPI_SceneDepth);
    float NewSceneDepth = SceneTextureLookup(NewClampedUV, PPI_SceneDepth, 0).r;
    Weight += (NewSceneDepth - SceneDepth);
}
 
return saturate(smoothstep(Threshold - 0.1, Threshold + 0.1, Weight));

림라이트 계산을 위한 커스텀 노드
 

개요

아웃라인도 림라이트와 마찬가지로 내선법으로 작성하고 있습니다.

일반적으로 외선법으로 작성하는 것이 일반적이지만, 사용하는 앤티앨리어싱이 TSR의 조합으로 외선법으로 작성하면 심도와 속도를 샘플링하지 않으면 잔상이 발생하기 때문에 내선법을 선택했습니다.

아웃라인 계산
const uint MaxOffset = 8;
const float2 Offsets[MaxOffset] = {
    float2(-1.0, 1.0), float2(0.0, 1.0), float2(1.0, 1.0),
    float2(-1.0, 0.0),                   float2(1.0, 0.0),
    float2(-1.0,-1.0), float2(0.0,-1.0), float2(1.0,-1.0),
};
 
// Sky Mask.
const float SceneDepth = SceneTextureLookup(ClampSceneTextureUV(ViewportUVToSceneTextureUV(UV, PPI_SceneDepth), PPI_SceneDepth), PPI_SceneDepth, 0).r;
BRANCH if (SceneDepth > 100000.0) { return SceneColor; }
 
// ToonLit Pixel Only.
const bool bIsToonLit = SceneTextureLookup(ClampSceneTextureUV(ViewportUVToSceneTextureUV(UV, PPI_ShadingModelID), PPI_ShadingModelID), PPI_ShadingModelID, 0).r == SHADINGMODELID_TOON_LIT;
BRANCH if (bIsToonLit == false) { return SceneColor; }
 
float Weight = 0.0;
UNROLL for (uint i = 0; i < MaxOffset; ++i) {
    float2 NewUV = UV + Radius * Offsets[i] * TexelSize;
    float2 NewClampedUV = ClampSceneTextureUV(ViewportUVToSceneTextureUV(NewUV, PPI_SceneDepth), PPI_SceneDepth);
    float NewSceneDepth = SceneTextureLookup(NewClampedUV, PPI_SceneDepth, 0).r;
    Weight += NewSceneDepth - SceneDepth;
}
 
 
Weight = smoothstep(0.0, 1.0, saturate(PositiveClampedPow(Weight * Intensity, Bias)));
 
return lerp(SceneColor, SceneColor * OutlineColor, Weight);

아웃라인 계산의 커스텀 노드
 

완성

CustomData.a와 ToonData1, ToonData2의 세 개의 슬롯에 무엇을 채워야 할지 기억이 나지 않아 완성해버렸다. 뭐, 남는 만큼의 여유가 있으니 일단 정리해두기로 하고, 완성된 라이팅이 어느 정도 수준까지 왔는지 보고 마무리합니다.

어느 정도 완성된 라이팅을 보고 마무리합니다. 아침과 낮에는 비교적 선명한 색감이 나오고, 밤에는 달밤에 비치는 느낌이 잘 나와서 좋네요. 유적지 같은 곳에 포인트 라이트를 배치한 것뿐인데, 의외로 분위기가 잘 살아났습니다.

 

끝!!!

세 차례에 걸쳐 엔진 개조를 통해 셀톤 룩을 구현했습니다.

구현을 되돌아보면 라이트 컬러를 무리하게(적당히) 정규화하거나, 그림자 그라데이션의 폭을 smoothstep으로 끌어오거나, 비현실적인 계산을 많이 하고 있었습니다. 기존 DefaultLit 등에서는 이런 라이팅 규칙을 수정할 수 없고, Unlit으로 구현할 수 있다고 해도 비용이 두 배로 들기 때문에 퍼포먼스가 나빠집니다.

이런 여러 가지 병목현상을 최소화하면서 이상적인 룩을 만들 수 있는 수단 중 하나가 '엔진 개조'입니다.

최근 UE의 업데이트 빈도를 고려하면 구현 및 유지보수 비용이 엄청나게 높지만, 대부분의 제품 버전에서 엔진 모딩이 이뤄지는 배경에는 이런 번거로움을 최소화하면서 무엇이든 할 수 있다는 점이 있다고 생각합니다.

설명이 다소 부족하거나 렌더링을 처음 접하는 분들에게는 어려운 부분도 있었겠지만, 조금이라도 엔진개조라는 편리하고 번거로운 작업의 문턱을 낮출 수 있다면 좋겠습니다.


역자 맺음말.
정말 멋진 토픽이죠! 한 스탭 스탭 따라가 볼 수 있는 멋진 튜토리얼 입니다. 이 버전은 언리얼 5.2 버전으로 되어 있습니다.
이번 주에 시간을 내어 5.3.2 와 5.4 버전으로 업그레이드 해서 원작자 브렌치에 리퀘스트 해 보겠습니다.
 
다시 한번 코바야시 님에게 감사의 말씀을 전합니다.


小林新汰/Kobayashi Arata
新卒でSPARK CREATIVEに入社後、Unreal Engineをはじめとした様々なグラフィック業務に従事している。

●SPARKグループ公式サイト
spark-group.jp
●SPARK CREATIVE Techブログ
tech.spark-creative.co.jp
●SPARK CREATIVE 採用ページ(新卒採用/キャリア採用)
spark-group.jp/recruit/