From d4a703fffb2de50d43c25c4b607b7c7b252fd70e Mon Sep 17 00:00:00 2001 From: yum Date: Mon, 15 Jul 2024 00:36:05 -0700 Subject: Add Toon shading mode Same as "realistic" except spherical harmonics all get the same normal. Approach derived from Poiyomi Toon shader. --- Editor/tooner.cs | 3 ++- pbr.cginc | 46 ++++++++++++++++++++++++---------------------- poi.cginc | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 23 deletions(-) diff --git a/Editor/tooner.cs b/Editor/tooner.cs index e7fdf6b..adc0fca 100644 --- a/Editor/tooner.cs +++ b/Editor/tooner.cs @@ -476,7 +476,8 @@ public class ToonerGUI : ShaderGUI { enum NormalsMode { Flat, Spherical, - Realistic + Realistic, + Toon }; void DoShadingMode() { diff --git a/pbr.cginc b/pbr.cginc index 44e0b88..581df3f 100644 --- a/pbr.cginc +++ b/pbr.cginc @@ -97,12 +97,11 @@ float3 BoxProjection ( float4 cubemapPosition, float3 boxMin, float3 boxMax ) { #if UNITY_SPECCUBE_BOX_PROJECTION - UNITY_BRANCH if (cubemapPosition.w > 0) { float3 factors = ((direction > 0 ? boxMax : boxMin) - position) / direction; float scalar = min(min(factors.x, factors.y), factors.z); - direction = direction * scalar + (position - cubemapPosition); + direction = direction * scalar + (position - cubemapPosition.xyz); } #endif return direction; @@ -120,7 +119,11 @@ UnityIndirect CreateIndirectLight(float4 vertexLightColor, float3 view_dir, floa // Avatars are not static, don't use lightmap. indirect.diffuse = 0; #else - indirect.diffuse += max(0, ShadeSH9(float4(normal, 1))); + if (_Mesh_Normals_Mode == 3) { // Toon + indirect.diffuse += max(0, BetterSH9(float4(0, 0, 0, 1))); + } else { + indirect.diffuse += max(0, BetterSH9(float4(normal, 1))); + } #endif float3 reflect_dir = reflect(-view_dir, normal); Unity_GlossyEnvironmentData env_data; @@ -133,38 +136,37 @@ UnityIndirect CreateIndirectLight(float4 vertexLightColor, float3 view_dir, floa float3 probe0 = Unity_GlossyEnvironment( UNITY_PASS_TEXCUBE(unity_SpecCube0), unity_SpecCube0_HDR, env_data ); - env_data.reflUVW = BoxProjection( - reflect_dir, worldPos, - unity_SpecCube1_ProbePosition, - unity_SpecCube1_BoxMin, unity_SpecCube1_BoxMax - ); + indirect.specular = probe0; #if UNITY_SPECCUBE_BLENDING - float interpolator = unity_SpecCube0_BoxMin.w; - UNITY_BRANCH - if (interpolator < 0.99999) { - float3 probe1 = Unity_GlossyEnvironment( - UNITY_PASS_TEXCUBE_SAMPLER(unity_SpecCube1, unity_SpecCube0), - unity_SpecCube0_HDR, env_data - ); - indirect.specular = lerp(probe1, probe0, interpolator); - } - else { - indirect.specular = probe0; - } + if (unity_SpecCube0_BoxMin.w < 0.99999) { + env_data.reflUVW = BoxProjection( + reflect_dir, worldPos, + unity_SpecCube1_ProbePosition, + unity_SpecCube1_BoxMin, unity_SpecCube1_BoxMax + ); + float3 probe1 = Unity_GlossyEnvironment( + UNITY_PASS_TEXCUBE_SAMPLER(unity_SpecCube1, unity_SpecCube0), + unity_SpecCube0_HDR, env_data + ); + indirect.specular = lerp(probe1, probe0, unity_SpecCube0_BoxMin.w); + } #else indirect.specular = probe0; #endif // UNITY_SPECCUBE_BLENDING + // Lifted from poi toon shader (MIT). + float horizon = min(1 + dot(reflect_dir, normal), 1); + indirect.specular *= horizon * horizon; + #if defined(_CUBEMAP) float roughness = GetRoughness(smoothness); - probe0 = + indirect.specular = UNITY_SAMPLE_TEXCUBE_LOD( _Cubemap, reflect_dir, roughness * UNITY_SPECCUBE_LOD_STEPS); #endif // _CUBEMAP - indirect.specular = probe0; #endif // FORWARD_BASE_PASS indirect.diffuse *= ao; diff --git a/poi.cginc b/poi.cginc index 5e31a42..1d2b846 100644 --- a/poi.cginc +++ b/poi.cginc @@ -57,4 +57,44 @@ float3 HSVtoRGB(in float3 HSV) return ((RGB - 1) * HSV.y + 1) * HSV.z; } +float shEvaluateDiffuseL1Geomerics_local(float L0, float3 L1, float3 n) +{ + // average energy + float R0 = max(0, L0); + + // avg direction of incoming light + float3 R1 = 0.5f * L1; + + // directional brightness + float lenR1 = length(R1); + + // linear angle between normal and direction 0-1 + //float q = 0.5f * (1.0f + dot(R1 / lenR1, n)); + //float q = dot(R1 / lenR1, n) * 0.5 + 0.5; + float q = dot(normalize(R1), n) * 0.5 + 0.5; + q = saturate(q); // Thanks to ScruffyRuffles for the bug identity. + + // power for q + // lerps from 1 (linear) to 3 (cubic) based on directionality + float p = 1.0f + 2.0f * lenR1 / R0; + + // dynamic range constant + // should vary between 4 (highly directional) and 0 (ambient) + float a = (1.0f - lenR1 / R0) / (1.0f + lenR1 / R0); + + return R0 * (a + (1.0f - a) * (p + 1.0f) * pow(q, p)); +} + +half3 BetterSH9(half4 normal) +{ + float3 indirect; + float3 L0 = float3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w) + float3(unity_SHBr.z, unity_SHBg.z, unity_SHBb.z) / 3.0; + indirect.r = shEvaluateDiffuseL1Geomerics_local(L0.r, unity_SHAr.xyz, normal.xyz); + indirect.g = shEvaluateDiffuseL1Geomerics_local(L0.g, unity_SHAg.xyz, normal.xyz); + indirect.b = shEvaluateDiffuseL1Geomerics_local(L0.b, unity_SHAb.xyz, normal.xyz); + indirect = max(0, indirect); + indirect += SHEvalLinearL2(normal); + return indirect; +} + #endif // __POI_INC -- cgit v1.2.3