From e3f18d8726d5f3c6e3ca7b5bcf9f74a2c018e94e Mon Sep 17 00:00:00 2001 From: KINDNICK <68893236+KINDNICK@users.noreply.github.com> Date: Mon, 15 Dec 2025 21:15:33 +0900 Subject: [PATCH] =?UTF-8?q?Fix=20:=20=ED=8D=BC=20=EC=89=90=EC=9D=B4?= =?UTF-8?q?=EB=8D=94=20=EC=B5=9C=EC=A2=85=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Passes/NiloToonFurShellPass.cs | 8 +- .../Shaders/NiloToonCharacterFur.shader | 233 +++++++++++++++- .../NiloToonCharacterFur_Fragment.hlsl | 250 +++++++++++++++++- .../NiloToonCharacterFur_Geometry.hlsl | 99 +++++-- .../NiloToonCharacterFur_Shared.hlsl | 112 +++++++- .../NiloToonCharacterFur_ShellOnly.shader | 156 ----------- ...NiloToonCharacterFur_ShellOnly.shader.meta | 9 - ...ingle Render Pipeline Asset_Renderer.asset | 4 +- 8 files changed, 665 insertions(+), 206 deletions(-) delete mode 100644 Assets/NiloToonURP/Shaders/NiloToonCharacterFur_ShellOnly.shader delete mode 100644 Assets/NiloToonURP/Shaders/NiloToonCharacterFur_ShellOnly.shader.meta diff --git a/Assets/NiloToonURP/Runtime/RendererFeatures/Passes/NiloToonFurShellPass.cs b/Assets/NiloToonURP/Runtime/RendererFeatures/Passes/NiloToonFurShellPass.cs index bde5ca1f..bedbd18d 100644 --- a/Assets/NiloToonURP/Runtime/RendererFeatures/Passes/NiloToonFurShellPass.cs +++ b/Assets/NiloToonURP/Runtime/RendererFeatures/Passes/NiloToonFurShellPass.cs @@ -22,14 +22,10 @@ namespace NiloToon.NiloToonURP [Header("Fur Shell Settings")] [Tooltip("Enable to render fur shells.\n\nDefault: ON")] - [OverrideDisplayName("Enable?")] - [Revertible] public bool ShouldRenderFurShell = true; - [Tooltip("When to render fur shells in the rendering pipeline.\n\nDefault: AfterRenderingOpaques")] - [Revertible] - [OverrideDisplayName("Render Timing")] - public RenderPassEvent renderTiming = RenderPassEvent.AfterRenderingOpaques; + [Tooltip("When to render fur shells in the rendering pipeline.\n\nDefault: AfterRenderingSkybox + 1 (right after classic outline)")] + public RenderPassEvent renderTiming = RenderPassEvent.AfterRenderingSkybox + 1; } Settings settings; diff --git a/Assets/NiloToonURP/Shaders/NiloToonCharacterFur.shader b/Assets/NiloToonURP/Shaders/NiloToonCharacterFur.shader index cd79c632..a6838dce 100644 --- a/Assets/NiloToonURP/Shaders/NiloToonCharacterFur.shader +++ b/Assets/NiloToonURP/Shaders/NiloToonCharacterFur.shader @@ -140,6 +140,79 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" _FurZWrite("Fur ZWrite", Float) = 0 [Enum(UnityEngine.Rendering.CompareFunction)] _ZTest("ZTest", Float) = 4 + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Dissolve (NiloToonPerCharacterRenderController) + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + [HideInInspector] _DissolveAmount("_DissolveAmount", Range(0,1)) = 0 + [HideInInspector] _DissolveMode("_DissolveMode", Integer) = 0 + [HideInInspector] _DissolveThresholdMap("_DissolveThresholdMap", 2D) = "linearGrey" {} + [HideInInspector] _DissolveThresholdMapTilingX("_DissolveThresholdMapTilingX", Float) = 1 + [HideInInspector] _DissolveThresholdMapTilingY("_DissolveThresholdMapTilingY", Float) = 1 + [HideInInspector] _DissolveNoiseStrength("_DissolveNoiseStrength", Float) = 1 + [HideInInspector] _DissolveBorderRange("_DissolveBorderRange", Float) = 0.02 + [HideInInspector] _DissolveBorderTintColor("_DissolveBorderTintColor", Color) = (1, 3, 6, 1) + [HideInInspector] _AllowPerCharacterDissolve("_AllowPerCharacterDissolve", Float) = 1 + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Per-Character Color Controls (NiloToonPerCharacterRenderController) + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + [HideInInspector] _PerCharacterBaseColorMultiply("_PerCharacterBaseColorMultiply", Float) = 1 + [HideInInspector] _PerCharacterBaseColorTint("_PerCharacterBaseColorTint", Color) = (1, 1, 1, 1) + [HideInInspector] _PerCharacterTintColor("_PerCharacterTintColor", Color) = (1, 1, 1, 1) + [HideInInspector] _PerCharacterAddColor("_PerCharacterAddColor", Color) = (0, 0, 0, 0) + [HideInInspector] _PerCharEffectDesaturatePercentage("_PerCharEffectDesaturatePercentage", Range(0,1)) = 0 + [HideInInspector] _PerCharEffectLerpColor("_PerCharEffectLerpColor", Color) = (1, 1, 0, 0) + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Per-Character Rim Light (NiloToonPerCharacterRenderController) + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + [HideInInspector] _UsePerCharacterRimLightIntensity("_UsePerCharacterRimLightIntensity", Float) = 0 + [HideInInspector] _PerCharacterRimLightIntensity("_PerCharacterRimLightIntensity", Float) = 2 + [HideInInspector] _PerCharacterRimLightColor("_PerCharacterRimLightColor", Color) = (1, 1, 1, 1) + [HideInInspector] _PerCharacterRimLightSharpnessPower("_PerCharacterRimLightSharpnessPower", Float) = 4 + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // BaseMap Override (NiloToonPerCharacterRenderController) + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + [HideInInspector] _PerCharacterBaseMapOverrideAmount("_PerCharacterBaseMapOverrideAmount", Range(0,1)) = 0 + [HideInInspector] _PerCharacterBaseMapOverrideTintColor("_PerCharacterBaseMapOverrideTintColor", Color) = (1, 1, 1, 1) + [HideInInspector] _PerCharacterBaseMapOverrideMap("_PerCharacterBaseMapOverrideMap", 2D) = "white" {} + [HideInInspector] _PerCharacterBaseMapOverrideBlendMode("_PerCharacterBaseMapOverrideBlendMode", Float) = 0 + [HideInInspector] _PerCharacterBaseMapOverrideUVOption("_PerCharacterBaseMapOverrideUVOption", Float) = 0 + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Character Area Color Fill (NiloToonPerCharacterRenderController) + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + [HideInInspector] _ShouldRenderCharacterAreaColorFill("_ShouldRenderCharacterAreaColorFill", Float) = 0 + [HideInInspector] _CharacterAreaColorFillColor("_CharacterAreaColorFillColor", Color) = (1, 1, 1, 1) + [HideInInspector] _CharacterAreaColorFillTexture("_CharacterAreaColorFillTexture", 2D) = "white" {} + [HideInInspector] _CharacterAreaColorFillUVOption("_CharacterAreaColorFillUVOption", Float) = 0 + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Dither Opacity (NiloToonPerCharacterRenderController) + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + [HideInInspector] _DitherOpacity("_DitherOpacity", Range(0,1)) = 1 + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Dither Fadeout (NiloToonPerCharacterRenderController) + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + [HideInInspector] _DitherFadeoutAmount("_DitherFadeoutAmount", Range(0,1)) = 0 + [HideInInspector] _DitherFadeoutNormalScaleFix("_DitherFadeoutNormalScaleFix", Float) = 1 + [HideInInspector] _AllowPerCharacterDitherFadeout("_AllowPerCharacterDitherFadeout", Float) = 1 + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Perspective Removal (NiloToonPerCharacterRenderController) + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + [HideInInspector] _PerspectiveRemovalAmount("_PerspectiveRemovalAmount", Range(0,1)) = 0 + [HideInInspector] _PerspectiveRemovalRadius("_PerspectiveRemovalRadius", Float) = 5 + [HideInInspector] _PerspectiveRemovalStartHeight("_PerspectiveRemovalStartHeight", Float) = 1 + [HideInInspector] _PerspectiveRemovalEndHeight("_PerspectiveRemovalEndHeight", Float) = 0 + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // ZOffset (NiloToonPerCharacterRenderController) + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + [HideInInspector] _PerCharacterZOffset("_PerCharacterZOffset", Float) = 0 + // Hidden [HideInInspector] _Surface("__surface", Float) = 0.0 [HideInInspector] _Blend("__blend", Float) = 0.0 @@ -182,6 +255,8 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" #pragma shader_feature_local _RIMLIGHT #pragma shader_feature_local _EMISSION #pragma shader_feature_local _OCCLUSIONMAP + #pragma multi_compile_local _ _NILOTOON_DISSOLVE + #pragma multi_compile_local _ _NILOTOON_DITHER_FADEOUT // URP Keywords #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN @@ -229,6 +304,8 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" #pragma shader_feature_local _RIMLIGHT #pragma shader_feature_local _EMISSION #pragma shader_feature_local _OCCLUSIONMAP + #pragma multi_compile_local _ _NILOTOON_DISSOLVE + #pragma multi_compile_local _ _NILOTOON_DITHER_FADEOUT // URP Keywords #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN @@ -263,14 +340,18 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" Tags { "LightMode" = "NiloToonFurShellMask" } Cull [_FurCull] - ZWrite Off - ZTest Always // Always pass - we want to mark ALL fur pixels regardless of depth + ZWrite On // Write depth to prevent outline from rendering over fur + ZTest LEqual // Standard depth test Blend One Zero // Just overwrite HLSLPROGRAM #pragma target 4.5 #pragma require geometry + // Dissolve support + #pragma multi_compile_local _ _NILOTOON_DISSOLVE + #pragma multi_compile_local _ _NILOTOON_DITHER_FADEOUT + // GPU Instancing #pragma multi_compile_instancing #pragma instancing_options renderinglayer @@ -305,12 +386,15 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" #pragma multi_compile_instancing #pragma instancing_options renderinglayer #pragma multi_compile_fog + #pragma multi_compile_local _ _NILOTOON_DISSOLVE + #pragma multi_compile_local _ _NILOTOON_DITHER_FADEOUT #pragma vertex vert_outline #pragma fragment frag_outline #define NILOTOON_FUR_OUTLINE_PASS #include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl" + #include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Fragment.hlsl" Varyings vert_outline(Attributes input) { @@ -328,8 +412,27 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" outlineWidth *= fovFactor; float3 posOS = input.positionOS.xyz + input.normalOS * outlineWidth; + output.positionWS = TransformObjectToWorld(posOS); output.positionCS = TransformObjectToHClip(posOS); output.uv = input.uv; + + // Apply Perspective Removal to outline + output.positionCS = NiloDoPerspectiveRemoval( + output.positionCS, + output.positionWS, + _NiloToonGlobalPerCharHeadBonePosWSArray[_CharacterID], + _PerspectiveRemovalRadius, + _PerspectiveRemovalAmount, + _PerspectiveRemovalStartHeight, + _PerspectiveRemovalEndHeight + ); + + // Apply per-character Z offset + if (_PerCharacterZOffset != 0) + { + output.positionCS.z += _PerCharacterZOffset * output.positionCS.w; + } + output.fogFactor = ComputeFogFactor(output.positionCS.z); return output; @@ -337,6 +440,17 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" half4 frag_outline(Varyings input) : SV_Target { + // Apply Dither Fadeout to outline (FIRST) + #if _NILOTOON_DITHER_FADEOUT + NiloDoDitherFadeoutClip(input.positionCS.xy, 1.0 - _DitherFadeoutAmount * _AllowPerCharacterDitherFadeout); + #endif + + // Apply dissolve to outline + #if _NILOTOON_DISSOLVE + half3 dummyColor = half3(1, 1, 1); + ApplyDissolve(dummyColor, input.uv, input.positionWS, input.positionCS); + #endif + half4 color = _OutlineColor; color.rgb = MixFog(color.rgb, input.fogFactor); return color; @@ -362,12 +476,15 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" #pragma multi_compile_instancing #pragma instancing_options renderinglayer + #pragma multi_compile_local _ _NILOTOON_DISSOLVE + #pragma multi_compile_local _ _NILOTOON_DITHER_FADEOUT #pragma vertex ShadowPassVertex #pragma fragment ShadowPassFragment #define NILOTOON_FUR_SHADOW_PASS #include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl" + #include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Fragment.hlsl" float3 _LightDirection; float3 _LightPosition; @@ -401,7 +518,26 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" UNITY_TRANSFER_INSTANCE_ID(input, output); output.uv = input.uv; + output.positionWS = TransformObjectToWorld(input.positionOS.xyz); output.positionCS = GetShadowPositionHClip(input); + + // Apply Perspective Removal to shadow casting + output.positionCS = NiloDoPerspectiveRemoval( + output.positionCS, + output.positionWS, + _NiloToonGlobalPerCharHeadBonePosWSArray[_CharacterID], + _PerspectiveRemovalRadius, + _PerspectiveRemovalAmount, + _PerspectiveRemovalStartHeight, + _PerspectiveRemovalEndHeight + ); + + // Apply per-character Z offset + if (_PerCharacterZOffset != 0) + { + output.positionCS.z += _PerCharacterZOffset * output.positionCS.w; + } + return output; } @@ -409,6 +545,13 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" { half alpha = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv).a * _BaseColor.a; clip(alpha - _Cutoff); + + // Apply dissolve to shadow casting + #if _NILOTOON_DISSOLVE + half3 dummyColor = half3(1, 1, 1); + ApplyDissolve(dummyColor, input.uv, input.positionWS, input.positionCS); + #endif + return 0; } ENDHLSL @@ -431,12 +574,15 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" #pragma multi_compile_instancing #pragma instancing_options renderinglayer + #pragma multi_compile_local _ _NILOTOON_DISSOLVE + #pragma multi_compile_local _ _NILOTOON_DITHER_FADEOUT #pragma vertex DepthOnlyVertex #pragma fragment DepthOnlyFragment #define NILOTOON_FUR_DEPTH_PASS #include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl" + #include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Fragment.hlsl" Varyings DepthOnlyVertex(Attributes input) { @@ -445,7 +591,26 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" UNITY_TRANSFER_INSTANCE_ID(input, output); output.uv = input.uv; + output.positionWS = TransformObjectToWorld(input.positionOS.xyz); output.positionCS = TransformObjectToHClip(input.positionOS.xyz); + + // Apply Perspective Removal to depth + output.positionCS = NiloDoPerspectiveRemoval( + output.positionCS, + output.positionWS, + _NiloToonGlobalPerCharHeadBonePosWSArray[_CharacterID], + _PerspectiveRemovalRadius, + _PerspectiveRemovalAmount, + _PerspectiveRemovalStartHeight, + _PerspectiveRemovalEndHeight + ); + + // Apply per-character Z offset + if (_PerCharacterZOffset != 0) + { + output.positionCS.z += _PerCharacterZOffset * output.positionCS.w; + } + return output; } @@ -453,6 +618,13 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" { half alpha = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv).a * _BaseColor.a; clip(alpha - _Cutoff); + + // Apply dissolve to depth + #if _NILOTOON_DISSOLVE + half3 dummyColor = half3(1, 1, 1); + ApplyDissolve(dummyColor, input.uv, input.positionWS, input.positionCS); + #endif + return input.positionCS.z; } ENDHLSL @@ -474,12 +646,15 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" #pragma multi_compile_instancing #pragma instancing_options renderinglayer + #pragma multi_compile_local _ _NILOTOON_DISSOLVE + #pragma multi_compile_local _ _NILOTOON_DITHER_FADEOUT #pragma vertex DepthNormalsVertex #pragma fragment DepthNormalsFragment #define NILOTOON_FUR_DEPTHNORMALS_PASS #include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl" + #include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Fragment.hlsl" Varyings DepthNormalsVertex(Attributes input) { @@ -488,8 +663,27 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" UNITY_TRANSFER_INSTANCE_ID(input, output); output.uv = input.uv; + output.positionWS = TransformObjectToWorld(input.positionOS.xyz); output.positionCS = TransformObjectToHClip(input.positionOS.xyz); output.normalWS = TransformObjectToWorldNormal(input.normalOS); + + // Apply Perspective Removal to depth normals + output.positionCS = NiloDoPerspectiveRemoval( + output.positionCS, + output.positionWS, + _NiloToonGlobalPerCharHeadBonePosWSArray[_CharacterID], + _PerspectiveRemovalRadius, + _PerspectiveRemovalAmount, + _PerspectiveRemovalStartHeight, + _PerspectiveRemovalEndHeight + ); + + // Apply per-character Z offset + if (_PerCharacterZOffset != 0) + { + output.positionCS.z += _PerCharacterZOffset * output.positionCS.w; + } + return output; } @@ -497,6 +691,13 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" { half alpha = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv).a * _BaseColor.a; clip(alpha - _Cutoff); + + // Apply dissolve to depth normals + #if _NILOTOON_DISSOLVE + half3 dummyColor = half3(1, 1, 1); + ApplyDissolve(dummyColor, input.uv, input.positionWS, input.positionCS); + #endif + return float4(normalize(input.normalWS) * 0.5 + 0.5, 0); } ENDHLSL @@ -520,12 +721,15 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" #pragma multi_compile_instancing #pragma instancing_options renderinglayer + #pragma multi_compile_local _ _NILOTOON_DISSOLVE + #pragma multi_compile_local _ _NILOTOON_DITHER_FADEOUT #pragma vertex PrepassBufferVertexBase #pragma fragment PrepassBufferFragmentBase #define NILOTOON_FUR_PREPASSBUFFER_PASS #include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl" + #include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Fragment.hlsl" Varyings PrepassBufferVertexBase(Attributes input) { @@ -535,7 +739,26 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); output.uv = input.uv; + output.positionWS = TransformObjectToWorld(input.positionOS.xyz); output.positionCS = TransformObjectToHClip(input.positionOS.xyz); + + // Apply Perspective Removal to prepass buffer + output.positionCS = NiloDoPerspectiveRemoval( + output.positionCS, + output.positionWS, + _NiloToonGlobalPerCharHeadBonePosWSArray[_CharacterID], + _PerspectiveRemovalRadius, + _PerspectiveRemovalAmount, + _PerspectiveRemovalStartHeight, + _PerspectiveRemovalEndHeight + ); + + // Apply per-character Z offset + if (_PerCharacterZOffset != 0) + { + output.positionCS.z += _PerCharacterZOffset * output.positionCS.w; + } + output.furLayer = -1; // Base mesh marker return output; } @@ -549,6 +772,12 @@ Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur" half alpha = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv).a * _BaseColor.a; clip(alpha - _Cutoff); + // Apply dissolve to prepass buffer + #if _NILOTOON_DISSOLVE + half3 dummyColor = half3(1, 1, 1); + ApplyDissolve(dummyColor, input.uv, input.positionWS, input.positionCS); + #endif + // Output: character visible area = 1 return float4(0, 1, 0, 1); } diff --git a/Assets/NiloToonURP/Shaders/NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Fragment.hlsl b/Assets/NiloToonURP/Shaders/NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Fragment.hlsl index 13f235ba..f47f8a30 100644 --- a/Assets/NiloToonURP/Shaders/NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Fragment.hlsl +++ b/Assets/NiloToonURP/Shaders/NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Fragment.hlsl @@ -22,6 +22,183 @@ half3 GetNormalFromMap(float2 uv, float3 normalWS, float4 tangentWS) #endif } +//------------------------------------------------------------------------------------------------------------------------------ +// Apply Dissolve (NiloToon Style) +//------------------------------------------------------------------------------------------------------------------------------ +void ApplyDissolve(inout half3 color, float2 uv, float3 positionWS, float4 positionCS) +{ +#if _NILOTOON_DISSOLVE + // Matching to NiloToonPerCharacterRenderController.cs's enum DissolveMode + #define DISSOLVEMODE_UV1 1 + #define DISSOLVEMODE_UV2 2 + #define DISSOLVEMODE_WorldSpaceNoise 3 + #define DISSOLVEMODE_WorldSpaceVerticalUpward 4 + #define DISSOLVEMODE_WorldSpaceVerticalDownward 5 + #define DISSOLVEMODE_ScreenSpaceNoise 6 + #define DISSOLVEMODE_ScreenSpaceVerticalUpward 7 + #define DISSOLVEMODE_ScreenSpaceVerticalDownward 8 + + half finalDissolveAmount = _DissolveAmount * _AllowPerCharacterDissolve; + if(finalDissolveAmount <= 0) return; + + float noiseStrength = _DissolveNoiseStrength * 0.1; + float2 uvTiling = float2(_DissolveThresholdMapTilingX, _DissolveThresholdMapTilingY); + + half dissolveMapThresholdMapValue = 0; + + if(_DissolveMode == DISSOLVEMODE_UV1) + { + float2 dissolveUV = uv * uvTiling; + dissolveMapThresholdMapValue = SAMPLE_TEXTURE2D(_DissolveThresholdMap, sampler_DissolveThresholdMap, dissolveUV).r; + } + else if(_DissolveMode == DISSOLVEMODE_WorldSpaceNoise) + { + float2 dissolveUV = positionWS.xz * uvTiling; + float noise = SAMPLE_TEXTURE2D(_DissolveThresholdMap, sampler_DissolveThresholdMap, dissolveUV).g * noiseStrength; + dissolveMapThresholdMapValue = SAMPLE_TEXTURE2D(_DissolveThresholdMap, sampler_DissolveThresholdMap, dissolveUV).r + noise; + } + else if(_DissolveMode == DISSOLVEMODE_WorldSpaceVerticalUpward) + { + float2 dissolveUV = positionWS.xz * uvTiling; + float noise = SAMPLE_TEXTURE2D(_DissolveThresholdMap, sampler_DissolveThresholdMap, dissolveUV).g * noiseStrength; + dissolveMapThresholdMapValue = saturate(positionWS.y * 0.1 + noise); + } + else if(_DissolveMode == DISSOLVEMODE_WorldSpaceVerticalDownward) + { + float2 dissolveUV = positionWS.xz * uvTiling; + float noise = SAMPLE_TEXTURE2D(_DissolveThresholdMap, sampler_DissolveThresholdMap, dissolveUV).g * noiseStrength; + dissolveMapThresholdMapValue = saturate((1 - positionWS.y * 0.1) + noise); + } + else if(_DissolveMode == DISSOLVEMODE_ScreenSpaceNoise) + { + float2 screenUV = positionCS.xy / positionCS.w; + float2 dissolveUV = screenUV * uvTiling; + float noise = SAMPLE_TEXTURE2D(_DissolveThresholdMap, sampler_DissolveThresholdMap, dissolveUV).g * noiseStrength; + dissolveMapThresholdMapValue = SAMPLE_TEXTURE2D(_DissolveThresholdMap, sampler_DissolveThresholdMap, dissolveUV).r + noise; + } + else if(_DissolveMode == DISSOLVEMODE_ScreenSpaceVerticalUpward) + { + float2 screenUV = positionCS.xy / positionCS.w; + float2 dissolveUV = screenUV * uvTiling; + float noise = SAMPLE_TEXTURE2D(_DissolveThresholdMap, sampler_DissolveThresholdMap, dissolveUV).g * noiseStrength; + dissolveMapThresholdMapValue = saturate(screenUV.y + noise); + } + else if(_DissolveMode == DISSOLVEMODE_ScreenSpaceVerticalDownward) + { + float2 screenUV = positionCS.xy / positionCS.w; + float2 dissolveUV = screenUV * uvTiling; + float noise = SAMPLE_TEXTURE2D(_DissolveThresholdMap, sampler_DissolveThresholdMap, dissolveUV).g * noiseStrength; + dissolveMapThresholdMapValue = saturate((1 - screenUV.y) + noise); + } + + half dissolve = dissolveMapThresholdMapValue - finalDissolveAmount; + + // Clip threshold + clip(dissolve - 0.0001); + + // HDR color tint to "near threshold area" + color = lerp(color, _DissolveBorderTintColor.rgb, smoothstep(finalDissolveAmount + _DissolveBorderRange, finalDissolveAmount, dissolveMapThresholdMapValue)); +#endif +} + +//------------------------------------------------------------------------------------------------------------------------------ +// Apply Per-Character Color Controls (Tint, Add, Desaturation, Lerp) +//------------------------------------------------------------------------------------------------------------------------------ +half3 ApplyPerCharacterColorControls(half3 color) +{ + // Base Color Multiply & Tint + color *= _PerCharacterBaseColorMultiply; + color *= _PerCharacterBaseColorTint.rgb; + + // Tint Color + color *= _PerCharacterTintColor.rgb; + + // Add Color + color += _PerCharacterAddColor.rgb; + + // Desaturation (matching NiloToonCharacter_Shared.hlsl line 4099) + color = lerp(color, Luminance(color), _PerCharEffectDesaturatePercentage); + + // Mul Add (already handled above with _PerCharacterTintColor and _PerCharacterAddColor) + + // Replace by Color / Lerp Color (matching NiloToonCharacter_Shared.hlsl line 4105) + // Uses alpha channel of _PerCharEffectLerpColor as the lerp amount + color.rgb = lerp(color.rgb, _PerCharEffectLerpColor.rgb, _PerCharEffectLerpColor.a); + + return color; +} + +//------------------------------------------------------------------------------------------------------------------------------ +// Apply BaseMap Override (matching NiloToonPerCharacterRenderController) +// Based on ApplyPostLightingPerCharacterBaseMapOverride from NiloToonCharacter_Shared.hlsl +//------------------------------------------------------------------------------------------------------------------------------ +half3 ApplyBaseMapOverride(half3 baseColor, float2 uv, float3 positionWS, float4 positionCS) +{ + // Sample texture with tiling/offset + float2 overrideUV = uv * _PerCharacterBaseMapOverrideMap_ST.xy + _PerCharacterBaseMapOverrideMap_ST.zw; + half4 texValue = SAMPLE_TEXTURE2D(_PerCharacterBaseMapOverrideMap, sampler_PerCharacterBaseMapOverrideMap, overrideUV); + + // Apply tint color + texValue.rgb *= _PerCharacterBaseMapOverrideTintColor; + + // KEY FIX: Multiply alpha by Amount (0-1 range) + // When Amount is 0, alpha becomes 0, making BlendColor() return baseColor unchanged + texValue.a *= _PerCharacterBaseMapOverrideAmount; + + // Create RGBA versions for BlendColor function + half4 originalColorRGBA = half4(baseColor.r, baseColor.g, baseColor.b, 1); + half4 resultColorRGBA = BlendColor(originalColorRGBA, texValue, _PerCharacterBaseMapOverrideBlendMode); + + // Return only RGB (don't modify alpha) + return resultColorRGBA.rgb; +} + +//------------------------------------------------------------------------------------------------------------------------------ +// Apply Character Area Color Fill +// Based on NiloToonCharacterAreaColorFillFragmentFunction from NiloToonCharacter.shader +//------------------------------------------------------------------------------------------------------------------------------ +half3 ApplyCharacterAreaColorFill(half3 color, float2 uv, float3 positionWS, float4 positionCS) +{ + // Sample texture with tiling/offset + float2 fillUV = uv * _CharacterAreaColorFillTexture_ST.xy + _CharacterAreaColorFillTexture_ST.zw; + half4 fillTexSample = SAMPLE_TEXTURE2D(_CharacterAreaColorFillTexture, sampler_CharacterAreaColorFillTexture, fillUV); + + // Apply color tint + half4 resultColor = _CharacterAreaColorFillColor * fillTexSample; + + // KEY FIX: Multiply alpha by _ShouldRenderCharacterAreaColorFill (acts as enabled flag) + // When disabled (0), alpha becomes 0, so the color addition has no effect + // Note: Original shader also multiplies by renderArea, but we don't have prepass buffer in fur shader, + // so we'll just use the enabled flag + resultColor.a *= _ShouldRenderCharacterAreaColorFill; + + // Add the color based on alpha (when disabled, alpha is 0 so nothing is added) + color += resultColor.rgb * resultColor.a; + + return color; +} + +//------------------------------------------------------------------------------------------------------------------------------ +// Apply Dither Opacity (screen-space dithering for transparency) +//------------------------------------------------------------------------------------------------------------------------------ +void ApplyDitherOpacity(float4 positionCS, half opacity) +{ + if (opacity >= 1.0) return; + + // 4x4 Bayer matrix for dithering + const float4x4 thresholdMatrix = float4x4( + 1.0 / 17.0, 9.0 / 17.0, 3.0 / 17.0, 11.0 / 17.0, + 13.0 / 17.0, 5.0 / 17.0, 15.0 / 17.0, 7.0 / 17.0, + 4.0 / 17.0, 12.0 / 17.0, 2.0 / 17.0, 10.0 / 17.0, + 16.0 / 17.0, 8.0 / 17.0, 14.0 / 17.0, 6.0 / 17.0 + ); + + int2 pixelPos = int2(positionCS.xy) % 4; + float threshold = thresholdMatrix[pixelPos.x][pixelPos.y]; + + clip(opacity - threshold); +} + //------------------------------------------------------------------------------------------------------------------------------ // Apply All Effects (MatCap, Rim, Emission) //------------------------------------------------------------------------------------------------------------------------------ @@ -56,8 +233,20 @@ half3 ApplyEffects( if (furLayer < 0) // Only for base pass { half NdotV = saturate(dot(normalWS, viewDirWS)); - half rim = pow(1.0 - NdotV, _RimLightPower); - color += rim * _RimLightColor.rgb * _RimLightIntensity * lightColor; + half rimPower = _RimLightPower; + half rimIntensity = _RimLightIntensity; + half3 rimColor = _RimLightColor.rgb; + + // Apply per-character rim light override if enabled + if (_UsePerCharacterRimLightIntensity > 0.5) + { + rimPower = _PerCharacterRimLightSharpnessPower; + rimIntensity = _PerCharacterRimLightIntensity; + rimColor = _PerCharacterRimLightColor.rgb; + } + + half rim = pow(1.0 - NdotV, rimPower); + color += rim * rimColor * rimIntensity * lightColor; } #endif @@ -87,6 +276,17 @@ half4 frag(Varyings input) : SV_Target // Alpha cutoff clip(color.a - _Cutoff); + // Apply Dither Fadeout (FIRST - affects visibility) + #if _NILOTOON_DITHER_FADEOUT + NiloDoDitherFadeoutClip(input.positionCS.xy, 1.0 - _DitherFadeoutAmount * _AllowPerCharacterDitherFadeout); + #endif + + // Apply dissolve (after dither fadeout) + // This will clip pixels and apply border glow + #if _NILOTOON_DISSOLVE + ApplyDissolve(color.rgb, input.uv, input.positionWS, input.positionCS); + #endif + // Get normal (with normal map if enabled) float3 normalWS = GetNormalFromMap(input.uv, input.normalWS, input.tangentWS); @@ -109,6 +309,9 @@ half4 frag(Varyings input) : SV_Target occlusion = lerp(1.0, SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, input.uv).r, _OcclusionStrength); #endif + // Apply BaseMap Override (before shading) + color.rgb = ApplyBaseMapOverride(color.rgb, input.uv, input.positionWS, input.positionCS); + // Apply NiloToon cel shading color.rgb = ApplyNiloToonCelShading( color.rgb, @@ -123,6 +326,15 @@ half4 frag(Varyings input) : SV_Target // Apply additional effects color.rgb = ApplyEffects(color.rgb, input.uv, normalWS, viewDirWS, mainLight.color, input.furLayer); + // Apply per-character color controls + color.rgb = ApplyPerCharacterColorControls(color.rgb); + + // Apply character area color fill + color.rgb = ApplyCharacterAreaColorFill(color.rgb, input.uv, input.positionWS, input.positionCS); + + // Apply dither opacity + ApplyDitherOpacity(input.positionCS, _DitherOpacity); + // Apply fog color.rgb = MixFog(color.rgb, input.fogFactor); @@ -186,6 +398,20 @@ half4 ComputeFurColor(Varyings input, out half furAlpha) // Minimum alpha threshold clip(furAlpha - 0.05); + // Apply Dither Fadeout (FIRST - affects visibility) + #if _NILOTOON_DITHER_FADEOUT + NiloDoDitherFadeoutClip(input.positionCS.xy, 1.0 - _DitherFadeoutAmount * _AllowPerCharacterDitherFadeout); + #endif + + // Apply dissolve (after dither fadeout) + // This will clip pixels and apply border glow + #if _NILOTOON_DISSOLVE + ApplyDissolve(color.rgb, input.uv, input.positionWS, input.positionCS); + #endif + + // Apply BaseMap Override (before shading) + color.rgb = ApplyBaseMapOverride(color.rgb, input.uv, input.positionWS, input.positionCS); + // Get normal (with normal map if enabled) float3 normalWS = GetNormalFromMap(input.uv, input.normalWS, input.tangentWS); @@ -260,6 +486,15 @@ half4 ComputeFurColor(Varyings input, out half furAlpha) color.rgb += emission; #endif + // Apply per-character color controls + color.rgb = ApplyPerCharacterColorControls(color.rgb); + + // Apply character area color fill + color.rgb = ApplyCharacterAreaColorFill(color.rgb, input.uv, input.positionWS, input.positionCS); + + // Apply dither opacity + ApplyDitherOpacity(input.positionCS, _DitherOpacity); + // Apply fog color.rgb = MixFog(color.rgb, input.fogFactor); @@ -323,6 +558,17 @@ half4 frag_fur_mask(Varyings input) : SV_Target // Clip pixels that don't pass threshold (same as main fur rendering) clip(furAlpha - 0.05); + // Apply Dither Fadeout (mask pass must also respect dither fadeout) + #if _NILOTOON_DITHER_FADEOUT + NiloDoDitherFadeoutClip(input.positionCS.xy, 1.0 - _DitherFadeoutAmount * _AllowPerCharacterDitherFadeout); + #endif + + // Apply dissolve clipping (mask pass must also respect dissolve) + #if _NILOTOON_DISSOLVE + half3 dummyColor = half3(1, 1, 1); + ApplyDissolve(dummyColor, input.uv, input.positionWS, float4(0, 0, 0, 1)); + #endif + // Output to PrepassBuffer format: // G channel = character visible area (unified mask for face, body, and fur) // All character areas use G channel for consistent masking diff --git a/Assets/NiloToonURP/Shaders/NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Geometry.hlsl b/Assets/NiloToonURP/Shaders/NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Geometry.hlsl index 8a960389..08896fa7 100644 --- a/Assets/NiloToonURP/Shaders/NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Geometry.hlsl +++ b/Assets/NiloToonURP/Shaders/NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Geometry.hlsl @@ -39,44 +39,87 @@ void AppendFurShell( float3 basePositionWS = LerpBary(input[0].positionWS, input[1].positionWS, input[2].positionWS, factor); output.positionWS = basePositionWS; - output.positionCS = TransformWorldToHClip(basePositionWS); + float4 basePositionCS_BeforePerspective = TransformWorldToHClip(basePositionWS); output.furLayer = 0; - // Clipping canceller for near plane - #if defined(UNITY_REVERSED_Z) - if(output.positionCS.w < _ProjectionParams.y * 1.01 && output.positionCS.w > 0) - { - output.positionCS.z = output.positionCS.z * 0.0001 + output.positionCS.w * 0.999; - } - #else - if(output.positionCS.w < _ProjectionParams.y * 1.01 && output.positionCS.w > 0) - { - output.positionCS.z = output.positionCS.z * 0.0001 - output.positionCS.w * 0.999; - } - #endif + // Apply Perspective Removal to base + float4 basePositionCS_AfterPerspective = NiloDoPerspectiveRemoval( + basePositionCS_BeforePerspective, + basePositionWS, + _NiloToonGlobalPerCharHeadBonePosWSArray[_CharacterID], + _PerspectiveRemovalRadius, + _PerspectiveRemovalAmount, + _PerspectiveRemovalStartHeight, + _PerspectiveRemovalEndHeight + ); + + output.positionCS = basePositionCS_AfterPerspective; + + // Apply per-character Z offset + if (_PerCharacterZOffset != 0) + { + output.positionCS.z += _PerCharacterZOffset * output.positionCS.w; + } outStream.Append(output); // Tip position (furLayer = 1) - float3 mixedFurVector = LerpBary(furVectors[0], furVectors[1], furVectors[2], factor); - float3 tipPositionWS = basePositionWS + mixedFurVector; + // CRITICAL FIX: Transform fur vector to clip space using the SAME perspective removal parameters as base + // Get the interpolated fur vector in world space + float3 mixedFurVector = LerpBary(furVectors[0], furVectors[1], furVectors[2], factor); + + // Calculate tip in world space (for lighting and shading) + float3 tipPositionWS = basePositionWS + mixedFurVector; output.positionWS = tipPositionWS; - output.positionCS = TransformWorldToHClip(tipPositionWS); + + // Transform fur vector to clip space by transforming both endpoints + // IMPORTANT: Don't apply perspective removal to tip separately! + // Instead, transform the fur vector in clip space relative to the base + + // Convert the fur vector direction to clip space + // We approximate this by transforming a point at base + furVector + float4 furVectorEndCS_BeforePerspective = TransformWorldToHClip(tipPositionWS); + + // Calculate the fur vector in clip space BEFORE perspective removal + float4 furVectorCS_BeforePerspective = furVectorEndCS_BeforePerspective - basePositionCS_BeforePerspective; + + // Apply the same perspective removal transformation to the fur vector as we did to the base + // This maintains the correct relative offset in perspective-removed space + // The key insight: we want to preserve the fur direction/length relative to the base's transformation + + // Transform the fur vector's XY component using the base's perspective removal scale + // Perspective removal formula: newXY = lerp(originalXY, originalXY * w / centerPosVSz, amount) + // For the fur vector, we need to apply the same scale change + + float3 centerPosWS = _NiloToonGlobalPerCharHeadBonePosWSArray[_CharacterID]; + float centerPosVSz = mul(UNITY_MATRIX_V, float4(centerPosWS,1)).z; + + // Calculate perspective removal amount for base position + float perspectiveRemovalAreaSphere = saturate(_PerspectiveRemovalRadius - distance(basePositionWS, centerPosWS) / _PerspectiveRemovalRadius); + float perspectiveRemovalAreaWorldHeight = saturate(invLerp(_PerspectiveRemovalStartHeight, _PerspectiveRemovalEndHeight, basePositionWS.y)); + float perspectiveRemovalFinalAmount = _PerspectiveRemovalAmount * perspectiveRemovalAreaSphere * perspectiveRemovalAreaWorldHeight; + + // Apply perspective removal scale to fur vector + // The scale factor is: lerp(1, w / centerPosVSz, amount) + float basePerspectiveScale = lerp(1.0, abs(basePositionCS_BeforePerspective.w) * rcp(abs(centerPosVSz)), perspectiveRemovalFinalAmount); + + // Scale the fur vector's XY by the same factor + float2 furVectorCS_XY = furVectorCS_BeforePerspective.xy * basePerspectiveScale; + + // Construct the final tip position in clip space + output.positionCS = basePositionCS_AfterPerspective; + output.positionCS.xy += furVectorCS_XY; + output.positionCS.z = furVectorEndCS_BeforePerspective.z; // Keep original Z depth for tip + output.positionCS.w = furVectorEndCS_BeforePerspective.w; // Keep original W for tip + output.furLayer = layerProgress; - // Clipping canceller for near plane - #if defined(UNITY_REVERSED_Z) - if(output.positionCS.w < _ProjectionParams.y * 1.01 && output.positionCS.w > 0) - { - output.positionCS.z = output.positionCS.z * 0.0001 + output.positionCS.w * 0.999; - } - #else - if(output.positionCS.w < _ProjectionParams.y * 1.01 && output.positionCS.w > 0) - { - output.positionCS.z = output.positionCS.z * 0.0001 - output.positionCS.w * 0.999; - } - #endif + // Apply per-character Z offset + if (_PerCharacterZOffset != 0) + { + output.positionCS.z += _PerCharacterZOffset * output.positionCS.w; + } outStream.Append(output); } diff --git a/Assets/NiloToonURP/Shaders/NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl b/Assets/NiloToonURP/Shaders/NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl index a5da66f0..a958ffbf 100644 --- a/Assets/NiloToonURP/Shaders/NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl +++ b/Assets/NiloToonURP/Shaders/NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl @@ -8,6 +8,12 @@ #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl" +// Include NiloToon utility functions (order matters - InvLerpRemapUtil must come before PerspectiveRemovalUtil) +#include "../../ShaderLibrary/NiloUtilityHLSL/NiloInvLerpRemapUtil.hlsl" +#include "../../ShaderLibrary/NiloUtilityHLSL/NiloBlendEquationUtil.hlsl" +#include "../../ShaderLibrary/NiloUtilityHLSL/NiloDitherFadeoutClipUtil.hlsl" +#include "../../ShaderLibrary/NiloUtilityHLSL/NiloPerspectiveRemovalUtil.hlsl" + //------------------------------------------------------------------------------------------------------------------------------ // NiloToon Global Light Override Variables (from NiloToonCharacterMainLightOverrider) //------------------------------------------------------------------------------------------------------------------------------ @@ -128,8 +134,77 @@ CBUFFER_START(UnityPerMaterial) // Rendering half _Cull; half _FurCull; + + // Dissolve + float _DissolveAmount; + float _DissolveMode; + float _DissolveThresholdMapTilingX; + float _DissolveThresholdMapTilingY; + float _DissolveNoiseStrength; + float _DissolveBorderRange; + half4 _DissolveBorderTintColor; + float _AllowPerCharacterDissolve; + + // Per-Character Color Controls + half _PerCharacterBaseColorMultiply; + half4 _PerCharacterBaseColorTint; + half4 _PerCharacterTintColor; + half4 _PerCharacterAddColor; + half _PerCharEffectDesaturatePercentage; + half4 _PerCharEffectLerpColor; + + // Per-Character Rim Light + half _UsePerCharacterRimLightIntensity; + half _PerCharacterRimLightIntensity; + half4 _PerCharacterRimLightColor; + half _PerCharacterRimLightSharpnessPower; + + // BaseMap Override + half _PerCharacterBaseMapOverrideAmount; + half4 _PerCharacterBaseMapOverrideTintColor; + float4 _PerCharacterBaseMapOverrideMap_ST; + half _PerCharacterBaseMapOverrideBlendMode; + half _PerCharacterBaseMapOverrideUVOption; + + // Character Area Color Fill + half _ShouldRenderCharacterAreaColorFill; + half4 _CharacterAreaColorFillColor; + float4 _CharacterAreaColorFillTexture_ST; + half _CharacterAreaColorFillUVOption; + + // Dither Opacity + half _DitherOpacity; + + // Dither Fadeout + half _DitherFadeoutAmount; + half _DitherFadeoutNormalScaleFix; + half _AllowPerCharacterDitherFadeout; + + // Perspective Removal + half _PerspectiveRemovalAmount; + half _PerspectiveRemovalRadius; + half _PerspectiveRemovalStartHeight; + half _PerspectiveRemovalEndHeight; + + // ZOffset + half _PerCharacterZOffset; + + // Character ID (for accessing global arrays) + uint _CharacterID; CBUFFER_END +//------------------------------------------------------------------------------------------------------------------------------ +// NiloToon Global Per-Character Arrays +// For properties that change every frame, use global arrays instead of CBUFFER for performance +//------------------------------------------------------------------------------------------------------------------------------ +#ifndef MAX_CHARACTER_COUNT +#define MAX_CHARACTER_COUNT 256 +#endif + +float3 _NiloToonGlobalPerCharHeadBonePosWSArray[MAX_CHARACTER_COUNT]; +float3 _NiloToonGlobalPerCharFaceForwardDirWSArray[MAX_CHARACTER_COUNT]; +float3 _NiloToonGlobalPerCharFaceUpwardDirWSArray[MAX_CHARACTER_COUNT]; + //------------------------------------------------------------------------------------------------------------------------------ // Texture Declarations (all textures declared unconditionally to avoid compilation issues) //------------------------------------------------------------------------------------------------------------------------------ @@ -156,6 +231,17 @@ TEXTURE2D(_OcclusionMap); SAMPLER(sampler_OcclusionMap); // Outline TEXTURE2D(_OutlineWidthMask); SAMPLER(sampler_OutlineWidthMask); +// Dissolve +#if _NILOTOON_DISSOLVE +TEXTURE2D(_DissolveThresholdMap); SAMPLER(sampler_DissolveThresholdMap); +#endif + +// BaseMap Override +TEXTURE2D(_PerCharacterBaseMapOverrideMap); SAMPLER(sampler_PerCharacterBaseMapOverrideMap); + +// Character Area Color Fill +TEXTURE2D(_CharacterAreaColorFillTexture); SAMPLER(sampler_CharacterAreaColorFillTexture); + //------------------------------------------------------------------------------------------------------------------------------ // Vertex Input Structure //------------------------------------------------------------------------------------------------------------------------------ @@ -386,6 +472,23 @@ Varyings vert(Attributes input) output.tangentWS = float4(normalInput.tangentWS, input.tangentOS.w); output.uv = TRANSFORM_TEX(input.uv, _BaseMap); + // Apply Perspective Removal + output.positionCS = NiloDoPerspectiveRemoval( + output.positionCS, + output.positionWS, + _NiloToonGlobalPerCharHeadBonePosWSArray[_CharacterID], + _PerspectiveRemovalRadius, + _PerspectiveRemovalAmount, + _PerspectiveRemovalStartHeight, + _PerspectiveRemovalEndHeight + ); + + // Apply per-character Z offset + if (_PerCharacterZOffset != 0) + { + output.positionCS.z += _PerCharacterZOffset * output.positionCS.w; + } + // Shadow coord output.shadowCoord = GetShadowCoordSafe(vertexInput); @@ -412,9 +515,11 @@ V2G vert_fur(Attributes input) UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); // Transform position and normal to world space + // IMPORTANT: Use GetVertexPositionInputs to match Base Pass transformation exactly + VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz); VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS); - output.positionWS = TransformObjectToWorld(input.positionOS.xyz); + output.positionWS = vertexInput.positionWS; output.normalWS = normalInput.normalWS; output.tangentWS = float4(normalInput.tangentWS, input.tangentOS.w); output.uv = TRANSFORM_TEX(input.uv, _BaseMap); @@ -429,6 +534,11 @@ V2G vert_fur(Attributes input) float furLengthMask = SAMPLE_TEXTURE2D_LOD(_FurLengthMask, sampler_FurLengthMask, input.uv * _FurLengthMask_ST.xy + _FurLengthMask_ST.zw, 0).r; output.furVector *= furLengthMask; + // Note: Perspective Removal and Z offset will be applied in geometry shader + // We don't apply them here in vertex shader for fur shells + // because geometry shader generates multiple shells and needs to apply + // the transformations consistently to all generated vertices + // Fog float4 posCS = TransformWorldToHClip(output.positionWS); output.fogFactor = ComputeFogFactor(posCS.z); diff --git a/Assets/NiloToonURP/Shaders/NiloToonCharacterFur_ShellOnly.shader b/Assets/NiloToonURP/Shaders/NiloToonCharacterFur_ShellOnly.shader deleted file mode 100644 index 50845aa8..00000000 --- a/Assets/NiloToonURP/Shaders/NiloToonCharacterFur_ShellOnly.shader +++ /dev/null @@ -1,156 +0,0 @@ -// NiloToon Character Fur Shell Only Shader -// Use this shader on a second material slot to render fur shells -// The base mesh should use NiloToonCharacter or NiloToonCharacterFur shader - -Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur_ShellOnly" -{ - Properties - { - [Header(Base Color)] - [Space(5)] - [MainTexture] _BaseMap("Base Map", 2D) = "white" {} - [HDR][MainColor] _BaseColor("Base Color", Color) = (1,1,1,1) - _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5 - - [Header(Normal Map)] - [Space(5)] - [Toggle(_NORMALMAP)] _UseNormalMap("Enable Normal Map", Float) = 0 - [Normal] _BumpMap("Normal Map", 2D) = "bump" {} - _BumpScale("Normal Scale", Range(0, 2)) = 1.0 - - [Header(Fur Shape)] - [Space(5)] - _FurNoiseMask("Fur Noise Mask", 2D) = "white" {} - _FurMask("Fur Mask (where fur appears)", 2D) = "white" {} - _FurLengthMask("Fur Length Mask", 2D) = "white" {} - _FurVector("Fur Direction (XYZ) + Length (W)", Vector) = (0, 0, 1, 0.02) - [Normal] _FurVectorTex("Fur Direction Map (Normal)", 2D) = "bump" {} - _FurVectorScale("Fur Direction Scale", Range(-10, 10)) = 1.0 - _FurGravity("Fur Gravity", Range(0, 1)) = 0.25 - _FurRandomize("Fur Randomize", Range(0, 1)) = 0.1 - _FurAO("Fur Ambient Occlusion", Range(0, 1)) = 0.5 - - [Header(Shell Layers)] - [Space(5)] - [IntRange] _FurLayerNum("Fur Layer Count", Range(1, 3)) = 2 - _FurRootOffset("Fur Root Offset", Range(-1, 0)) = 0 - - [Header(Fur Rim Light)] - [Space(5)] - [HDR] _FurRimColor("Fur Rim Color", Color) = (1, 1, 1, 1) - _FurRimFresnelPower("Fur Rim Fresnel Power", Range(0.01, 50)) = 3.0 - _FurRimAntiLight("Fur Rim Anti-Light", Range(0, 1)) = 0.5 - - [Header(Cel Shading)] - [Space(5)] - _CelShadeMidPoint("Cel Shade Mid Point", Range(-1, 1)) = 0 - _CelShadeSoftness("Cel Shade Softness", Range(0, 1)) = 0.1 - - [Header(Shadow Color)] - [Space(5)] - [Toggle(_SHADOW_COLOR)] _EnableShadowColor("Enable Shadow Color", Float) = 0 - [HDR] _ShadowColor("Shadow Tint Color", Color) = (1, 1, 1, 1) - _ShadowBrightness("Shadow Brightness", Range(0, 2)) = 1.0 - _ShadowHueShift("Shadow Hue Shift", Range(-0.5, 0.5)) = 0 - _ShadowSaturationBoost("Shadow Saturation Boost", Range(0, 2)) = 1.0 - _ShadowValueMultiplier("Shadow Value Multiplier", Range(0, 2)) = 1.0 - - [Header(MatCap Additive)] - [Space(5)] - [Toggle(_MATCAP_ADD)] _UseMatCapAdd("Enable MatCap (Add)", Float) = 0 - _MatCapAddMap("MatCap Add Map", 2D) = "black" {} - [HDR] _MatCapAddColor("MatCap Add Color", Color) = (1, 1, 1, 1) - _MatCapAddIntensity("MatCap Add Intensity", Range(0, 5)) = 1.0 - _MatCapAddMask("MatCap Add Mask", 2D) = "white" {} - - [Header(MatCap Multiply)] - [Space(5)] - [Toggle(_MATCAP_MUL)] _UseMatCapMul("Enable MatCap (Multiply)", Float) = 0 - _MatCapMulMap("MatCap Multiply Map", 2D) = "white" {} - _MatCapMulIntensity("MatCap Multiply Intensity", Range(0, 2)) = 1.0 - - [Header(Emission)] - [Space(5)] - [Toggle(_EMISSION)] _UseEmission("Enable Emission", Float) = 0 - _EmissionMap("Emission Map", 2D) = "white" {} - [HDR] _EmissionColor("Emission Color", Color) = (0, 0, 0, 1) - _EmissionIntensity("Emission Intensity", Range(0, 10)) = 1.0 - - [Header(Occlusion)] - [Space(5)] - [Toggle(_OCCLUSIONMAP)] _UseOcclusion("Enable Occlusion Map", Float) = 0 - _OcclusionMap("Occlusion Map", 2D) = "white" {} - _OcclusionStrength("Occlusion Strength", Range(0, 1)) = 1.0 - - [Header(Outline)] - [Space(5)] - _OutlineWidth("Outline Width", Range(0, 10)) = 0 - [HDR] _OutlineColor("Outline Color", Color) = (0, 0, 0, 1) - _OutlineWidthMask("Outline Width Mask", 2D) = "white" {} - - [Header(Rendering Options)] - [Space(5)] - [Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull Mode", Float) = 2 - [Enum(UnityEngine.Rendering.CullMode)] _FurCull("Fur Cull Mode", Float) = 0 - [Enum(Off, 0, On, 1)] _ZWrite("ZWrite", Float) = 0 - _FurZWrite("Fur ZWrite", Float) = 0 - [Enum(UnityEngine.Rendering.CompareFunction)] _ZTest("ZTest", Float) = 4 - } - - SubShader - { - Tags - { - "RenderType" = "Transparent" - "RenderPipeline" = "UniversalPipeline" - "IgnoreProjector" = "True" - "Queue" = "Transparent-50" - } - LOD 300 - - // Fur Shell Pass - Pass - { - Name "FurShell" - Tags { "LightMode" = "UniversalForward" } - - Cull [_FurCull] - ZWrite [_FurZWrite] - ZTest LEqual - Blend SrcAlpha OneMinusSrcAlpha, One OneMinusSrcAlpha - - HLSLPROGRAM - #pragma target 4.5 - #pragma require geometry - - #pragma shader_feature_local _NORMALMAP - #pragma shader_feature_local _SHADOW_COLOR - #pragma shader_feature_local _MATCAP_ADD - #pragma shader_feature_local _MATCAP_MUL - #pragma shader_feature_local _RIMLIGHT - #pragma shader_feature_local _EMISSION - #pragma shader_feature_local _OCCLUSIONMAP - - #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN - #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS - #pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS - #pragma multi_compile_fragment _ _SHADOWS_SOFT - #pragma multi_compile_fog - - #pragma multi_compile_instancing - #pragma instancing_options renderinglayer - - #pragma vertex vert_fur - #pragma geometry geom_fur - #pragma fragment frag_fur - - #define NILOTOON_FUR_SHELL_PASS - #include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl" - #include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Geometry.hlsl" - #include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Fragment.hlsl" - ENDHLSL - } - } - - FallBack Off -} diff --git a/Assets/NiloToonURP/Shaders/NiloToonCharacterFur_ShellOnly.shader.meta b/Assets/NiloToonURP/Shaders/NiloToonCharacterFur_ShellOnly.shader.meta deleted file mode 100644 index 7a5f0d87..00000000 --- a/Assets/NiloToonURP/Shaders/NiloToonCharacterFur_ShellOnly.shader.meta +++ /dev/null @@ -1,9 +0,0 @@ -fileFormatVersion: 2 -guid: 9b3349963a9dea04da1b88df137b8d82 -ShaderImporter: - externalObjects: {} - defaultTextures: [] - nonModifiableTextures: [] - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Settings/Streamingle Render Pipeline Asset_Renderer.asset b/Assets/Resources/Settings/Streamingle Render Pipeline Asset_Renderer.asset index 627adf5e..3e46f1c8 100644 --- a/Assets/Resources/Settings/Streamingle Render Pipeline Asset_Renderer.asset +++ b/Assets/Resources/Settings/Streamingle Render Pipeline Asset_Renderer.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ebeddcbf60234fff8794171b515b7f1c1d07866479e74f1c3c35d04e50513af9 -size 17848 +oid sha256:db7a7df30a6e4e2e93d141602389866fe06ff8676cf9c37facbe769e15beb0fb +size 18464