// SPDX-License-Identifier: (Not available for this version, you are only allowed to use this software if you have express permission from the copyright holder and agreed to the latest NiloToonURP EULA) // Copyright (c) 2021 Kuroneko ShaderLab Limited #pragma once #if _SHADOW_MODE_LILTOON struct NiloToonLilToonShadowModeResult { half3 lightColorIndependentLitColor; half finalShadowAreaForNiloEffects; }; float NiloToonLilToonTooningNoSaturateScale(float aaStrength, float value, float border, float blur) { float borderMin = saturate(border - blur * 0.5); float borderMax = saturate(border + blur * 0.5); return (value - borderMin) / saturate(borderMax - borderMin + fwidth(value) * aaStrength); } float NiloToonLilToonTooningNoSaturateScale(float aaStrength, float value, float border, float blur, float borderRange) { float borderMin = saturate(border - blur * 0.5 - borderRange); float borderMax = saturate(border + blur * 0.5); return (value - borderMin) / saturate(borderMax - borderMin + fwidth(value) * aaStrength); } float NiloToonLilToonTooningScale(float aaStrength, float value, float border, float blur) { return saturate(NiloToonLilToonTooningNoSaturateScale(aaStrength, value, border, blur)); } float NiloToonLilToonTooningScale(float aaStrength, float value, float border, float blur, float borderRange) { return saturate(NiloToonLilToonTooningNoSaturateScale(aaStrength, value, border, blur, borderRange)); } float3 NiloToonLilToonLinearToSRGB(float3 col) { float3 low = col * 12.92; float3 high = 1.055 * pow(max(col, 0.0), 1.0 / 2.4) - 0.055; return lerp(low, high, step(0.0031308, col)); } void NiloToonLilToonCalcLUTUV(float3 col, float resX, float resY, out float4 uv, out float factor) { #if !UNITY_COLORSPACE_GAMMA col = NiloToonLilToonLinearToSRGB(col); #endif float3 res = float3(resX, resY, resX * resY); float3 resInv = float3(1.0, -1.0, 1.0) / res; float3 col2 = (col - col * resInv.z) + 0.5 * resInv.z; float4 b2 = saturate(col2.b + resInv.z * float2(-0.5, 0.5)).xxyy * res.zyzy; float4 b3 = floor(b2); uv = float4(0, 1, 0, 1) + (col2.rgrg + b3) * resInv.xyxy; factor = abs(b2.x - b3.x); } // Match lilToon's shared sampler behavior for shadow masks. #define NILO_LIL_SAMPLE_SHADOW_MASK_WITH_LOD(textureName, uv, lod) \ ((lod) > 0 ? SAMPLE_TEXTURE2D_GRAD(textureName, sampler_linear_repeat, uv, max(abs(ddx(uv)), lod), max(abs(ddy(uv)), lod)) : SAMPLE_TEXTURE2D(textureName, sampler_linear_repeat, uv)) #define NILO_LIL_SAMPLE_SHADOW_COLOR_TEX(textureName, uv, lutUv, lutFactor) \ (_lilShadowColorType == 1 ? lerp(SAMPLE_TEXTURE2D_LOD(textureName, sampler_linear_clamp, (lutUv).xy, 0), SAMPLE_TEXTURE2D_LOD(textureName, sampler_linear_clamp, (lutUv).zw, 0), lutFactor) : SAMPLE_TEXTURE2D(textureName, sampler_BaseMap, uv)) half NiloToonLilToonRemapAO(half value, half minValue, half maxValue) { return invLerpClamp(minValue, maxValue, value); } NiloToonLilToonShadowModeResult CalculateNiloToonLilToonShadowMode( ToonSurfaceData surfaceData, ToonLightingData lightingData, Light light, half selfShadowMapShadow, half depthDiffShadow, half finalNiloShadowArea) { NiloToonLilToonShadowModeResult result; if (!_EnableShadowColor) { result.lightColorIndependentLitColor = surfaceData.albedo; result.finalShadowAreaForNiloEffects = finalNiloShadowArea; return result; } const float2 uv = lightingData.uv; const half3 rawAlbedo = surfaceData.albedo; const half3 L = light.direction; // lilToon also adds distance(fd.L, fd.origL) for _LightDirectionOverride; intentionally skipped here. half receivedShadow = saturate(light.shadowAttenuation); #if _NILOTOON_RECEIVE_SELF_SHADOW receivedShadow *= selfShadowMapShadow; #endif receivedShadow *= depthDiffShadow; receivedShadow = saturate(receivedShadow); half3 N1 = lerp(lightingData.normalWS_NoNormalMap, lightingData.normalWS, _lilShadowNormalStrength); half3 N2 = lerp(lightingData.normalWS_NoNormalMap, lightingData.normalWS, _lilShadow2ndNormalStrength); half3 N3 = lerp(lightingData.normalWS_NoNormalMap, lightingData.normalWS, _lilShadow3rdNormalStrength); half4 shadowStrengthMask = NILO_LIL_SAMPLE_SHADOW_MASK_WITH_LOD(_lilShadowStrengthMask, uv, _lilShadowStrengthMaskLOD); half4 lns = 1.0; lns.x = saturate(dot(L, N1) * 0.5 + 0.5); lns.y = saturate(dot(L, N2) * 0.5 + 0.5); lns.z = saturate(dot(L, N3) * 0.5 + 0.5); lns.w = lns.x; half aaStrength = _lilAAStrength; if (_lilShadowMaskType == 2) { float3 faceR = mul((float3x3)UNITY_MATRIX_M, float3(-1.0, 0.0, 0.0)); float LdotR = dot(L.xz, faceR.xz); half sdf = LdotR < 0 ? shadowStrengthMask.g : shadowStrengthMask.r; float3 faceF = mul((float3x3)UNITY_MATRIX_M, float3(0.0, 0.0, 1.0)); faceF.y *= _lilShadowFlatBlur; faceF = SafeNormalize(faceF); float3 faceL = L; faceL.y *= _lilShadowFlatBlur; faceL = SafeNormalize(faceL); half lnSDF = dot(faceL, faceF); lns = lerp(saturate(lnSDF * 0.5 + sdf * 0.5 + 0.25), lns, shadowStrengthMask.b); aaStrength = 0; shadowStrengthMask.r = shadowStrengthMask.a; } lns.x *= lerp(1.0, receivedShadow, _lilShadowReceive); lns.y *= lerp(1.0, receivedShadow, _lilShadow2ndReceive); lns.z *= lerp(1.0, receivedShadow, _lilShadow3rdReceive); half shadowBlur = _lilShadowBlur; half shadow2ndBlur = _lilShadow2ndBlur; half shadow3rdBlur = _lilShadow3rdBlur; half4 shadowBlurMask = NILO_LIL_SAMPLE_SHADOW_MASK_WITH_LOD(_lilShadowBlurMask, uv, _lilShadowBlurMaskLOD); shadowBlur *= shadowBlurMask.r; shadow2ndBlur *= shadowBlurMask.g; shadow3rdBlur *= shadowBlurMask.b; half4 shadowBorderMask = NILO_LIL_SAMPLE_SHADOW_MASK_WITH_LOD(_lilShadowBorderMask, uv, _lilShadowBorderMaskLOD); shadowBorderMask.r = NiloToonLilToonRemapAO(shadowBorderMask.r, _lilShadowAO1Min, _lilShadowAO1Max); shadowBorderMask.g = NiloToonLilToonRemapAO(shadowBorderMask.g, _lilShadowAO2Min, _lilShadowAO2Max); shadowBorderMask.b = NiloToonLilToonRemapAO(shadowBorderMask.b, _lilShadowAO3Min, _lilShadowAO3Max); lns.xyz = _lilShadowPostAO ? lns.xyz : lns.xyz * shadowBorderMask.rgb; lns.w = lns.x; lns.x = NiloToonLilToonTooningNoSaturateScale(aaStrength, lns.x, _lilShadowBorder, shadowBlur); lns.y = NiloToonLilToonTooningNoSaturateScale(aaStrength, lns.y, _lilShadow2ndBorder, shadow2ndBlur); lns.w = NiloToonLilToonTooningNoSaturateScale(aaStrength, lns.w, _lilShadowBorder, shadowBlur, _lilShadowBorderRange); lns.z = NiloToonLilToonTooningNoSaturateScale(aaStrength, lns.z, _lilShadow3rdBorder, shadow3rdBlur); lns = _lilShadowPostAO ? lns * shadowBorderMask.rgbr : lns; lns = saturate(lns); half backfaceShadow = lightingData.facing < 0.0 ? 1.0 - _BackFaceForceShadow : 1.0; lns.x *= backfaceShadow; lns.y *= backfaceShadow; lns.z *= backfaceShadow; lns.w *= backfaceShadow; half shadowStrength = _lilShadowStrength; if (_lilShadowMaskType == 1) { half3 flatN = normalize(mul((float3x3)UNITY_MATRIX_M, float3(0.0, 0.25, 1.0))); half lnFlat = saturate((dot(flatN, L) + _lilShadowFlatBorder) / _lilShadowFlatBlur); lnFlat *= lerp(1.0, receivedShadow, _lilShadowReceive); lns = lerp(lnFlat, lns, shadowStrengthMask.r); } else { shadowStrength *= shadowStrengthMask.r; } lns.x = lerp(1.0, lns.x, shadowStrength); float4 shadowLutUv; float shadowLutFactor; NiloToonLilToonCalcLUTUV(rawAlbedo, 16, 1, shadowLutUv, shadowLutFactor); half4 shadowColorTex = NILO_LIL_SAMPLE_SHADOW_COLOR_TEX(_lilShadowColorTex, uv, shadowLutUv, shadowLutFactor); half4 shadow2ndColorTex = NILO_LIL_SAMPLE_SHADOW_COLOR_TEX(_lilShadow2ndColorTex, uv, shadowLutUv, shadowLutFactor); half4 shadow3rdColorTex = NILO_LIL_SAMPLE_SHADOW_COLOR_TEX(_lilShadow3rdColorTex, uv, shadowLutUv, shadowLutFactor); half3 indirectColor = lerp(rawAlbedo, shadowColorTex.rgb, shadowColorTex.a) * _lilShadowColor.rgb; half3 shadow2ndColor = lerp(rawAlbedo, shadow2ndColorTex.rgb, shadow2ndColorTex.a) * _lilShadow2ndColor.rgb; lns.y = _lilShadow2ndColor.a - lns.y * _lilShadow2ndColor.a; indirectColor = lerp(indirectColor, shadow2ndColor, lns.y); half3 shadow3rdColor = lerp(rawAlbedo, shadow3rdColorTex.rgb, shadow3rdColorTex.a) * _lilShadow3rdColor.rgb; lns.z = _lilShadow3rdColor.a - lns.z * _lilShadow3rdColor.a; indirectColor = lerp(indirectColor, shadow3rdColor, lns.z); indirectColor = lerp(indirectColor, indirectColor * rawAlbedo, _lilShadowMainStrength); indirectColor = lerp(indirectColor, rawAlbedo, saturate(Luminance(lightingData.SH) * _lilShadowEnvStrength)); indirectColor = min(indirectColor, rawAlbedo); indirectColor = lerp(indirectColor, rawAlbedo, lns.w * _lilShadowBorderColor.rgb); result.lightColorIndependentLitColor = lerp(indirectColor, rawAlbedo, lns.x); result.finalShadowAreaForNiloEffects = saturate(lns.x); return result; } #endif