From df77089d90024f229e4dd27a25a9b97645b614d1 Mon Sep 17 00:00:00 2001 From: yum Date: Fri, 14 Mar 2025 18:36:01 -0700 Subject: Simplify spherical harmonics Implement standard SH9 as a learning exercise. Drop poi's fancy SH9 code. Realize that we just want diffuse and lock everything else away behind a ui-inaccessible #define. --- 2ner.shader | 1 - poi.cginc | 61 ------------------------------------------------------ yum_lighting.cginc | 50 +++++++++++++++++++++++++++++++++++++------- 3 files changed, 43 insertions(+), 69 deletions(-) diff --git a/2ner.shader b/2ner.shader index ad29182..1192b58 100644 --- a/2ner.shader +++ b/2ner.shader @@ -672,7 +672,6 @@ Shader "yum_food/2ner" [Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend("Source Blend", Float) = 1 [Enum(UnityEngine.Rendering.BlendMode)] _DstBlend("Destination Blend", Float) = 0 [Enum(Off, 0, On, 1)] _ZWrite("ZWrite", Int) = 1 - [Enum(Realistic, 0, Toon, 1)] _SphericalHarmonics("Spherical harmonics", Int) = 1 [HideInInspector] m_start_blending ("Blending--{button_help:{text:Tutorial,action:{type:URL,data:https://www.poiyomi.com/rendering/blending},hover:Documentation}}", Float) = 0 [DoNotAnimate][Enum(Thry.BlendOp)]_BlendOp ("RGB Blend Op", Int) = 0 [DoNotAnimate][Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend ("RGB Source Blend", Int) = 1 diff --git a/poi.cginc b/poi.cginc index df89bbf..cc12b1a 100644 --- a/poi.cginc +++ b/poi.cginc @@ -57,72 +57,11 @@ 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; -} - float calculateluminance(float3 color) { return color.r * 0.299 + color.g * 0.587 + color.b * 0.114; } -float3 getPoiLightingDirect(float3 normal) { - float3 magic = max(BetterSH9(normalize(unity_SHAr + unity_SHAg + unity_SHAb)), 0); - float3 normalLight = _LightColor0.rgb + BetterSH9(float4(0, 0, 0, 1)); - - float magiLumi = calculateluminance(magic); - float normaLumi = calculateluminance(normalLight); - float maginormalumi = magiLumi + normaLumi; - - float magiratio = magiLumi / maginormalumi; - float normaRatio = normaLumi / maginormalumi; - - float target = calculateluminance(magic * magiratio + normalLight * normaRatio); - float3 properLightColor = magic + normalLight; - float properLuminance = calculateluminance(magic + normalLight); - return properLightColor * max(0.0001, (target / properLuminance)); -} - -float3 getPoiLightingIndirect() { - return BetterSH9(float4(0, 0, 0, 1)); -} - bool SceneHasReflections() { float width, height; diff --git a/yum_lighting.cginc b/yum_lighting.cginc index b8856ed..23e360e 100644 --- a/yum_lighting.cginc +++ b/yum_lighting.cginc @@ -142,13 +142,54 @@ float3 getIndirectSpecular(v2f i, YumPbr pbr, float3 view_dir) { return UnityGI_prefilteredRadiance(data, roughness, reflect_dir); } +float3 yumSH9(float4 n) { +#if defined(YUM_SH9_STANDARD) + // Unity gives us the first three bands (L0-L2) of SH coefficients as follows: + // unity_SHA*.w: L0 coefficients + // unity_SHA*.xyz: L1 coefficients + // unity_SHB*: first four of the L2 coefficients + // unity_SHC: last L2 coefficient + + // Parse out coefficients into a simpler but less efficient format. + float3 L00 = float3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w); + float3 L1_1 = float3(unity_SHAr.x, unity_SHAg.x, unity_SHAb.x); + float3 L10 = float3(unity_SHAr.y, unity_SHAg.y, unity_SHAb.y); + float3 L11 = float3(unity_SHAr.z, unity_SHAg.z, unity_SHAb.z); + float3 L2_2 = float3(unity_SHBr.x, unity_SHBg.x, unity_SHBb.x); + float3 L2_1 = float3(unity_SHBr.y, unity_SHBg.y, unity_SHBb.y); + float3 L20 = float3(unity_SHBr.z, unity_SHBg.z, unity_SHBb.z); + float3 L21 = float3(unity_SHBr.w, unity_SHBg.w, unity_SHBb.w); + float3 L22 = unity_SHC; + + // Equation 13 from "An Efficient Representation for Irradiance Environment + // Maps" by Ramamoorthi and Hanrahan. Note that the order of some + // coefficients is different, and normalization constants have been + // premultiplied by Unity. + float3 L0 = L00; + float3 L1 = L1_1 * n.x + L10 * n.y + L11 * n.z; + float3 L2 = + L2_2 * n.x * n.y + + L2_1 * n.y * n.z + + L20 * n.z * n.z + + L21 * n.x * n.z + + L22 * (n.x * n.x - n.y * n.y); + + return L0 + L1 + L2; +#else + // On non-photorealistic avatars, simply using the diffuse component looks + // better. *shruge* + float3 L00 = float3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w); + return L00; +#endif +} + float4 getIndirectDiffuse(v2f i, float4 vertexLightColor) { float4 diffuse = vertexLightColor; #if defined(FORWARD_BASE_PASS) #if defined(LIGHTMAP_ON) diffuse.xyz = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap, i.uv2)); #else - diffuse.xyz += max(0, BetterSH9(float4(0, 0, 0, 1))); + diffuse.xyz += max(0, yumSH9(float4(0, 0, 0, 1))); #endif #endif return diffuse; @@ -169,12 +210,7 @@ YumLighting GetYumLighting(v2f i, YumPbr pbr) { light.direct = _LightColor0.rgb; // TODO filament's spherical harmonics look nicer than this. // See FilamentLightIndirect.cginc::UnityGI_Irradiance in filamented. - //ifex _Spherical_Harmonics==0 - light.diffuse = max(0, BetterSH9(float4(i.normal, 1))); - //endex - //ifex _Spherical_Harmonics==1 - light.diffuse = max(0, BetterSH9(float4(0, 0, 0, 1))); - //endex + light.diffuse = getIndirectDiffuse(i, /*vertexLightColor=*/0); #if defined(_MIN_BRIGHTNESS) light.diffuse = max(_Min_Brightness, light.diffuse); #endif -- cgit v1.2.3