WinCNT

URP로 Toon Shader 만들기 -Specular 2- 본문

Unity/URP or Shader 관련

URP로 Toon Shader 만들기 -Specular 2-

WinCNT_SSS 2023. 4. 17. 15:03

URP에 대해서 공부하면서 알게 된 것을 정리하는 페이지입니다


서론

저번에는 Toon Shading의 Specular 요소란 무엇인가에 대해서 정리해 봤다

이번에는 Specular의 실제 구현에 대해서 정리하고자 한다


스펙큘러냐 하이라이트냐

여기서 말하는 스펙큘러와 하이라이트는 모두 블린 퐁 모델에서의 스펙큘러와 같다

즉, 빛이 정반사되어 눈에 들어와서 빛나 보이는 부분을 말한다

 

다만 그 표현 방식이 다르기 때문에 유니티 툰 쉐이더(UTS)에서는 다른 단어를 사용하고 있었다

간단히 말해 스펙큘러는 블린 퐁 모델에서의 스펙큘러와 같은 느낌이고

하이라이트는 스펙큘러를 Two Tone Shading처럼 경계로 나눈 느낌이다

 

예시를 보면 이해하기 쉬울 것이다

스펙큘러(좌), 하이라이트(우)

그럼 Two Tone Shading처럼 스펙큘러도 모두 하이라이트로 하면 되지 않을까라고 생각할 수 있는데, 모든 걸 경계로 나눌 수 없다는 것이 Toon Shading의 귀찮은 점이다

배경을 PBR, 캐릭터를 NPR로 한다고 해도(원신처럼) 캐릭터도 머리카락, 얼굴, 몸(옷), 스킬 시전 시의 표현은 다를 것이다

또한 아트 컨셉에 따라서는 다양한 표현이 필요하게 될 것이다

 

즉, 결론은 일단 둘 다 구현하는 게 좋다는 것…


스펙큘러와 하이라이트의 그래프

일단 여러 스펙큘러의 그래프를 정리해봤다

 

Blinn-Phong

float y(float x) {
  // x == Normal dot Half Angle
  float specFactor = pow(max(x, 0.0), 20.0);
  return specFactor;
}

UTS의 Specular

float y(float x) {
  // x == Half NdotH == 0.5 * dot(normal, halfAngle) + 0.5
  float _SpecularPower = 0.5;
  float ret = pow(x, exp2(mix(11.0, 1.0, _SpecularPower)));
  return ret;
}

UTS의 Highlight

float y(float x) {
  // x == Half NdotH == 0.5 * dot(normal, halfAngle) + 0.5
  float _SpecularPower = 0.5;
  float ret = 1.0 - pow(x, 5.0);
  ret = step(x, 1.0 - pow(_SpecularPower, 5.0));
  ret = 1.0 - ret;
  return ret;
}

대략적으로 Blinn-Phong과 UTS의 스펙큘러 그래프는 큰 차이가 없어 보인다

UTS의 Hightlight의 그래프는 보이는 바와 같이 경계로 나눠져 있다

 

사족이지만 UTS도 그렇고 우마무스메도 그렇고

스펙큘러는 특히 매직 넘버가 많은 것처럼 보인다

단순히 필자가 이해하지 못 한 수식이 숨어있을 가능성도 있다


Specular 구현하기

그래프의 식을 구현하면 스펙큘러도 구현할 수 있다

 

물론 스펙큘러냐 하이라이트냐를 나눠주는 처리도 필요하다

외부에서 프로퍼티 등으로 조절하는 게 제일 좋지만 필자는 일단 내부의 변수로 만들었다

나중에 필요해지면 그 때가서 수정하면 되겠지

/// Specular
// Half-Angle Vector
const float3 _HalfDir = normalize(IN.lightDir + IN.viewDir); 
const float _HalfNdotH = 0.5 * dot(IN.normal, _HalfDir) + 0.5;

// Specluar로 할까 하이라이트로 할까 정하는 변수
// 0 : Specluar, 1 : Highlight
const bool _IsHighColorToSpecular = true;
// Specular Color에 Main LightColor를 반영할지를 정하는 변수
const bool _UseMainLightColorForSpec = true;

// Specular Power와 Half NdotH로 스펙큘러를 계산함
const float _HighColorMask =
    lerp(
        pow(abs(_HalfNdotH), exp2(lerp(11, 1, _SpecularPower))),    // Specluar
        1.0 - step(_HalfNdotH, 1.0 - pow(_SpecularPower, 5)),       // Highlight
        _IsHighColorToSpecular
    );

// 프로퍼티에서 설정한 Specular Color를 스펙큘러 수치에 따라 조정(설정에 따라서 Main Light Color를 반영함)
const float3 _HighColorOnly = lerp(_SpecularColor.rgb, _SpecularColor.rgb * _MainLight.color.rgb, _UseMainLightColorForSpec) * _HighColorMask;

// Base Color로 Specular를 조정한다
half3 _FinalHighColor =
    lerp(
        saturate(_FinalBaseColor.rgb - _HighColorMask), // 이 부분은 수수께끼
        _FinalBaseColor.rgb,
        lerp(0.0, 1.0, _IsHighColorToSpecular)
    )
    + _HighColorOnly.rgb;

// Specular를 더한다
_FinalColor.rgb += _FinalHighColor.rgb;

마무리

이것으로 Specular 구현에 대한 내용 마무리할까 한다

다음에는 림 라이트에 대해 정리해보려고 한다


참고 사이트

UnityChanToonShaderVer2_Project/UTS2_Manual_ja.md at release/legacy/2.0 · unity3d-jp/UnityChanToonShaderVer2_Project

 

GitHub - unity3d-jp/UnityChanToonShaderVer2_Project: UnityChanToonShaderVer2 Project / v.2.0.9 Release

UnityChanToonShaderVer2 Project / v.2.0.9 Release. Contribute to unity3d-jp/UnityChanToonShaderVer2_Project development by creating an account on GitHub.

github.com

GLSL Grapher

 

GLSL Grapher

 

fordhurley.com