Add : Amplify Environment template에 NiloToon char shadow receiver 지원

NiloToonEnvironment.shader 본체와 동일한 self-shadow 수신 기능을 ASE
template에도 이식. ASE로 환경 셰이더를 작성한 머티리얼도 캐릭터의
NiloToon 셀프 섀도우를 바닥/벽에 받을 수 있게 됨.

- Properties: [Toggle(_RECEIVE_NILOTOON_CHAR_SHADOW)] 토글 + Shadow
  Strength slider 추가 (기본 off, 기존 머티리얼 호환)
- Pragma: shader_feature_local_fragment _RECEIVE_NILOTOON_CHAR_SHADOW
  + multi_compile_fragment _ _NILOTOON_RECEIVE_SELF_SHADOW
- Per-material CBUFFER에 _NiloToonCharShadowStrength 추가
- 두 키워드 모두 활성 시: 텍스처/샘플러/매트릭스 uniform과
  SampleNiloToonCharSelfShadow 함수 선언
- frag에서 UniversalFragmentPBR 직후·shadow border tint 직전에 적용
  (NiloToonEnvironment_LitForwardPass.hlsl과 동일한 위치)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
user 2026-05-05 23:14:06 +09:00
parent 67c99af14f
commit 7075f46b25

View File

@ -22,6 +22,10 @@ Shader /*ase_name*/ "Hidden/Universal/NiloToonEnvironment" /*end*/
_ScreenSpaceOutlineIntensity("Screen Space Outline Intensity", Range(0,1)) = 1 _ScreenSpaceOutlineIntensity("Screen Space Outline Intensity", Range(0,1)) = 1
[HDR]_ScreenSpaceOutlineColor("Screen Space Outline Color", Color) = (1,1,1,1) [HDR]_ScreenSpaceOutlineColor("Screen Space Outline Color", Color) = (1,1,1,1)
_ScreenSpaceOutlineWidth("Screen Space Outline Width", Float) = 1 _ScreenSpaceOutlineWidth("Screen Space Outline Width", Float) = 1
// NiloToon Character Self Shadow Receiver (off by default to keep parity with original ASE template)
[Toggle(_RECEIVE_NILOTOON_CHAR_SHADOW)] _ReceiveNiloToonCharShadow("Receive NiloToon Char Shadow", Float) = 0
_NiloToonCharShadowStrength(" Shadow Strength", Range(0, 1)) = 1.0
} }
SubShader SubShader
@ -198,6 +202,10 @@ Shader /*ase_name*/ "Hidden/Universal/NiloToonEnvironment" /*end*/
#pragma shader_feature_local_fragment _ENVIRONMENTREFLECTIONS_OFF #pragma shader_feature_local_fragment _ENVIRONMENTREFLECTIONS_OFF
#pragma shader_feature_local_fragment _SPECULAR_SETUP #pragma shader_feature_local_fragment _SPECULAR_SETUP
// NiloToon Character Self Shadow keywords
#pragma shader_feature_local_fragment _RECEIVE_NILOTOON_CHAR_SHADOW
#pragma multi_compile_fragment _ _NILOTOON_RECEIVE_SELF_SHADOW
// ------------------------------------- // -------------------------------------
// Universal Pipeline keywords // Universal Pipeline keywords
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
@ -307,6 +315,8 @@ Shader /*ase_name*/ "Hidden/Universal/NiloToonEnvironment" /*end*/
half3 _ScreenSpaceOutlineColor; half3 _ScreenSpaceOutlineColor;
float _ScreenSpaceOutlineWidth; float _ScreenSpaceOutlineWidth;
float _Surface; float _Surface;
// NiloToon Character Self Shadow per-material strength
float _NiloToonCharShadowStrength;
/*ase_srp_batcher*/ /*ase_srp_batcher*/
CBUFFER_END CBUFFER_END
@ -326,6 +336,56 @@ Shader /*ase_name*/ "Hidden/Universal/NiloToonEnvironment" /*end*/
half3 _GlobalScreenSpaceOutlineTintColorForEnvi; half3 _GlobalScreenSpaceOutlineTintColorForEnvi;
float _CurrentCameraFOV; float _CurrentCameraFOV;
// NiloToon Character Self Shadow uniforms + sampling function
// (mirrors NiloToonEnvironment_LitForwardPass.hlsl. Both keywords required:
// _RECEIVE_NILOTOON_CHAR_SHADOW = per-material toggle, _NILOTOON_RECEIVE_SELF_SHADOW = global runtime keyword set by C# pass.)
#if defined(_RECEIVE_NILOTOON_CHAR_SHADOW) && defined(_NILOTOON_RECEIVE_SELF_SHADOW)
TEXTURE2D(_NiloToonCharSelfShadowMapRT);
SAMPLER_CMP(sampler_NiloToonCharSelfShadowMapRT_LinearClampCompare);
SAMPLER(sampler_NiloToonCharSelfShadowMapRT_Linear_Clamp);
float4x4 _NiloToonSelfShadowWorldToClip;
float4 _NiloToonSelfShadowParam;
float3 _NiloToonSelfShadowLightDirection;
float _GlobalReceiveNiloToonSelfShadowMap;
float _NiloToonSelfShadowRange;
float _NiloToonSelfShadowUseNdotLFix;
half SampleNiloToonCharSelfShadow(float3 positionWS, float3 normalWS, float linearEyeDepth)
{
float4 positionSelfShadowCS = mul(_NiloToonSelfShadowWorldToClip, float4(positionWS, 1));
float3 positionSelfShadowNDC = positionSelfShadowCS.xyz;
float2 shadowMapUV = positionSelfShadowNDC.xy * 0.5 + 0.5;
float ndcZCompareValue = positionSelfShadowNDC.z;
ndcZCompareValue = UNITY_NEAR_CLIP_VALUE < 0 ? ndcZCompareValue * 0.5 + 0.5 : ndcZCompareValue;
#if UNITY_UV_STARTS_AT_TOP
shadowMapUV.y = 1 - shadowMapUV.y;
#endif
if(shadowMapUV.x < 0 || shadowMapUV.x > 1 || shadowMapUV.y < 0 || shadowMapUV.y > 1)
return 1;
float shadowMapRawDepth = SAMPLE_TEXTURE2D_LOD(_NiloToonCharSelfShadowMapRT,
sampler_NiloToonCharSelfShadowMapRT_Linear_Clamp,
shadowMapUV, 0).r;
if(shadowMapRawDepth < 0.0001)
return 1;
half shadow = SAMPLE_TEXTURE2D_SHADOW(_NiloToonCharSelfShadowMapRT,
sampler_NiloToonCharSelfShadowMapRT_LinearClampCompare,
float3(shadowMapUV, ndcZCompareValue));
float fadeTotalDistance = 1.0;
shadow = lerp(shadow, 1, saturate((1.0 / fadeTotalDistance) * (linearEyeDepth - (_NiloToonSelfShadowRange - fadeTotalDistance))));
if(_NiloToonSelfShadowUseNdotLFix > 0.5)
shadow *= smoothstep(0.1, 0.2, saturate(dot(normalWS, _NiloToonSelfShadowLightDirection)));
return shadow;
}
#endif
#ifdef SCENEPICKINGPASS #ifdef SCENEPICKINGPASS
float4 _SelectionID; float4 _SelectionID;
#endif #endif
@ -531,6 +591,17 @@ Shader /*ase_name*/ "Hidden/Universal/NiloToonEnvironment" /*end*/
#endif #endif
Light mainLight = GetMainLight(inputData.shadowCoord, inputData.positionWS, nilotoonShadowMask); Light mainLight = GetMainLight(inputData.shadowCoord, inputData.positionWS, nilotoonShadowMask);
// NiloToon Character Self Shadow (matches order in NiloToonEnvironment_LitForwardPass.hlsl: applied before shadow border tint)
#if defined(_RECEIVE_NILOTOON_CHAR_SHADOW) && defined(_NILOTOON_RECEIVE_SELF_SHADOW)
{
float linearEyeDepth = abs(mul(UNITY_MATRIX_V, float4(input.positionWS, 1)).z);
half charSelfShadow = SampleNiloToonCharSelfShadow(input.positionWS, inputData.normalWS, linearEyeDepth);
charSelfShadow = lerp(1, charSelfShadow, _NiloToonCharShadowStrength * _GlobalReceiveNiloToonSelfShadowMap);
color.rgb *= charSelfShadow;
}
#endif
float isShadowEdge = 1-abs(mainLight.shadowAttenuation-0.5)*2; float isShadowEdge = 1-abs(mainLight.shadowAttenuation-0.5)*2;
color.rgb = lerp(color.rgb, color.rgb * _NiloToonGlobalEnviShadowBorderTintColor.rgb, isShadowEdge * _NiloToonGlobalEnviShadowBorderTintColor.a); color.rgb = lerp(color.rgb, color.rgb * _NiloToonGlobalEnviShadowBorderTintColor.rgb, isShadowEdge * _NiloToonGlobalEnviShadowBorderTintColor.a);