Browse Source

Add Light Radius and Inverse Squared Falloff

Sébastien Vandenberghe 9 years ago
parent
commit
568c16769e

+ 13 - 5
materialsLibrary/materials/pbr/babylon.pbrMaterial.ts

@@ -154,7 +154,7 @@ module BABYLON {
         public linkEmissiveWithAlbedo = false;
         public useSpecularOverAlpha = true;
         public disableLighting = false;
-
+        
         public indexOfRefraction = 0.66;
         public invertRefractionY = false;
         public linkRefractionWithTransparency = false;
@@ -170,7 +170,6 @@ module BABYLON {
         private _worldViewProjectionMatrix = Matrix.Zero();
         private _globalAmbientColor = new Color3(0, 0, 0);
         private _tempColor = new Color3();
-
         private _renderId: number;
 
         private _defines = new PBRMaterialDefines();
@@ -334,6 +333,7 @@ module BABYLON {
         private static _scaledReflectivity = new Color3();
         private static _scaledEmissive = new Color3();
         private static _scaledReflection = new Color3();
+        private static _lightRadiuses = [1, 1, 1, 1];
 
         public static BindLights(scene: Scene, mesh: AbstractMesh, effect: Effect, defines: MaterialDefines) {
             var lightIndex = 0;
@@ -348,6 +348,8 @@ module BABYLON {
                 if (!light.canAffectMesh(mesh)) {
                     continue;
                 }
+                
+                this._lightRadiuses[lightIndex] = light.radius;
 
                 if (light instanceof PointLight) {
                     // Point Light
@@ -365,12 +367,13 @@ module BABYLON {
 
                 // GAMMA CORRECTION.
                 light.diffuse.toLinearSpaceToRef(PBRMaterial._scaledAlbedo);
+                
                 PBRMaterial._scaledAlbedo.scaleToRef(light.intensity, PBRMaterial._scaledAlbedo);
-
-                light.diffuse.scaleToRef(light.intensity, PBRMaterial._scaledAlbedo);
                 effect.setColor4("vLightDiffuse" + lightIndex, PBRMaterial._scaledAlbedo, light.range);
+                
                 if (defines["SPECULARTERM"]) {
                     light.specular.toLinearSpaceToRef(PBRMaterial._scaledReflectivity);
+                    
                     PBRMaterial._scaledReflectivity.scaleToRef(light.intensity, PBRMaterial._scaledReflectivity);
                     effect.setColor3("vLightSpecular" + lightIndex, PBRMaterial._scaledReflectivity);
                 }
@@ -397,6 +400,11 @@ module BABYLON {
                 if (lightIndex === maxSimultaneousLights)
                     break;
             }
+            
+            effect.setFloat4("vLightRadiuses", this._lightRadiuses[0],
+                this._lightRadiuses[1],
+                this._lightRadiuses[2],
+                this._lightRadiuses[3]);
         }
 
         public isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean {
@@ -814,7 +822,7 @@ module BABYLON {
                         "vSphericalX", "vSphericalY", "vSphericalZ",
                         "vSphericalXX", "vSphericalYY", "vSphericalZZ",
                         "vSphericalXY", "vSphericalYZ", "vSphericalZX",
-                        "vMicrosurfaceTextureLods"
+                        "vMicrosurfaceTextureLods", "vLightRadiuses"
                     ],
                     ["albedoSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler", "reflection2DSampler", "emissiveSampler", "reflectivitySampler", "bumpSampler", "lightmapSampler", "refractionCubeSampler", "refraction2DSampler",
                         "shadowSampler0", "shadowSampler1", "shadowSampler2", "shadowSampler3"

+ 77 - 41
materialsLibrary/materials/pbr/pbr.fragment.fx

@@ -20,6 +20,7 @@ uniform vec3 vEyePosition;
 uniform vec3 vAmbientColor;
 uniform vec3 vReflectionColor;
 uniform vec4 vAlbedoColor;
+uniform vec4 vLightRadiuses;
 
 // CUSTOM CONTROLS
 uniform vec4 vLightingIntensity;
@@ -208,8 +209,9 @@ vec3 FresnelSchlickEnvironmentGGX(float VdotN, vec3 reflectance0, vec3 reflectan
 }
 
 // Cook Torance Specular computation.
-vec3 computeSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, float alphaG, vec3 specularColor)
+vec3 computeSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, float roughness, vec3 specularColor)
 {
+    float alphaG = convertRoughnessToAverageSlope(roughness);
     float distribution = normalDistributionFunction_TrowbridgeReitzGGX(NdotH, alphaG);
     float visibility = smithVisibilityG_TrowbridgeReitzGGX_Walter(NdotL, NdotV, alphaG);
     visibility /= (4.0 * NdotL * NdotV); // Cook Torance Denominator  integated in viibility to avoid issues when visibility function changes.
@@ -237,6 +239,15 @@ float computeDiffuseTerm(float NdotL, float NdotV, float VdotH, float roughness)
     // diffuseFresnelTerm /= kPi;
 }
 
+float adjustRoughnessFromLightProperties(float roughness, float lightRadius, float lightDistance)
+{
+    // At small angle this approximation works. 
+    float lightRoughness = lightRadius / lightDistance;
+    // Distribution can sum.
+    float totalRoughness = clamp(lightRoughness + roughness, 0., 1.);
+    return totalRoughness;
+}
+
 float computeDefaultMicroSurface(float microSurface, vec3 reflectivityColor)
 {
     float kReflectivityNoAlphaWorkflow_SmoothnessMax = 0.95;
@@ -704,26 +715,39 @@ struct lightingInfo
 #endif
 };
 
-lightingInfo computeLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, float range, float roughness, float alphaG, float NdotV) {
+lightingInfo computeLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, float range, float roughness, float NdotV, float lightRadius) {
     lightingInfo result;
 
-    vec3 lightVectorW;
+    vec3 lightDirection;
     float attenuation = 1.0;
+    float lightDistance;
+    
+    // Point
     if (lightData.w == 0.)
     {
-        vec3 direction = lightData.xyz - vPositionW;
-
-        attenuation = max(0., 1.0 - length(direction) / range);
-        lightVectorW = normalize(direction);
+        vec3 lightOffset = lightData.xyz - vPositionW;
+        
+        // Inverse squared falloff.
+        float lightDistanceSquared = dot(lightOffset, lightOffset);
+        float lightDistanceFalloff = 1.0 / ((lightDistanceSquared + 0.0001) * range);
+        attenuation = lightDistanceFalloff;
+        
+        lightDistance = sqrt(lightDistanceSquared);
+        lightDirection = normalize(lightOffset);
     }
+    // Directional
     else
     {
-        lightVectorW = normalize(-lightData.xyz);
+        lightDistance = length(-lightData.xyz);
+        lightDirection = normalize(-lightData.xyz);
     }
-
+    
+    // Roughness
+    roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
+    
     // diffuse
-    vec3 H = normalize(viewDirectionW + lightVectorW);
-    float NdotL = max(0.00000000001, dot(vNormal, lightVectorW));
+    vec3 H = normalize(viewDirectionW + lightDirection);
+    float NdotL = max(0.00000000001, dot(vNormal, lightDirection));
     float VdotH = clamp(0.00000000001, 1.0, dot(viewDirectionW, H));
 
     float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
@@ -733,43 +757,52 @@ lightingInfo computeLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData,
     // Specular
     float NdotH = max(0.00000000001, dot(vNormal, H));
 
-    vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, alphaG, specularColor);
+    vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
     result.specular = specTerm * attenuation;
 #endif
 
     return result;
 }
 
-lightingInfo computeSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec4 lightDirection, vec3 diffuseColor, vec3 specularColor, float range, float roughness, float alphaG, float NdotV) {
+lightingInfo computeSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec4 lightDirection, vec3 diffuseColor, vec3 specularColor, float range, float roughness, float NdotV, float lightRadius) {
     lightingInfo result;
 
-    vec3 direction = lightData.xyz - vPositionW;
-    vec3 lightVectorW = normalize(direction);
-    float attenuation = max(0., 1.0 - length(direction) / range);
+    vec3 lightOffset = lightData.xyz - vPositionW;
+    vec3 lightVectorW = normalize(lightOffset);
 
     // diffuse
-    float cosAngle = max(0.0000001, dot(-lightDirection.xyz, lightVectorW));
-    float spotAtten = 0.0;
-
+    float cosAngle = max(0.000000000000001, dot(-lightDirection.xyz, lightVectorW));
+    
     if (cosAngle >= lightDirection.w)
     {
         cosAngle = max(0., pow(cosAngle, lightData.w));
-        spotAtten = clamp((cosAngle - lightDirection.w) / (1. - cosAngle), 0.0, 1.0);
-
+        
+        // Inverse squared falloff.
+        float lightDistanceSquared = dot(lightOffset, lightOffset);
+        float lightDistanceFalloff = 1.0 / ((lightDistanceSquared + 0.0001) * range);
+        float attenuation = lightDistanceFalloff;
+        
+        // Directional falloff.
+        attenuation *= cosAngle;
+        
+        // Roughness.
+        float lightDistance = sqrt(lightDistanceSquared);
+        roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
+        
         // Diffuse
         vec3 H = normalize(viewDirectionW - lightDirection.xyz);
         float 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 * spotAtten;
+        result.diffuse = diffuseTerm * diffuseColor * attenuation;
 
 #ifdef SPECULARTERM
         // Specular
         float NdotH = max(0.00000000001, dot(vNormal, H));
 
-        vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, alphaG, specularColor);
-        result.specular = specTerm  * attenuation * spotAtten;
+        vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
+        result.specular = specTerm  * attenuation;
 #endif
 
         return result;
@@ -783,10 +816,11 @@ lightingInfo computeSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightDa
     return result;
 }
 
-lightingInfo computeHemisphericLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, vec3 groundColor, float roughness, float alphaG, float NdotV) {
+lightingInfo computeHemisphericLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, vec3 groundColor, float roughness, float NdotV, float lightRadius) {
     lightingInfo result;
 
-    vec3 lightVectorW = normalize(lightData.xyz);
+    // Roughness
+    // Do not touch roughness on hemispheric.
 
     // Diffuse
     float ndl = dot(vNormal, lightData.xyz) * 0.5 + 0.5;
@@ -794,12 +828,13 @@ lightingInfo computeHemisphericLighting(vec3 viewDirectionW, vec3 vNormal, vec4
 
 #ifdef SPECULARTERM
     // Specular
+    vec3 lightVectorW = normalize(lightData.xyz);
     vec3 H = normalize(viewDirectionW + lightVectorW);
     float NdotH = max(0.00000000001, dot(vNormal, H));
     float NdotL = max(0.00000000001, ndl);
     float VdotH = clamp(0.00000000001, 1.0, dot(viewDirectionW, H));
 
-    vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, alphaG, specularColor);
+    vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
     result.specular = specTerm;
 #endif
 
@@ -913,9 +948,8 @@ void main(void) {
     // Adapt microSurface.
     microSurface = clamp(microSurface, 0., 1.) * 0.98;
 
-    // Compute roughness and average slope.
+    // Compute roughness.
     float roughness = clamp(1. - microSurface, 0.000001, 1.0);
-    float alphaG = convertRoughnessToAverageSlope(roughness);
     
     // Lighting
     vec3 lightDiffuseContribution = vec3(0., 0., 0.);
@@ -934,13 +968,13 @@ void main(void) {
     vec3 vLightSpecular0 = vec3(0.0);
 #endif
 #ifdef SPOTLIGHT0
-    lightingInfo info = computeSpotLighting(viewDirectionW, normalW, vLightData0, vLightDirection0, vLightDiffuse0.rgb, vLightSpecular0, vLightDiffuse0.a, roughness, alphaG, NdotV);
+    lightingInfo info = computeSpotLighting(viewDirectionW, normalW, vLightData0, vLightDirection0, vLightDiffuse0.rgb, vLightSpecular0, vLightDiffuse0.a, roughness, NdotV, vLightRadiuses[0]);
 #endif
 #ifdef HEMILIGHT0
-    lightingInfo info = computeHemisphericLighting(viewDirectionW, normalW, vLightData0, vLightDiffuse0.rgb, vLightSpecular0, vLightGround0, roughness, alphaG, NdotV);
+    lightingInfo info = computeHemisphericLighting(viewDirectionW, normalW, vLightData0, vLightDiffuse0.rgb, vLightSpecular0, vLightGround0, roughness, NdotV, vLightRadiuses[0]);
 #endif
 #if defined(POINTLIGHT0) || defined(DIRLIGHT0)
-    lightingInfo info = computeLighting(viewDirectionW, normalW, vLightData0, vLightDiffuse0.rgb, vLightSpecular0, vLightDiffuse0.a, roughness, alphaG, NdotV);
+    lightingInfo info = computeLighting(viewDirectionW, normalW, vLightData0, vLightDiffuse0.rgb, vLightSpecular0, vLightDiffuse0.a, roughness, NdotV, vLightRadiuses[0]);
 #endif
 #ifdef SHADOW0
 #ifdef SHADOWVSM0
@@ -978,13 +1012,13 @@ void main(void) {
     vec3 vLightSpecular1 = vec3(0.0);
 #endif
 #ifdef SPOTLIGHT1
-    info = computeSpotLighting(viewDirectionW, normalW, vLightData1, vLightDirection1, vLightDiffuse1.rgb, vLightSpecular1, vLightDiffuse1.a, roughness, alphaG, NdotV);
+    info = computeSpotLighting(viewDirectionW, normalW, vLightData1, vLightDirection1, vLightDiffuse1.rgb, vLightSpecular1, vLightDiffuse1.a, roughness, NdotV, vLightRadiuses[1]);
 #endif
 #ifdef HEMILIGHT1
-    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData1, vLightDiffuse1.rgb, vLightSpecular1, vLightGround1, roughness, alphaG, NdotV);
+    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData1, vLightDiffuse1.rgb, vLightSpecular1, vLightGround1, roughness, NdotV, vLightRadiuses[1]);
 #endif
 #if defined(POINTLIGHT1) || defined(DIRLIGHT1)
-    info = computeLighting(viewDirectionW, normalW, vLightData1, vLightDiffuse1.rgb, vLightSpecular1, vLightDiffuse1.a, roughness, alphaG, NdotV);
+    info = computeLighting(viewDirectionW, normalW, vLightData1, vLightDiffuse1.rgb, vLightSpecular1, vLightDiffuse1.a, roughness, NdotV, vLightRadiuses[1]);
 #endif
 #ifdef SHADOW1
 #ifdef SHADOWVSM1
@@ -1023,13 +1057,13 @@ void main(void) {
     vec3 vLightSpecular2 = vec3(0.0);
 #endif
 #ifdef SPOTLIGHT2
-    info = computeSpotLighting(viewDirectionW, normalW, vLightData2, vLightDirection2, vLightDiffuse2.rgb, vLightSpecular2, vLightDiffuse2.a, roughness, alphaG, NdotV);
+    info = computeSpotLighting(viewDirectionW, normalW, vLightData2, vLightDirection2, vLightDiffuse2.rgb, vLightSpecular2, vLightDiffuse2.a, roughness, NdotV, vLightRadiuses[2]);
 #endif
 #ifdef HEMILIGHT2
-    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData2, vLightDiffuse2.rgb, vLightSpecular2, vLightGround2, roughness, alphaG, NdotV);
+    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData2, vLightDiffuse2.rgb, vLightSpecular2, vLightGround2, roughness, NdotV, vLightRadiuses[2]);
 #endif
 #if defined(POINTLIGHT2) || defined(DIRLIGHT2)
-    info = computeLighting(viewDirectionW, normalW, vLightData2, vLightDiffuse2.rgb, vLightSpecular2, vLightDiffuse2.a, roughness, alphaG, NdotV);
+    info = computeLighting(viewDirectionW, normalW, vLightData2, vLightDiffuse2.rgb, vLightSpecular2, vLightDiffuse2.a, roughness, NdotV, vLightRadiuses[2]);
 #endif
 #ifdef SHADOW2
 #ifdef SHADOWVSM2
@@ -1068,13 +1102,13 @@ void main(void) {
     vec3 vLightSpecular3 = vec3(0.0);
 #endif
 #ifdef SPOTLIGHT3
-    info = computeSpotLighting(viewDirectionW, normalW, vLightData3, vLightDirection3, vLightDiffuse3.rgb, vLightSpecular3, vLightDiffuse3.a, roughness, alphaG, NdotV);
+    info = computeSpotLighting(viewDirectionW, normalW, vLightData3, vLightDirection3, vLightDiffuse3.rgb, vLightSpecular3, vLightDiffuse3.a, roughness, NdotV, vLightRadiuses[3]);
 #endif
 #ifdef HEMILIGHT3
-    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData3, vLightDiffuse3.rgb, vLightSpecular3, vLightGround3, roughness, alphaG, NdotV);
+    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData3, vLightDiffuse3.rgb, vLightSpecular3, vLightGround3, roughness, NdotV, vLightRadiuses[3]);
 #endif
 #if defined(POINTLIGHT3) || defined(DIRLIGHT3)
-    info = computeLighting(viewDirectionW, normalW, vLightData3, vLightDiffuse3.rgb, vLightSpecular3, vLightDiffuse3.a, roughness, alphaG, NdotV);
+    info = computeLighting(viewDirectionW, normalW, vLightData3, vLightDiffuse3.rgb, vLightSpecular3, vLightDiffuse3.a, roughness, NdotV, vLightRadiuses[3]);
 #endif
 #ifdef SHADOW3
 #ifdef SHADOWVSM3
@@ -1140,6 +1174,8 @@ vec3 surfaceRefractionColor = vec3(0., 0., 0.);
 // Go mat -> blurry reflexion according to microSurface
 #ifndef LODBASEDMICROSFURACE
     float bias = 20. * (1.0 - microSurface);
+#else
+    float alphaG = convertRoughnessToAverageSlope(roughness);
 #endif
         
 #ifdef REFRACTION

+ 7 - 0
materialsLibrary/test/index.html

@@ -314,6 +314,13 @@
                     spotLight.range = options.lightRange;
                 });
                 
+                f1.add(options, 'lightRadius').onChange(function() {
+                    hemisphericLight.radius = options.lightRadius;
+                    directionalLight.radius = options.lightRadius;
+                    pointLight.radius = options.lightRadius;
+                    spotLight.radius = options.lightRadius;
+                });
+                
 				f1.add(options, 'hemisphericLight').onChange(function () {
 					hemisphericLight.setEnabled(options.hemisphericLight);
 				});

+ 2 - 1
materialsLibrary/test/index.js

@@ -11,7 +11,8 @@ var options = {
 	fog: false,
 	skybox: false,
     lightIntensity: 1.0,
-    lightRange: 1000
+    lightRange: 1000,
+    lightRadius: 0.0000001
 }
 
 var registeredUIs = {};

+ 8 - 0
src/Lights/babylon.light.ts

@@ -30,6 +30,9 @@
         public excludedMeshes = new Array<AbstractMesh>();
         public excludeWithLayerMask = 0;
 
+        // PBR Properties.
+        public radius = 0.00001;
+
         public _shadowGenerator: ShadowGenerator;
         private _parentedWorldMatrix: Matrix;
         public _excludedMeshesIds = new Array<string>();
@@ -129,6 +132,7 @@
             }
 
             serializationObject.range = this.range;
+            serializationObject.radius = this.radius;
 
             serializationObject.diffuse = this.diffuse.asArray();
             serializationObject.specular = this.specular.asArray();
@@ -167,6 +171,10 @@
             if (parsedLight.range) {
                 light.range = parsedLight.range;
             }
+            
+            if (parsedLight.radius) {
+                light.radius = parsedLight.radius;
+            }
 
             light.diffuse = Color3.FromArray(parsedLight.diffuse);
             light.specular = Color3.FromArray(parsedLight.specular);