Bläddra i källkod

Spot Light Physical Falloff

= 9 år sedan
förälder
incheckning
21a0f6284b

+ 0 - 11
src/Shaders/ShadersInclude/pbrFunctions.fx

@@ -150,17 +150,6 @@ vec3 toGammaSpace(vec3 color)
     return vec3(pow(color.r, 1.0 / 2.2), pow(color.g, 1.0 / 2.2), pow(color.b, 1.0 / 2.2));
 }
 
-float computeLightFalloff(vec3 lightOffset, float lightDistanceSquared, float range)
-{
-    #ifdef USEPHYSICALLIGHTFALLOFF
-        float lightDistanceFalloff = 1.0 / ((lightDistanceSquared + 0.0001));
-        return lightDistanceFalloff;
-    #else
-        float lightFalloff = max(0., 1.0 - length(lightOffset) / range);
-        return lightFalloff;
-    #endif
-}
-
 #ifdef CAMERATONEMAP
     vec3 toneMaps(vec3 color)
     {

+ 65 - 38
src/Shaders/ShadersInclude/pbrLightFunctions.fx

@@ -7,6 +7,47 @@ struct lightingInfo
     #endif
 };
 
+float computeDistanceLightFalloff(vec3 lightOffset, float lightDistanceSquared, float range)
+{   
+    #ifdef USEPHYSICALLIGHTFALLOFF
+        float lightDistanceFalloff = 1.0 / ((lightDistanceSquared + 0.0001));
+    #else
+        float lightDistanceFalloff = max(0., 1.0 - length(lightOffset) / range);
+    #endif
+    
+    return lightDistanceFalloff;
+}
+
+float computeDirectionalLightFalloff(vec3 lightDirection, vec3 directionToLightCenterW, float lightAngle, float exponent)
+{
+    float falloff = 0.0;
+    
+    #ifdef USEPHYSICALLIGHTFALLOFF
+        float cosHalfAngle = cos(lightAngle * 0.5);
+        const float kMinusLog2ConeAngleIntensityRatio = 6.64385618977; // -log2(0.01)
+
+        // Calculate a Spherical Gaussian (von Mises-Fisher distribution, not angle-based Gaussian) such that the peak is in the light direction,
+        // and the value at the nominal cone angle is 1% of the peak. Because we want the distribution to decay from unity (100%)
+        // at the peak direction (dot product = 1) down to 1% at the nominal cone cutoff (dot product = cosAngle) 
+        // the falloff rate expressed in terms of the base-two dot product is therefore -log2(ConeAngleIntensityRatio) / (1.0 - cosAngle).
+        // Note that the distribution is unnormalised in that peak density is unity, rather than the total energy is unity.
+        float concentrationKappa = kMinusLog2ConeAngleIntensityRatio / (1.0 - cosHalfAngle);
+    
+        // Evaluate spherical gaussian for light directional falloff for spot light type (note: spot directional falloff; 
+        // not directional light type)
+        vec4 lightDirectionSpreadSG = vec4(-lightDirection * concentrationKappa, -concentrationKappa);
+        falloff = exp2(dot(vec4(directionToLightCenterW, 1.0), lightDirectionSpreadSG));
+    #else
+        float cosAngle = max(0.000000000000001, dot(-lightDirection, directionToLightCenterW));
+        if (cosAngle >= lightAngle)
+        {
+            falloff = max(0., pow(cosAngle, exponent));
+        }
+    #endif
+    
+    return falloff;
+}
+
 lightingInfo computeLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, float rangeRadius, float roughness, float NdotV, out float NdotL) {
     lightingInfo result;
 
@@ -19,7 +60,7 @@ lightingInfo computeLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData,
     {
         vec3 lightOffset = lightData.xyz - vPositionW;
         float lightDistanceSquared = dot(lightOffset, lightOffset);
-        attenuation = computeLightFalloff(lightOffset, lightDistanceSquared, rangeRadius);
+        attenuation = computeDistanceLightFalloff(lightOffset, lightDistanceSquared, rangeRadius);
         
         lightDistance = sqrt(lightDistanceSquared);
         lightDirection = normalize(lightOffset);
@@ -57,48 +98,34 @@ lightingInfo computeSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightDa
     lightingInfo result;
 
     vec3 lightOffset = lightData.xyz - vPositionW;
-    vec3 lightVectorW = normalize(lightOffset);
+    vec3 directionToLightCenterW = normalize(lightOffset);
 
-    // diffuse
-    float cosAngle = max(0.000000000000001, dot(-lightDirection.xyz, lightVectorW));
+    // Distance falloff.
+    float lightDistanceSquared = dot(lightOffset, lightOffset);
+    float attenuation = computeDistanceLightFalloff(lightOffset, lightDistanceSquared, rangeRadius);
     
-    if (cosAngle >= lightDirection.w)
-    {
-        cosAngle = max(0., pow(cosAngle, lightData.w));
-        
-        // Inverse squared falloff.
-        float lightDistanceSquared = dot(lightOffset, lightOffset);
-        float attenuation = computeLightFalloff(lightOffset, lightDistanceSquared, rangeRadius);
-        
-        // Directional falloff.
-        attenuation *= cosAngle;
-        
-        // Roughness.
-        float lightDistance = sqrt(lightDistanceSquared);
-        roughness = adjustRoughnessFromLightProperties(roughness, rangeRadius, lightDistance);
-        
-        // Diffuse
-        vec3 H = normalize(viewDirectionW - lightDirection.xyz);
-        NdotL = max(0.00000000001, dot(vNormal, -lightDirection.xyz));
-        float VdotH = clamp(dot(viewDirectionW, H), 0.00000000001, 1.0);
-
-        float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
-        result.diffuse = diffuseTerm * diffuseColor * attenuation;
-
-        #ifdef SPECULARTERM
-            // Specular
-            float NdotH = max(0.00000000001, dot(vNormal, H));
-
-            vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
-            result.specular = specTerm  * attenuation;
-        #endif
+    // Directional falloff.
+    float directionalAttenuation = computeDirectionalLightFalloff(lightDirection.xyz, directionToLightCenterW, lightDirection.w, lightData.w);
+    attenuation *= directionalAttenuation;
+    
+    // Roughness.
+    float lightDistance = sqrt(lightDistanceSquared);
+    roughness = adjustRoughnessFromLightProperties(roughness, rangeRadius, lightDistance);
+    
+    // Diffuse
+    vec3 H = normalize(viewDirectionW - lightDirection.xyz);
+    NdotL = max(0.00000000001, dot(vNormal, -lightDirection.xyz));
+    float VdotH = clamp(dot(viewDirectionW, H), 0.00000000001, 1.0);
 
-        return result;
-    }
+    float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
+    result.diffuse = diffuseTerm * diffuseColor * attenuation;
 
-    result.diffuse = vec3(0.);
     #ifdef SPECULARTERM
-        result.specular = vec3(0.);
+        // Specular
+        float NdotH = max(0.00000000001, dot(vNormal, H));
+
+        vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
+        result.specular = specTerm  * attenuation;
     #endif
 
     return result;