Parcourir la source

Merge pull request #4814 from sebavan/master

Frostbite light falloff Step1
sebavan il y a 7 ans
Parent
commit
aec42d756e

+ 1 - 0
dist/preview release/what's new.md

@@ -80,6 +80,7 @@
 - Added attachToBoxBehavior to attach UI to a bounding box ([TrevorDev](https://github.com/TrevorDev))
 - Gizmo manager's internal gizmos are now public ([TrevorDev](https://github.com/TrevorDev))
 - Ability to customize meshes on gizmos ([TrevorDev](https://github.com/TrevorDev))
+- Added gltf light falloff [Issue 4148](https://github.com/BabylonJS/Babylon.js/issues/4148) ([sebavan](http://www.github.com/sebavan))
 
 ### glTF Loader
 

+ 14 - 1
src/Lights/babylon.light.ts

@@ -90,12 +90,25 @@ module BABYLON {
         @serialize()
         public intensity = 1.0;
 
+        private _range = Number.MAX_VALUE;
+        protected _inverseSquaredRange = 0;
+
         /**
          * Defines how far from the source the light is impacting in scene units.
          * Note: Unused in PBR material as the distance light falloff is defined following the inverse squared falloff.
          */
         @serialize()
-        public range = Number.MAX_VALUE;
+        public get range(): number {
+            return this._range
+        }
+        /**
+         * Defines how far from the source the light is impacting in scene units.
+         * Note: Unused in PBR material as the distance light falloff is defined following the inverse squared falloff.
+         */
+        public set range(value: number) {
+            this._range = value;
+            this._inverseSquaredRange = 1.0 / (this.range * this.range);
+        }
 
         /**
          * Cached photometric scale default to 1.0 as the automatic intensity mode defaults to 1.0 for every type

+ 12 - 3
src/Lights/babylon.pointLight.ts

@@ -145,6 +145,7 @@
             this._uniformBuffer.addUniform("vLightData", 4);
             this._uniformBuffer.addUniform("vLightDiffuse", 4);
             this._uniformBuffer.addUniform("vLightSpecular", 3);
+            this._uniformBuffer.addUniform("vLightFalloff", 4);
             this._uniformBuffer.addUniform("shadowsInfo", 3);
             this._uniformBuffer.addUniform("depthValues", 2);
             this._uniformBuffer.create();
@@ -163,11 +164,19 @@
                     this.transformedPosition.y,
                     this.transformedPosition.z,
                     0.0,
-                    lightIndex); 
-                return this;
+                    lightIndex);
+            }
+            else {
+                this._uniformBuffer.updateFloat4("vLightData", this.position.x, this.position.y, this.position.z, 0, lightIndex);
             }
 
-            this._uniformBuffer.updateFloat4("vLightData", this.position.x, this.position.y, this.position.z, 0, lightIndex);
+            this._uniformBuffer.updateFloat4("vLightFalloff",
+                this.range,
+                this._inverseSquaredRange,
+                0,
+                0,
+                lightIndex
+            );
             return this;
         }
 

+ 42 - 1
src/Lights/babylon.spotLight.ts

@@ -25,6 +25,12 @@
         */
 
         private _angle: number;
+        private _innerAngle: number = 0;
+        private _cosHalfAngle: number;
+
+        private _lightAngleScale: number;
+        private _lightAngleOffset: number;
+
         /**
          * Gets the cone angle of the spot light in Radians.
          */
@@ -37,8 +43,29 @@
          */
         public set angle(value: number) {
             this._angle = value;
+            this._cosHalfAngle = Math.cos(value * 0.5);
             this._projectionTextureProjectionLightDirty = true;
             this.forceProjectionMatrixCompute();
+            this._computeAngleValues();
+        }
+
+        /**
+         * Only used in gltf falloff mode, this defines the angle where 
+         * the directional falloff will start before cutting at angle which could be seen
+         * as outer angle.
+         */
+        @serialize()
+        public get innerAngle(): number {
+            return this._angle
+        }
+        /**
+         * Only used in gltf falloff mode, this defines the angle where 
+         * the directional falloff will start before cutting at angle which could be seen
+         * as outer angle.
+         */
+        public set innerAngle(value: number) {
+            this._innerAngle = value;
+            this._computeAngleValues();
         }
 
         private _shadowAngleScale: number;
@@ -261,11 +288,17 @@
             this._uniformBuffer.addUniform("vLightDiffuse", 4);
             this._uniformBuffer.addUniform("vLightSpecular", 3);
             this._uniformBuffer.addUniform("vLightDirection", 3);
+            this._uniformBuffer.addUniform("vLightFalloff", 4);
             this._uniformBuffer.addUniform("shadowsInfo", 3);
             this._uniformBuffer.addUniform("depthValues", 2);
             this._uniformBuffer.create();
         }
 
+        private _computeAngleValues(): void {
+            this._lightAngleScale = 1.0 / Math.max(0.001, (Math.cos(this._innerAngle * 0.5) - this._cosHalfAngle));
+            this._lightAngleOffset = -this._cosHalfAngle * this._lightAngleScale;
+        }
+
         /**
          * Sets the passed Effect object with the SpotLight transfomed position (or position if not parented) and normalized direction.  
          * @param effect The effect to update
@@ -299,9 +332,17 @@
                 normalizeDirection.x,
                 normalizeDirection.y,
                 normalizeDirection.z,
-                Math.cos(this.angle * 0.5),
+                this._cosHalfAngle,
                 lightIndex);
 
+            this._uniformBuffer.updateFloat4("vLightFalloff",
+                this.range,
+                this._inverseSquaredRange,
+                this._lightAngleScale,
+                this._lightAngleOffset,
+                lightIndex
+            );
+
             if (this.projectionTexture && this.projectionTexture.isReady()) {
                 if (this._projectionTextureViewLightDirty) {
                     this._computeProjectionTextureViewLightMatrix();

+ 33 - 6
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -125,6 +125,7 @@
         public EXPOSURE = false;
 
         public USEPHYSICALLIGHTFALLOFF = false;
+        public USEGLTFLIGHTFALLOFF = false;
         public TWOSIDEDLIGHTING = false;
         public SHADOWFLOAT = false;
         public CLIPPLANE = false;
@@ -164,6 +165,22 @@
      * http://doc.babylonjs.com/extensions/Physically_Based_Rendering
      */
     export abstract class PBRBaseMaterial extends PushMaterial {
+        /**
+         * PBRMaterialLightFalloff Physical: light is falling off following the inverse squared distance law.
+         */
+        public static readonly LIGHTFALLOFF_PHYSICAL = 0;
+
+        /**
+         * PBRMaterialLightFalloff gltf: light is falling off as described in the gltf moving to PBR document 
+         * to enhance interoperability with other engines.
+         */
+        public static readonly LIGHTFALLOFF_GLTF = 1;
+
+        /**
+         * PBRMaterialLightFalloff Standard: light is falling off like in the standard material 
+         * to enhance interoperability with other materials.
+         */
+        public static readonly LIGHTFALLOFF_STANDARD = 2;
 
         /**
          * Intensity of the direct lights e.g. the four lights available in your scene.
@@ -383,11 +400,10 @@
         protected _useAutoMicroSurfaceFromReflectivityMap = false;
 
         /**
-         * BJS is using an harcoded light falloff based on a manually sets up range.
-         * In PBR, one way to represents the fallof is to use the inverse squared root algorythm.
-         * This parameter can help you switch back to the BJS mode in order to create scenes using both materials.
+         * Defines the  falloff type used in this material.
+         * It by default is Physical.
          */
-        protected _usePhysicalLightFalloff = true;
+        protected _lightFalloff = PBRBaseMaterial.LIGHTFALLOFF_PHYSICAL;
 
         /**
          * Specifies that the material will keeps the reflection highlights over a transparent surface (only the most limunous ones).
@@ -1216,7 +1232,18 @@
 
                 defines.SPECULAROVERALPHA = this._useSpecularOverAlpha;
 
-                defines.USEPHYSICALLIGHTFALLOFF = this._usePhysicalLightFalloff;
+                if (this._lightFalloff === PBRBaseMaterial.LIGHTFALLOFF_STANDARD) {
+                    defines.USEPHYSICALLIGHTFALLOFF = false;
+                    defines.USEGLTFLIGHTFALLOFF = false;
+                }
+                else if (this._lightFalloff === PBRBaseMaterial.LIGHTFALLOFF_GLTF) {
+                    defines.USEPHYSICALLIGHTFALLOFF = false;
+                    defines.USEGLTFLIGHTFALLOFF = true;
+                }
+                else {
+                    defines.USEPHYSICALLIGHTFALLOFF = true;
+                    defines.USEGLTFLIGHTFALLOFF = false;
+                }
 
                 defines.RADIANCEOVERALPHA = this._useRadianceOverAlpha;
 
@@ -1606,7 +1633,7 @@
             if (mustRebind || !this.isFrozen) {
                 // Lights
                 if (scene.lightsEnabled && !this._disableLighting) {
-                    MaterialHelper.BindLights(scene, mesh, this._activeEffect, defines, this._maxSimultaneousLights, this._usePhysicalLightFalloff);
+                    MaterialHelper.BindLights(scene, mesh, this._activeEffect, defines, this._maxSimultaneousLights, this._lightFalloff !== PBRBaseMaterial.LIGHTFALLOFF_STANDARD);
                 }
 
                 // View

+ 53 - 31
src/Materials/PBR/babylon.pbrMaterial.ts

@@ -7,51 +7,26 @@
      * http://doc.babylonjs.com/extensions/Physically_Based_Rendering
      */
     export class PBRMaterial extends PBRBaseMaterial {
-        private static _PBRMATERIAL_OPAQUE = 0;
         /**
          * PBRMaterialTransparencyMode: No transparency mode, Alpha channel is not use.
          */
-        public static get PBRMATERIAL_OPAQUE(): number {
-            return this._PBRMATERIAL_OPAQUE;
-        }
-
-        /**
-         * Alpha Test mode, pixel are discarded below a certain threshold defined by the alpha cutoff value.
-         */
-        private static _PBRMATERIAL_ALPHATEST = 1;
+        public static readonly PBRMATERIAL_OPAQUE = 0;
 
         /**
          * PBRMaterialTransparencyMode: Alpha Test mode, pixel are discarded below a certain threshold defined by the alpha cutoff value.
          */
-        public static get PBRMATERIAL_ALPHATEST(): number {
-            return this._PBRMATERIAL_ALPHATEST;
-        }
-
-        /**
-         * Represents the value for Alpha Blend.  Pixels are blended (according to the alpha mode) with the already drawn pixels in the current frame buffer.
-         */
-        private static _PBRMATERIAL_ALPHABLEND = 2;
+        public static readonly PBRMATERIAL_ALPHATEST = 1;
 
         /**
          * PBRMaterialTransparencyMode: Pixels are blended (according to the alpha mode) with the already drawn pixels in the current frame buffer.
          */
-        public static get PBRMATERIAL_ALPHABLEND(): number {
-            return this._PBRMATERIAL_ALPHABLEND;
-        }
-
-        /**
-         * Represents the value for Alpha Test and Blend.  Pixels are blended (according to the alpha mode) with the already drawn pixels in the current frame buffer.
-         * They are also discarded below the alpha cutoff threshold to improve performances.
-         */
-        private static _PBRMATERIAL_ALPHATESTANDBLEND = 3;
+        public static readonly PBRMATERIAL_ALPHABLEND = 2;
 
         /**
          * PBRMaterialTransparencyMode: Pixels are blended (according to the alpha mode) with the already drawn pixels in the current frame buffer.
          * They are also discarded below the alpha cutoff threshold to improve performances.
          */
-        public static get PBRMATERIAL_ALPHATESTANDBLEND(): number {
-            return this._PBRMATERIAL_ALPHATESTANDBLEND;
-        }
+        public static readonly PBRMATERIAL_ALPHATESTANDBLEND = 3;
 
         /**
          * Intensity of the direct lights e.g. the four lights available in your scene.
@@ -346,8 +321,55 @@
          * This parameter can help you switch back to the BJS mode in order to create scenes using both materials.
          */
         @serialize()
-        @expandToProperty("_markAllSubMeshesAsTexturesDirty")
-        public usePhysicalLightFalloff = true;
+        public get usePhysicalLightFalloff(): boolean {
+            return this._lightFalloff === PBRBaseMaterial.LIGHTFALLOFF_PHYSICAL;
+        }
+
+        /**
+         * BJS is using an harcoded light falloff based on a manually sets up range.
+         * In PBR, one way to represents the fallof is to use the inverse squared root algorythm.
+         * This parameter can help you switch back to the BJS mode in order to create scenes using both materials.
+         */
+        public set usePhysicalLightFalloff(value: boolean) {
+            if (value !== this.usePhysicalLightFalloff) {
+                // Ensure the effect will be rebuilt.
+                this._markAllSubMeshesAsTexturesDirty();
+
+                if (value) {
+                    this._lightFalloff = PBRBaseMaterial.LIGHTFALLOFF_PHYSICAL;
+                }
+                else {
+                    this._lightFalloff = PBRBaseMaterial.LIGHTFALLOFF_STANDARD;
+                }
+            }
+        }
+
+        /**
+         * In order to support the falloff compatibility with gltf, a special mode has been added 
+         * to reproduce the gltf light falloff.
+         */
+        @serialize()
+        public get useGLTFLightFalloff(): boolean {
+            return this._lightFalloff === PBRBaseMaterial.LIGHTFALLOFF_GLTF;
+        }
+
+        /**
+         * In order to support the falloff compatibility with gltf, a special mode has been added 
+         * to reproduce the gltf light falloff.
+         */
+        public set useGLTFLightFalloff(value: boolean) {
+            if (value !== this.useGLTFLightFalloff) {
+                // Ensure the effect will be rebuilt.
+                this._markAllSubMeshesAsTexturesDirty();
+
+                if (value) {
+                    this._lightFalloff = PBRBaseMaterial.LIGHTFALLOFF_GLTF;
+                }
+                else {
+                    this._lightFalloff = PBRBaseMaterial.LIGHTFALLOFF_STANDARD;
+                }
+            }
+        }
 
         /**
          * Specifies that the material will keeps the reflection highlights over a transparent surface (only the most limunous ones).

+ 1 - 0
src/Materials/babylon.materialHelper.ts

@@ -330,6 +330,7 @@ module BABYLON {
                     "vLightDiffuse" + lightIndex,
                     "vLightSpecular" + lightIndex,
                     "vLightDirection" + lightIndex,
+                    "vLightFalloff" + lightIndex,
                     "vLightGround" + lightIndex,
                     "lightMatrix" + lightIndex,
                     "shadowsInfo" + lightIndex,

+ 8 - 10
src/Shaders/ShadersInclude/lightFragment.fx

@@ -4,22 +4,20 @@
     #else
         #ifdef PBR
             #ifdef SPOTLIGHT{X}
-                info = computeSpotLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDirection, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightDiffuse.a, roughness, NdotV, specularEnvironmentR0, specularEnvironmentR90, geometricRoughnessFactor, NdotL);
-            #endif
-            #ifdef HEMILIGHT{X}
+                info = computeSpotLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDirection, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightDiffuse.a, roughness, NdotV, specularEnvironmentR0, specularEnvironmentR90, geometricRoughnessFactor, light{X}.vLightFalloff, NdotL);
+            #elif defined(HEMILIGHT{X})
                 info = computeHemisphericLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightGround, roughness, NdotV, specularEnvironmentR0, specularEnvironmentR90, geometricRoughnessFactor, NdotL);
-            #endif
-            #if defined(POINTLIGHT{X}) || defined(DIRLIGHT{X})
-                info = computeLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightDiffuse.a, roughness, NdotV, specularEnvironmentR0, specularEnvironmentR90, geometricRoughnessFactor, NdotL);
+            #elif defined(POINTLIGHT{X})
+                info = computePointLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightDiffuse.a, roughness, NdotV, specularEnvironmentR0, specularEnvironmentR90, geometricRoughnessFactor, light{X}.vLightFalloff, NdotL);
+            #elif defined(DIRLIGHT{X})
+                info = computeDirectionalLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightDiffuse.a, roughness, NdotV, specularEnvironmentR0, specularEnvironmentR90, geometricRoughnessFactor, NdotL);
             #endif
         #else
             #ifdef SPOTLIGHT{X}
                 info = computeSpotLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDirection, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightDiffuse.a, glossiness);
-            #endif
-            #ifdef HEMILIGHT{X}
+            #elif defined(HEMILIGHT{X})
                 info = computeHemisphericLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightGround, glossiness);
-            #endif
-            #if defined(POINTLIGHT{X}) || defined(DIRLIGHT{X})
+            #elif defined(POINTLIGHT{X}) || defined(DIRLIGHT{X})
                 info = computeLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightDiffuse.a, glossiness);
             #endif
         #endif

+ 5 - 2
src/Shaders/ShadersInclude/lightFragmentDeclaration.fx

@@ -1,6 +1,7 @@
 #ifdef LIGHT{X}
 	uniform vec4 vLightData{X};
 	uniform vec4 vLightDiffuse{X};
+
 	#ifdef SPECULARTERM
 		uniform vec3 vLightSpecular{X};
 	#else
@@ -28,8 +29,10 @@
 	#endif
 	#ifdef SPOTLIGHT{X}
 		uniform vec4 vLightDirection{X};
-	#endif
-	#ifdef HEMILIGHT{X}
+		uniform vec4 vLightFalloff{X};
+	#elif defined(POINTLIGHT{X})
+		uniform vec4 vLightFalloff{X};
+	#elif defined(HEMILIGHT{X})
 		uniform vec3 vLightGround{X};
 	#endif
 	#ifdef PROJECTEDLIGHTTEXTURE{X}

+ 5 - 2
src/Shaders/ShadersInclude/lightUboDeclaration.fx

@@ -2,12 +2,15 @@
 	uniform Light{X}
 	{
 		vec4 vLightData;
+
 		vec4 vLightDiffuse;
 		vec3 vLightSpecular;
 		#ifdef SPOTLIGHT{X}
 			vec4 vLightDirection;
-		#endif
-		#ifdef HEMILIGHT{X}
+			vec4 vLightFalloff;
+		#elif defined(POINTLIGHT{X})
+			vec4 vLightFalloff;
+		#elif defined(HEMILIGHT{X})
 			vec3 vLightGround;
 		#endif
 		vec4 shadowsInfo;

+ 1 - 1
src/Shaders/ShadersInclude/pbrFunctions.fx

@@ -81,7 +81,7 @@ float computeDiffuseTerm(float NdotL, float NdotV, float VdotH, float roughness)
 
 float adjustRoughnessFromLightProperties(float roughness, float lightRadius, float lightDistance)
 {
-    #ifdef USEPHYSICALLIGHTFALLOFF
+    #if defined(USEPHYSICALLIGHTFALLOFF) || defined(USEGLTFLIGHTFALLOFF)
         // At small angle this approximation works. 
         float lightRoughness = lightRadius / lightDistance;
         // Distribution can sum.

+ 61 - 27
src/Shaders/ShadersInclude/pbrLightFunctions.fx

@@ -7,10 +7,21 @@ struct lightingInfo
     #endif
 };
 
-float computeDistanceLightFalloff(vec3 lightOffset, float lightDistanceSquared, float range)
+float computeDistanceLightFalloff(vec3 lightOffset, float lightDistanceSquared, float range, float inverseSquaredRange)
 {   
     #ifdef USEPHYSICALLIGHTFALLOFF
         float lightDistanceFalloff = 1.0 / ((lightDistanceSquared + 0.001));
+    #elif defined(USEGLTFLIGHTFALLOFF)
+        // Prevents infinity issues at 0.
+        const float minDistanceSquared = 0.01*0.01;
+        float lightDistanceFalloff = 1.0 / (max(lightDistanceSquared, minDistanceSquared));
+
+        float factor = lightDistanceSquared * inverseSquaredRange;
+        float attenuation = clamp(1.0 - factor * factor, 0., 1.);
+        attenuation *= attenuation;
+
+        // Smooth attenuation of the falloff defined by the range.
+        lightDistanceFalloff *= attenuation;
     #else
         float lightDistanceFalloff = max(0., 1.0 - length(lightOffset) / range);
     #endif
@@ -18,7 +29,7 @@ float computeDistanceLightFalloff(vec3 lightOffset, float lightDistanceSquared,
     return lightDistanceFalloff;
 }
 
-float computeDirectionalLightFalloff(vec3 lightDirection, vec3 directionToLightCenterW, float cosHalfAngle, float exponent)
+float computeDirectionalLightFalloff(vec3 lightDirection, vec3 directionToLightCenterW, float cosHalfAngle, float exponent, float lightAngleScale, float lightAngleOffset)
 {
     float falloff = 0.0;
     
@@ -36,6 +47,15 @@ float computeDirectionalLightFalloff(vec3 lightDirection, vec3 directionToLightC
         // not directional light type)
         vec4 lightDirectionSpreadSG = vec4(-lightDirection * concentrationKappa, -concentrationKappa);
         falloff = exp2(dot(vec4(directionToLightCenterW, 1.0), lightDirectionSpreadSG));
+    #elif defined(USEGLTFLIGHTFALLOFF)
+        // On the CPU
+        // float lightAngleScale = 1.0 f / max (0.001f, ( cosInner - cosOuter ));
+        // float lightAngleOffset = -cosOuter * angleScale;
+
+        float cd = dot(-lightDirection, directionToLightCenterW);
+        falloff = clamp(cd * lightAngleScale + lightAngleOffset, 0., 1.);
+        // smooth the transition
+        falloff *= falloff;
     #else
         float cosAngle = max(0.000000000000001, dot(-lightDirection, directionToLightCenterW));
         if (cosAngle >= cosHalfAngle)
@@ -47,32 +67,46 @@ float computeDirectionalLightFalloff(vec3 lightDirection, vec3 directionToLightC
     return falloff;
 }
 
-lightingInfo computeLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, float rangeRadius, float roughness, float NdotV, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor, out float NdotL) {
+lightingInfo computeDirectionalLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, float lightRadius, float roughness, float NdotV, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor, out float NdotL) {
+    lightingInfo result;
+
+    float lightDistance = length(-lightData.xyz);
+    vec3 lightDirection = normalize(-lightData.xyz);
+
+    // Roughness
+    roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
+    
+    // diffuse
+    vec3 H = normalize(viewDirectionW + lightDirection);
+    NdotL = clamp(dot(vNormal, lightDirection), 0.00000000001, 1.0);
+    float VdotH = clamp(dot(viewDirectionW, H), 0.0, 1.0);
+
+    float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
+    result.diffuse = diffuseTerm * diffuseColor;
+
+    #ifdef SPECULARTERM
+        // Specular
+        float NdotH = clamp(dot(vNormal, H), 0.000000000001, 1.0);
+
+        vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, reflectance0, reflectance90, geometricRoughnessFactor);
+        result.specular = specTerm * diffuseColor;
+    #endif
+
+    return result;
+}
+
+lightingInfo computePointLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, float lightRadius, float roughness, float NdotV, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor, vec4 lightFalloff, out float NdotL) {
     lightingInfo result;
 
-    vec3 lightDirection;
-    float attenuation = 1.0;
-    float lightDistance;
+    vec3 lightOffset = lightData.xyz - vPositionW;
+    float lightDistanceSquared = dot(lightOffset, lightOffset);
+    float attenuation = computeDistanceLightFalloff(lightOffset, lightDistanceSquared, lightFalloff.x, lightFalloff.y);
     
-    // Point
-    if (lightData.w == 0.)
-    {
-        vec3 lightOffset = lightData.xyz - vPositionW;
-        float lightDistanceSquared = dot(lightOffset, lightOffset);
-        attenuation = computeDistanceLightFalloff(lightOffset, lightDistanceSquared, rangeRadius);
-        
-        lightDistance = sqrt(lightDistanceSquared);
-        lightDirection = normalize(lightOffset);
-    }
-    // Directional
-    else
-    {
-        lightDistance = length(-lightData.xyz);
-        lightDirection = normalize(-lightData.xyz);
-    }
+    float lightDistance = sqrt(lightDistanceSquared);
+    vec3 lightDirection = normalize(lightOffset);
     
     // Roughness
-    roughness = adjustRoughnessFromLightProperties(roughness, rangeRadius, lightDistance);
+    roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
     
     // diffuse
     vec3 H = normalize(viewDirectionW + lightDirection);
@@ -93,7 +127,7 @@ lightingInfo computeLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData,
     return result;
 }
 
-lightingInfo computeSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec4 lightDirection, vec3 diffuseColor, vec3 specularColor, float rangeRadius, float roughness, float NdotV, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor, out float NdotL) {
+lightingInfo computeSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec4 lightDirection, vec3 diffuseColor, vec3 specularColor, float lightRadius, float roughness, float NdotV, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor, vec4 lightFalloff, out float NdotL) {
     lightingInfo result;
 
     vec3 lightOffset = lightData.xyz - vPositionW;
@@ -101,15 +135,15 @@ lightingInfo computeSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightDa
 
     // Distance falloff.
     float lightDistanceSquared = dot(lightOffset, lightOffset);
-    float attenuation = computeDistanceLightFalloff(lightOffset, lightDistanceSquared, rangeRadius);
+    float attenuation = computeDistanceLightFalloff(lightOffset, lightDistanceSquared, lightFalloff.x, lightFalloff.y);
     
     // Directional falloff.
-    float directionalAttenuation = computeDirectionalLightFalloff(lightDirection.xyz, directionToLightCenterW, lightDirection.w, lightData.w);
+    float directionalAttenuation = computeDirectionalLightFalloff(lightDirection.xyz, directionToLightCenterW, lightDirection.w, lightData.w, lightFalloff.z, lightFalloff.w);
     attenuation *= directionalAttenuation;
     
     // Roughness.
     float lightDistance = sqrt(lightDistanceSquared);
-    roughness = adjustRoughnessFromLightProperties(roughness, rangeRadius, lightDistance);
+    roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
     
     // Diffuse
     vec3 H = normalize(viewDirectionW + directionToLightCenterW);