#ifndef __LIGHTING_INC #define __LIGHTING_INC #include "UnityCG.cginc" #include "AutoLight.cginc" #include "UnityPBSLighting.cginc" #include "UnityLightingCommon.cginc" #include "UnityStandardCoreMinimal.cginc" #include "features.cginc" #include "filamented.cginc" #include "interpolators.cginc" #include "LightVolumes.cginc" #include "pbr.cginc" struct LightCommon { float3 V; float3 N; float NoV; #if defined(_CLEARCOAT) float NoV_cc; #endif }; struct LightDirect { float3 dir; float3 H; float NoH; float NoL; #if defined(_CLEARCOAT) float NoH_cc; float NoL_cc; #endif float LoH; float LoV; float double_LoV; float3 color; }; struct LightIndirect { float3 dir; float3 H; float NoH; float NoL; #if defined(_CLEARCOAT) float LoH_cc; #endif float LoH; float LoV; float double_LoV; float3 specular; #if defined(_CLEARCOAT) float3 specular_cc; #endif float3 diffuse; float3 L00; float3 L01r; float3 L01g; float3 L01b; }; struct LightData { LightCommon common; LightDirect direct; LightIndirect indirect; }; float3 getDirectLightDirection(v2f i) { #if defined(POINT) || defined(POINT_COOKIE) || defined(SPOT) return normalize((_WorldSpaceLightPos0 - i.worldPos).xyz); #else return _WorldSpaceLightPos0; #endif } float getShadowAttenuation(v2f i) { UNITY_LIGHT_ATTENUATION(attenuation, i, i.worldPos); return attenuation; } float4 getDirectLightColorIntensity() { // Properly separate light color from intensity like filamented if (_LightColor0.w <= 0) return float4(0, 0, 0, 0); return float4(_LightColor0.xyz, _LightColor0.w); } float3 getIndirectSpecular(v2f i, float roughness, float3 view_dir, float3 reflect_dir) { UnityGIInput data = InitialiseUnityGIInput(i.worldPos, view_dir); float3 env_refl = UnityGI_prefilteredRadiance(data, roughness, reflect_dir); return env_refl; } // Geomerics SH evaluation // https://community.arm.com/cfs-file/__key/telligent-evolution-components-attachments/01-2066-00-00-00-01-27-70/Simplifying_2D00_Spherical_2D00_Harmonics_2D00_for_2D00_Lighting.pdf float shEvaluateDiffuseL1Geomerics(float L0, float3 L1, float3 n) { // average energy float R0 = max(L0, 0); // 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 = dot(normalize(R1), n) * 0.5 + 0.5; q = saturate(q); // 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)); } float3 yumSH9(float4 n, float3 worldPos, inout LightIndirect light) { [branch] if (_UdonLightVolumeEnabled) { LightVolumeSH(worldPos, light.L00, light.L01r, light.L01g, light.L01b); return light.L00 + float3( dot(light.L01r, n.xyz), dot(light.L01g, n.xyz), dot(light.L01b, n.xyz)); } // Geomerics SH evaluation // Extract L0 and optionally incorporate part of L2 float3 L0 = float3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w); float3 L0L2 = float3(unity_SHBr.z, unity_SHBg.z, unity_SHBb.z) / 3.0; L0 = L0 + L0L2; // useL2 = true float3 finalSH; finalSH.r = shEvaluateDiffuseL1Geomerics(L0.r, unity_SHAr.xyz, n.xyz); finalSH.g = shEvaluateDiffuseL1Geomerics(L0.g, unity_SHAg.xyz, n.xyz); finalSH.b = shEvaluateDiffuseL1Geomerics(L0.b, unity_SHAb.xyz, n.xyz); light.L00 = L0; light.L01r = unity_SHAr.xyz; light.L01g = unity_SHAg.xyz; light.L01b = unity_SHAb.xyz; return finalSH; } float4 getIndirectDiffuse(v2f i, Pbr pbr, inout LightIndirect light) { float4 diffuse = 0; #if defined(FORWARD_BASE_PASS) diffuse.xyz += max(0, yumSH9(float4(i.normal, 1.0), i.worldPos, light)); #endif return diffuse; } void GetLighting(v2f i, Pbr pbr, out LightData data) { data = (LightData) 0; float3 view_dir = normalize(i.eyeVec.xyz); data.common.V = -view_dir; data.common.N = pbr.normal; data.common.NoV = saturate(dot(pbr.normal, data.common.V)); #if defined(_CLEARCOAT) data.common.NoV_cc = saturate(dot(i.normal, data.common.V)); #endif // Direct lighting data.direct.dir = getDirectLightDirection(i); data.direct.H = normalize(data.common.V + data.direct.dir); data.direct.NoL = saturate(dot(pbr.normal, data.direct.dir)); data.direct.NoH = saturate(dot(pbr.normal, data.direct.H)); data.direct.LoH = saturate(dot(data.direct.dir, data.direct.H)); #if defined(_CLEARCOAT) data.direct.NoH_cc = saturate(dot(i.normal, data.direct.H)); data.direct.NoL_cc = saturate(dot(i.normal, data.direct.dir)); #endif float direct_LoV = dot(data.direct.dir, data.common.V); data.direct.LoV = saturate(direct_LoV); data.direct.double_LoV = saturate(2.0f * direct_LoV * direct_LoV - 1.0f); float4 lightColorIntensity = getDirectLightColorIntensity(); data.direct.color = lightColorIntensity.rgb * getShadowAttenuation(i); // Indirect lighting float3 reflect_dir = reflect(data.common.V, pbr.normal); float3 dominant_dir = getSpecularDominantDirection(pbr.normal, reflect_dir, pbr.roughness); data.indirect.dir = normalize(dominant_dir); data.indirect.H = normalize(data.common.V + data.indirect.dir); data.indirect.NoL = saturate(dot(pbr.normal, data.indirect.dir)); data.indirect.NoH = saturate(dot(pbr.normal, data.indirect.H)); #if defined(_CLEARCOAT) float3 dir_cc = reflect(data.common.V, i.normal); float3 H_cc = normalize(data.common.V + dir_cc); data.indirect.LoH_cc = saturate(dot(dir_cc, H_cc)); #endif data.indirect.LoH = saturate(dot(data.indirect.dir, data.indirect.H)); float indirect_LoV = dot(data.indirect.dir, data.common.V); data.indirect.LoV = saturate(indirect_LoV); data.indirect.double_LoV = saturate(2.0f * indirect_LoV * indirect_LoV - 1.0f); data.indirect.diffuse = getIndirectDiffuse(i, pbr, data.indirect); data.indirect.specular = getIndirectSpecular(i, pbr.roughness, view_dir, -data.indirect.dir); #if defined(_CLEARCOAT) data.indirect.specular_cc = getIndirectSpecular(i, pbr.cc_roughness, view_dir, dir_cc); #if defined(_CLEARCOAT_MASK) float cc_mask = _Clearcoat_Mask.Sample(bilinear_clamp_s, i.uv01.xy).r; data.indirect.specular_cc *= cc_mask; #endif #endif } #endif // __LIGHTING_INC