Selaa lähdekoodia

Improve Shadows

Sebastien Vandenberghe 8 vuotta sitten
vanhempi
commit
070a83291c
40 muutettua tiedostoa jossa 591 lisäystä ja 393 poistoa
  1. 1 1
      materialsLibrary/src/cell/babylon.cellMaterial.ts
  2. 1 1
      materialsLibrary/src/cell/cell.vertex.fx
  3. 1 1
      materialsLibrary/src/custom/babylon.customMaterial.ts
  4. 1 1
      materialsLibrary/src/fur/fur.vertex.fx
  5. 1 1
      materialsLibrary/src/gradient/babylon.gradientMaterial.ts
  6. 1 1
      materialsLibrary/src/gradient/gradient.vertex.fx
  7. 0 1
      materialsLibrary/src/lava/babylon.lavaMaterial.ts
  8. 1 1
      materialsLibrary/src/lava/lava.vertex.fx
  9. 1 3
      materialsLibrary/src/legacyPBR/babylon.legacyPBRMaterial.ts
  10. 1 1
      materialsLibrary/src/legacyPBR/legacyPbr.vertex.fx
  11. 1 2
      materialsLibrary/src/normal/babylon.normalMaterial.ts
  12. 1 1
      materialsLibrary/src/normal/normal.vertex.fx
  13. 1 1
      materialsLibrary/src/shadowOnly/babylon.shadowOnlyMaterial.ts
  14. 1 1
      materialsLibrary/src/shadowOnly/shadowOnly.vertex.fx
  15. 1 1
      materialsLibrary/src/simple/babylon.simpleMaterial.ts
  16. 1 1
      materialsLibrary/src/simple/simple.vertex.fx
  17. 1 1
      materialsLibrary/src/terrain/terrain.vertex.fx
  18. 1 1
      materialsLibrary/src/triPlanar/triplanar.vertex.fx
  19. 1 1
      materialsLibrary/src/water/water.vertex.fx
  20. 374 257
      src/Lights/Shadows/babylon.shadowGenerator.ts
  21. 17 0
      src/Lights/babylon.directionalLight.ts
  22. 1 0
      src/Lights/babylon.hemisphericLight.ts
  23. 2 1
      src/Lights/babylon.pointLight.ts
  24. 29 1
      src/Lights/babylon.shadowLight.ts
  25. 2 1
      src/Lights/babylon.spotLight.ts
  26. 1 3
      src/Materials/PBR/babylon.pbrBaseMaterial.ts
  27. 5 8
      src/Materials/babylon.materialHelper.ts
  28. 0 1
      src/Materials/babylon.standardMaterial.ts
  29. 5 4
      src/Materials/babylon.uniformBuffer.ts
  30. 19 11
      src/Shaders/ShadersInclude/lightFragment.fx
  31. 4 0
      src/Shaders/ShadersInclude/lightFragmentDeclaration.fx
  32. 4 0
      src/Shaders/ShadersInclude/lightUboDeclaration.fx
  33. 90 32
      src/Shaders/ShadersInclude/shadowsFragmentFunctions.fx
  34. 2 1
      src/Shaders/ShadersInclude/shadowsVertex.fx
  35. 0 6
      src/Shaders/ShadersInclude/shadowsVertexDeclaration.fx
  36. 1 2
      src/Shaders/default.vertex.fx
  37. 1 1
      src/Shaders/pbr.vertex.fx
  38. 4 19
      src/Shaders/shadowMap.fragment.fx
  39. 12 14
      src/Shaders/shadowMap.vertex.fx
  40. 0 9
      src/babylon.engine.ts

+ 1 - 1
materialsLibrary/src/cell/babylon.cellMaterial.ts

@@ -168,7 +168,7 @@ module BABYLON {
                                 "vFogInfos", "vFogColor", "pointSize",
                                 "vDiffuseInfos", 
                                 "mBones",
-                                "vClipPlane", "diffuseMatrix", "depthValues"
+                                "vClipPlane", "diffuseMatrix"
                 ];
                 var samplers = ["diffuseSampler"];
                 var uniformBuffers = [];

+ 1 - 1
materialsLibrary/src/cell/cell.vertex.fx

@@ -47,7 +47,7 @@ varying vec4 vColor;
 #include<clipPlaneVertexDeclaration>
 
 #include<fogVertexDeclaration>
-#include<shadowsVertexDeclaration>[0..maxSimultaneousLights]
+#include<__decl__lightFragment>[0..maxSimultaneousLights]
 
 void main(void) {
 

+ 1 - 1
materialsLibrary/src/custom/babylon.customMaterial.ts

@@ -404,7 +404,7 @@ varying vec4 vColor;\n\
 #include<bumpVertexDeclaration>\n\
 #include<clipPlaneVertexDeclaration>\n\
 #include<fogVertexDeclaration>\n\
-#include<shadowsVertexDeclaration>[0..maxSimultaneousLights]\n\
+#include<__decl__lightFragment>[0..maxSimultaneousLights]\n\
 #include<morphTargetsVertexGlobalDeclaration>\n\
 #include<morphTargetsVertexDeclaration>[0..maxSimultaneousMorphTargets]\n\
 #ifdef REFLECTIONMAP_SKYBOX\n\

+ 1 - 1
materialsLibrary/src/fur/fur.vertex.fx

@@ -62,7 +62,7 @@ varying vec4 vColor;
 
 #include<clipPlaneVertexDeclaration>
 #include<fogVertexDeclaration>
-#include<shadowsVertexDeclaration>[0..maxSimultaneousLights]
+#include<__decl__lightFragment>[0..maxSimultaneousLights]
 
 float Rand(vec3 rv) {
 	float x = dot(rv, vec3(12.9898,78.233, 24.65487));

+ 1 - 1
materialsLibrary/src/gradient/babylon.gradientMaterial.ts

@@ -189,7 +189,7 @@ module BABYLON {
                     "vDiffuseInfos", 
                     "mBones",
                     "vClipPlane", "diffuseMatrix",
-                    "depthValues", "topColor", "bottomColor", "offset", "smoothness"
+                    "topColor", "bottomColor", "offset", "smoothness"
                 ];
                 var samplers = ["diffuseSampler"];
                 var uniformBuffers = [];

+ 1 - 1
materialsLibrary/src/gradient/gradient.vertex.fx

@@ -46,7 +46,7 @@ varying vec4 vColor;
 #include<clipPlaneVertexDeclaration>
 
 #include<fogVertexDeclaration>
-#include<shadowsVertexDeclaration>[0..maxSimultaneousLights]
+#include<__decl__lightFragment>[0..maxSimultaneousLights]
 
 void main(void) {
 #include<instancesVertex>

+ 0 - 1
materialsLibrary/src/lava/babylon.lavaMaterial.ts

@@ -213,7 +213,6 @@ module BABYLON {
                     "vDiffuseInfos",
                     "mBones",
                     "vClipPlane", "diffuseMatrix",
-                    "depthValues",
                     "time", "speed","movingSpeed",
                     "fogColor","fogDensity", "lowFrequencySpeed"
                 ];

+ 1 - 1
materialsLibrary/src/lava/lava.vertex.fx

@@ -52,7 +52,7 @@ varying vec4 vColor;
 #include<clipPlaneVertexDeclaration>
 
 #include<fogVertexDeclaration>
-#include<shadowsVertexDeclaration>[0..maxSimultaneousLights]
+#include<__decl__lightFragment>[0..maxSimultaneousLights]
 
 /* NOISE FUNCTIONS */
 ////// ASHIMA webgl noise

+ 1 - 3
materialsLibrary/src/legacyPBR/babylon.legacyPBRMaterial.ts

@@ -636,7 +636,6 @@ module BABYLON {
 
         public static BindLights(scene: Scene, mesh: AbstractMesh, effect: Effect, defines: MaterialDefines, useScalarInLinearSpace: boolean, maxSimultaneousLights: number, usePhysicalLightFalloff: boolean) {
             var lightIndex = 0;
-            var depthValuesAlreadySet = false;
             for (var light of mesh._lightSources) {
                 var useUbo = light._uniformBuffer.useUbo;
 
@@ -658,7 +657,7 @@ module BABYLON {
 
                 // Shadows
                 if (scene.shadowsEnabled) {
-                    depthValuesAlreadySet = MaterialHelper.BindLightShadow(light, scene, mesh, lightIndex + "", effect, depthValuesAlreadySet);
+                    MaterialHelper.BindLightShadow(light, scene, mesh, lightIndex + "", effect);
                 }
 
                 light._uniformBuffer.update();
@@ -1148,7 +1147,6 @@ module BABYLON {
                         "vAlbedoInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vEmissiveInfos", "vReflectivityInfos", "vMicroSurfaceSamplerInfos", "vBumpInfos", "vLightmapInfos", "vRefractionInfos",
                         "mBones",
                         "vClipPlane", "albedoMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "reflectivityMatrix", "microSurfaceSamplerMatrix", "bumpMatrix", "lightmapMatrix", "refractionMatrix",
-                        "depthValues",
                         "opacityParts", "emissiveLeftColor", "emissiveRightColor",
                         "vLightingIntensity", "vOverloadedShadowIntensity", "vOverloadedIntensity", "vOverloadedAlbedo", "vOverloadedReflection", "vOverloadedReflectivity", "vOverloadedEmissive", "vOverloadedMicroSurface",
                         "logarithmicDepthConstant",

+ 1 - 1
materialsLibrary/src/legacyPBR/legacyPbr.vertex.fx

@@ -70,7 +70,7 @@ varying vec4 vColor;
 #include<bumpVertexDeclaration>
 #include<clipPlaneVertexDeclaration>
 #include<fogVertexDeclaration>
-#include<shadowsVertexDeclaration>[0..maxSimultaneousLights]
+#include<__decl__lightFragment>[0..maxSimultaneousLights]
 
 #include<morphTargetsVertexGlobalDeclaration>
 #include<morphTargetsVertexDeclaration>[0..maxSimultaneousMorphTargets]

+ 1 - 2
materialsLibrary/src/normal/babylon.normalMaterial.ts

@@ -192,8 +192,7 @@ module BABYLON {
                     "vFogInfos", "vFogColor", "pointSize",
                     "vDiffuseInfos", 
                     "mBones",
-                    "vClipPlane", "diffuseMatrix",
-                    "depthValues"
+                    "vClipPlane", "diffuseMatrix"
                 ];
                 var samplers = ["diffuseSampler"];
                 var uniformBuffers = [];

+ 1 - 1
materialsLibrary/src/normal/normal.vertex.fx

@@ -47,7 +47,7 @@ varying vec4 vColor;
 #include<clipPlaneVertexDeclaration>
 
 #include<fogVertexDeclaration>
-#include<shadowsVertexDeclaration>[0..maxSimultaneousLights]
+#include<__decl__lightFragment>[0..maxSimultaneousLights]
 
 void main(void) {
 

+ 1 - 1
materialsLibrary/src/shadowOnly/babylon.shadowOnlyMaterial.ts

@@ -105,7 +105,7 @@ module BABYLON {
                 var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType",
                                 "vFogInfos", "vFogColor", "pointSize",
                                 "mBones",
-                                "vClipPlane", "depthValues"
+                                "vClipPlane"
                 ];
                 var samplers = [];
                 

+ 1 - 1
materialsLibrary/src/shadowOnly/shadowOnly.vertex.fx

@@ -32,7 +32,7 @@ varying vec4 vColor;
 #include<clipPlaneVertexDeclaration>
 
 #include<fogVertexDeclaration>
-#include<shadowsVertexDeclaration>[0..maxSimultaneousLights]
+#include<__decl__lightFragment>[0..maxSimultaneousLights]
 
 void main(void) {
 

+ 1 - 1
materialsLibrary/src/simple/babylon.simpleMaterial.ts

@@ -157,7 +157,7 @@ module BABYLON {
                                 "vFogInfos", "vFogColor", "pointSize",
                                 "vDiffuseInfos", 
                                 "mBones",
-                                "vClipPlane", "diffuseMatrix", "depthValues"
+                                "vClipPlane", "diffuseMatrix"
                 ];
                 var samplers = ["diffuseSampler"];
                 var uniformBuffers = [];

+ 1 - 1
materialsLibrary/src/simple/simple.vertex.fx

@@ -47,7 +47,7 @@ varying vec4 vColor;
 #include<clipPlaneVertexDeclaration>
 
 #include<fogVertexDeclaration>
-#include<shadowsVertexDeclaration>[0..maxSimultaneousLights]
+#include<__decl__lightFragment>[0..maxSimultaneousLights]
 
 void main(void) {
 

+ 1 - 1
materialsLibrary/src/terrain/terrain.vertex.fx

@@ -45,7 +45,7 @@ varying vec4 vColor;
 
 #include<clipPlaneVertexDeclaration>
 #include<fogVertexDeclaration>
-#include<shadowsVertexDeclaration>[0..maxSimultaneousLights]
+#include<__decl__lightFragment>[0..maxSimultaneousLights]
 
 void main(void) {
 	#include<instancesVertex>

+ 1 - 1
materialsLibrary/src/triPlanar/triplanar.vertex.fx

@@ -48,7 +48,7 @@ varying vec4 vColor;
 #include<clipPlaneVertexDeclaration>
 
 #include<fogVertexDeclaration>
-#include<shadowsVertexDeclaration>[0..maxSimultaneousLights]
+#include<__decl__lightFragment>[0..maxSimultaneousLights]
 
 void main(void)
 {

+ 1 - 1
materialsLibrary/src/water/water.vertex.fx

@@ -49,7 +49,7 @@ varying vec4 vColor;
 #include<clipPlaneVertexDeclaration>
 
 #include<fogVertexDeclaration>
-#include<shadowsVertexDeclaration>[0..maxSimultaneousLights]
+#include<__decl__lightFragment>[0..maxSimultaneousLights]
 
 #include<logDepthDeclaration>
 

+ 374 - 257
src/Lights/Shadows/babylon.shadowGenerator.ts

@@ -1,11 +1,14 @@
 module BABYLON {
+    /**
+     * Interface to implement to create a shadow generator compatible with BJS.
+     */
     export interface IShadowGenerator {
         getShadowMap(): RenderTargetTexture;
  
         isReady(subMesh: SubMesh, useInstances: boolean): boolean;
 
         prepareDefines(defines: MaterialDefines, lightIndex: number): void;
-        bindShadowLight(lightIndex: string, effect: Effect, depthValuesAlreadySet: boolean): boolean;
+        bindShadowLight(lightIndex: string, effect: Effect): void;
 
         recreateShadowMap(): void;
 
@@ -18,6 +21,8 @@
         private static _FILTER_EXPONENTIALSHADOWMAP = 1;
         private static _FILTER_POISSONSAMPLING = 2;
         private static _FILTER_BLUREXPONENTIALSHADOWMAP = 3;
+        private static _FILTER_CLOSEEXPONENTIALSHADOWMAP = 4;
+        private static _FILTER_BLURCLOSEEXPONENTIALSHADOWMAP = 5;
 
         // Static
         public static get FILTER_NONE(): number {
@@ -36,67 +41,116 @@
             return ShadowGenerator._FILTER_BLUREXPONENTIALSHADOWMAP;
         }
 
-        // Members
-        private _filter = ShadowGenerator.FILTER_NONE;
-        public blurScale = 2;
-        private _blurBoxOffset = 0;
-        private _bias = 0.00005;
-        private _lightDirection = Vector3.Zero();
-        private _depthScale: number;
+        public static get FILTER_CLOSEEXPONENTIALSHADOWMAP(): number {
+            return ShadowGenerator._FILTER_CLOSEEXPONENTIALSHADOWMAP;
+        }
 
-        public forceBackFacesOnly = false;
+        public static get FILTER_BLURCLOSEEXPONENTIALSHADOWMAP(): number {
+            return ShadowGenerator._FILTER_BLURCLOSEEXPONENTIALSHADOWMAP;
+        }
 
+        // Members
+        private _bias = 0.00005;
         public get bias(): number {
             return this._bias;
         }
-
         public set bias(bias: number) {
             this._bias = bias;
         }
 
+        private _blurBoxOffset = 1;
         public get blurBoxOffset(): number {
             return this._blurBoxOffset;
         }
-
         public set blurBoxOffset(value: number) {
             if (this._blurBoxOffset === value) {
                 return;
             }
 
             this._blurBoxOffset = value;
+            this._disposeBlurPostProcesses();
+        }
 
-            if (this._boxBlurPostprocess) {
-                this._boxBlurPostprocess.dispose();
+        private _blurScale = 2;
+        public get blurScale(): number {
+            return this._blurScale;
+        }
+        public set blurScale(value: number) {
+            if (this._blurScale === value) {
+                return;
             }
 
-            this._boxBlurPostprocess = new PostProcess("DepthBoxBlur", "depthBoxBlur", ["screenSize", "boxOffset"], [], 1.0 / this.blurScale, null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, "#define OFFSET " + value, this._textureType);
-            this._boxBlurPostprocess.onApplyObservable.add(effect => {
-                effect.setFloat2("screenSize", this._mapSize / this.blurScale, this._mapSize / this.blurScale);
-            });
+            this._blurScale = value;
+            this._disposeBlurPostProcesses();
+        }
+
+        private _blurKernel = 1;
+        public get blurKernel(): number {
+            return this._blurKernel;
+        }
+        public set blurKernel(value: number) {
+            if (this._blurKernel === value) {
+                return;
+            }
+
+            this._blurKernel = value;
+            this._disposeBlurPostProcesses();
         }
 
+        private _useKernelBlur = false;
+        public get useKernelBlur(): boolean {
+            return this._useKernelBlur;
+        }
+        public set useKernelBlur(value: boolean) {
+            if (this._useKernelBlur === value) {
+                return;
+            }
+
+            this._useKernelBlur = value;
+            this._disposeBlurPostProcesses();
+        }
+
+        private _depthScale: number;
         public get depthScale(): number {
             return this._depthScale !== undefined ? this._depthScale : this._light.getDepthScale();
         }
-
         public set depthScale(value: number) {
             this._depthScale = value;
         }
 
+        private _filter = ShadowGenerator.FILTER_NONE;
         public get filter(): number {
             return this._filter;
         }
-
         public set filter(value: number) {
+            // Blurring the cubemap is going to be too expensive. Reverting to unblurred version
+            if (this._light.needCube()) {
+                if (value === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP) {
+                    this.useExponentialShadowMap = true;
+                }
+                else if (value === ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP) {
+                    this.useCloseExponentialShadowMap = true;
+                }
+            }
+
             if (this._filter === value) {
                 return;
             }
 
             this._filter = value;
+            this._disposeBlurPostProcesses();
             this._applyFilterValues();
             this._light._markMeshesAsLightDirty();
         }
 
+        public get usePoissonSampling(): boolean {
+            return this.filter === ShadowGenerator.FILTER_POISSONSAMPLING;
+        }
+
+        public set usePoissonSampling(value: boolean) {
+            this.filter = (value ? ShadowGenerator.FILTER_POISSONSAMPLING : ShadowGenerator.FILTER_NONE);
+        }
+
         public get useVarianceShadowMap(): boolean {
             Tools.Warn("VSM are now replaced by ESM. Please use useExponentialShadowMap instead.");
             return this.useExponentialShadowMap;
@@ -106,6 +160,15 @@
             this.useExponentialShadowMap = value;
         }
 
+        public get useBlurVarianceShadowMap(): boolean {
+            Tools.Warn("VSM are now replaced by ESM. Please use useBlurExponentialShadowMap instead.");
+            return this.useBlurExponentialShadowMap;
+        }
+        public set useBlurVarianceShadowMap(value: boolean) {
+            Tools.Warn("VSM are now replaced by ESM. Please use useBlurExponentialShadowMap instead.");
+            this.useBlurExponentialShadowMap = value;
+        }
+
         public get useExponentialShadowMap(): boolean {
             return this.filter === ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP;
         }
@@ -113,40 +176,90 @@
             this.filter = (value ? ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP : ShadowGenerator.FILTER_NONE);
         }
 
-        public get usePoissonSampling(): boolean {
-            return this.filter === ShadowGenerator.FILTER_POISSONSAMPLING;
+        public get useBlurExponentialShadowMap(): boolean {
+            return this.filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP;
+        }
+        public set useBlurExponentialShadowMap(value: boolean) {
+            this.filter = (value ? ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP : ShadowGenerator.FILTER_NONE);
         }
 
-        public set usePoissonSampling(value: boolean) {
-            this.filter = (value ? ShadowGenerator.FILTER_POISSONSAMPLING : ShadowGenerator.FILTER_NONE);
+        public get useCloseExponentialShadowMap(): boolean {
+            return this.filter === ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP;
+        }
+        public set useCloseExponentialShadowMap(value: boolean) {
+            this.filter = (value ? ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP : ShadowGenerator.FILTER_NONE);
         }
 
-        public get useBlurVarianceShadowMap(): boolean {
-            Tools.Warn("VSM are now replaced by ESM. Please use useBlurExponentialShadowMap instead.");
-            return this.useBlurExponentialShadowMap;
+        public get useBlurCloseExponentialShadowMap(): boolean {
+            return this.filter === ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP;
         }
-        public set useBlurVarianceShadowMap(value: boolean) {
-            Tools.Warn("VSM are now replaced by ESM. Please use useBlurExponentialShadowMap instead.");
-            this.useBlurExponentialShadowMap = value;
+        public set useBlurCloseExponentialShadowMap(value: boolean) {
+            this.filter = (value ? ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP : ShadowGenerator.FILTER_NONE);
         }
 
-        public get useBlurExponentialShadowMap(): boolean {
-            return this.filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP;
+        private _darkness = 0;
+        /**
+         * Returns the darkness value (float).  
+         */
+        public getDarkness(): number {
+            return this._darkness;
         }
-        public set useBlurExponentialShadowMap(value: boolean) {
-            if (this._light.needCube() && value) {
-                this.useExponentialShadowMap = true; // Blurring the cubemap is going to be too expensive. Reverting to unblurred version
-            } else {
-                this.filter = (value ? ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP : ShadowGenerator.FILTER_NONE);
+        /**
+         * Sets the ShadowGenerator darkness value (float <= 1.0).  
+         * Returns the ShadowGenerator.  
+         */
+        public setDarkness(darkness: number): ShadowGenerator {
+            if (darkness >= 1.0)
+                this._darkness = 1.0;
+            else if (darkness <= 0.0)
+                this._darkness = 0.0;
+            else
+                this._darkness = darkness;
+            return this;
+        }
+        
+        private _transparencyShadow = false;
+        /**
+         * Sets the ability to have transparent shadow (boolean).  
+         * Returns the ShadowGenerator.  
+         */
+        public setTransparencyShadow(hasShadow: boolean): ShadowGenerator {
+            this._transparencyShadow = hasShadow;
+            return this;
+        }
+
+        private _shadowMap: RenderTargetTexture;
+        private _shadowMap2: RenderTargetTexture;
+        /**
+         * Returns a RenderTargetTexture object : the shadow map texture.  
+         */
+        public getShadowMap(): RenderTargetTexture {
+            return this._shadowMap;
+        }
+        /**
+         * Returns the most ready computed shadow map as a RenderTargetTexture object.  
+         */
+        public getShadowMapForRendering(): RenderTargetTexture {
+            if (this._shadowMap2) {
+                return this._shadowMap2;
             }
+
+            return this._shadowMap;
         }
 
         private _light: IShadowLight;
+        /**
+         * Returns the associated light object.  
+         */
+        public getLight(): IShadowLight {
+            return this._light;
+        }
+
+        public forceBackFacesOnly = false;
+
         private _scene: Scene;
-        private _shadowMap: RenderTargetTexture;
-        private _shadowMap2: RenderTargetTexture;
-        private _darkness = 0;
-        private _transparencyShadow = false;
+        private _lightDirection = Vector3.Zero();
+
         private _effect: Effect;
 
         private _viewMatrix = Matrix.Zero();
@@ -159,10 +272,12 @@
         private _currentRenderID: number;
         private _downSamplePostprocess: PassPostProcess;
         private _boxBlurPostprocess: PostProcess;
+        private _kernelBlurXPostprocess: PostProcess;
+        private _kernelBlurYPostprocess: PostProcess;
+        private _blurPostProcesses: PostProcess[];
         private _mapSize: number;
         private _currentFaceIndex = 0;
         private _currentFaceIndexCache = 0;
-
         private _textureType: number;
         private _isCube = false;
 
@@ -193,165 +308,177 @@
                 this._textureType = Engine.TEXTURETYPE_UNSIGNED_INT;
             }
 
-            this._initializeGenerator(1);
+            this._initializeGenerator();
         }
-        
-        private _initializeGenerator(boxBlurOffset: number): void {
-            var light = this._light;
-            var scene = this._scene;
-            var textureType = this._textureType;
 
-            light._markMeshesAsLightDirty();
+        private _initializeGenerator(): void {
+            this._light._markMeshesAsLightDirty();
+            this._initializeShadowMap();
+        }
 
+        private _initializeShadowMap(): void {
             // Render target
-            this._shadowMap = new RenderTargetTexture(light.name + "_shadowMap", this._mapSize, this._scene, false, true, textureType, light.needCube());
+            this._shadowMap = new RenderTargetTexture(this._light.name + "_shadowMap", this._mapSize, this._scene, false, true, this._textureType, this._light.needCube());
             this._shadowMap.wrapU = Texture.CLAMP_ADDRESSMODE;
             this._shadowMap.wrapV = Texture.CLAMP_ADDRESSMODE;
             this._shadowMap.anisotropicFilteringLevel = 1;
             this._shadowMap.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
             this._shadowMap.renderParticles = false;
 
+            // Record Face Index before render.
             this._shadowMap.onBeforeRenderObservable.add((faceIndex: number) => {
                 this._currentFaceIndex = faceIndex;
             });
 
+            // Custom render function.
+            this._shadowMap.customRenderFunction = this._renderForShadowMap.bind(this);
+
+            // Blur if required afer render.
             this._shadowMap.onAfterUnbindObservable.add(() => {
-                if (!this.useBlurExponentialShadowMap) {
+                if (!this.useBlurExponentialShadowMap && !this.useBlurCloseExponentialShadowMap) {
                     return;
                 }
 
-                if (!this._shadowMap2) {
-                    this._shadowMap2 = new RenderTargetTexture(light.name + "_shadowMap", this._mapSize, this._scene, false, true, textureType);
-                    this._shadowMap2.wrapU = Texture.CLAMP_ADDRESSMODE;
-                    this._shadowMap2.wrapV = Texture.CLAMP_ADDRESSMODE;
-                    this._shadowMap2.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
+                if (!this._blurPostProcesses) {
+                    this._initializeBlurRTTAndPostProcesses();
+                }
 
-                    this._downSamplePostprocess = new PassPostProcess("downScale", 1.0 / this.blurScale, null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, textureType);
-                    this._downSamplePostprocess.onApplyObservable.add(effect => {
-                        effect.setTexture("textureSampler", this._shadowMap);
-                    });
+                this._scene.postProcessManager.directRender(this._blurPostProcesses, this.getShadowMapForRendering().getInternalTexture());
+            });
 
-                    this.blurBoxOffset = boxBlurOffset;
+            // Clear according to the chosen filter.
+            this._shadowMap.onClearObservable.add((engine: Engine) => {
+                if (this.useExponentialShadowMap || this.useBlurExponentialShadowMap) {
+                    engine.clear(new Color4(0, 0, 0, 0), true, true, true);
+                }
+                else {
+                    engine.clear(new Color4(1.0, 1.0, 1.0, 1.0), true, true, true);
                 }
-
-                this._scene.postProcessManager.directRender([this._downSamplePostprocess, this._boxBlurPostprocess], this._shadowMap2.getInternalTexture());
             });
+        }
 
-            // Custom render function
-            var renderSubMesh = (subMesh: SubMesh): void => {
-                var mesh = subMesh.getRenderingMesh();
-                var scene = this._scene;
-                var engine = scene.getEngine();
+        private _initializeBlurRTTAndPostProcesses(): void {
+            var engine = this._scene.getEngine();
+            var targetSize = this._mapSize / this.blurScale;
 
-                // Culling
-                engine.setState(subMesh.getMaterial().backFaceCulling);
+            if (!this.useKernelBlur || this.blurScale !== 1.0) {
+                this._shadowMap2 = new RenderTargetTexture(this._light.name + "_shadowMap2", targetSize, this._scene, false, true, this._textureType);
+                this._shadowMap2.wrapU = Texture.CLAMP_ADDRESSMODE;
+                this._shadowMap2.wrapV = Texture.CLAMP_ADDRESSMODE;
+                this._shadowMap2.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
+            }
 
-                // Managing instances
-                var batch = mesh._getInstancesRenderList(subMesh._id);
+            if (this.useKernelBlur) {
+                this._kernelBlurXPostprocess = new BlurPostProcess(this._light.name + "KernelBlurX", new Vector2(1, 0), this.blurKernel, 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, this._textureType);
+                this._kernelBlurXPostprocess.width = targetSize;
+                this._kernelBlurXPostprocess.height = targetSize;
+                this._kernelBlurXPostprocess.onApplyObservable.add(effect => {
+                    effect.setTexture("textureSampler", this._shadowMap);
+                });
 
-                if (batch.mustReturn) {
-                    return;
-                }
+                this._kernelBlurYPostprocess = new BlurPostProcess(this._light.name + "KernelBlurY", new Vector2(0, 1), this.blurKernel, 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, this._textureType);
 
-                var hardwareInstancedRendering = (engine.getCaps().instancedArrays !== null) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
+                this._kernelBlurXPostprocess.autoClear = false;
+                this._kernelBlurYPostprocess.autoClear = false;
 
-                if (this.isReady(subMesh, hardwareInstancedRendering)) {
-                    engine.enableEffect(this._effect);
-                    mesh._bind(subMesh, this._effect, Material.TriangleFillMode);
-                    var material = subMesh.getMaterial();
+                this._blurPostProcesses = [this._kernelBlurXPostprocess, this._kernelBlurYPostprocess];
+            }
+            else {
+                this._boxBlurPostprocess = new PostProcess(this._light.name + "DepthBoxBlur", "depthBoxBlur", ["screenSize", "boxOffset"], [], 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, "#define OFFSET " + this._blurBoxOffset, this._textureType);
+                this._boxBlurPostprocess.onApplyObservable.add(effect => {
+                    effect.setFloat2("screenSize", targetSize, targetSize);
+                    effect.setTexture("textureSampler", this._shadowMap);
+                });
 
-                    this._effect.setFloat2("biasAndScale", this.bias, this.depthScale);
+                this._boxBlurPostprocess.autoClear = false;
 
-                    this._effect.setMatrix("viewProjection", this.getTransformMatrix());
-                    this._effect.setVector3("lightPosition", this.getLight().position);
+                this._blurPostProcesses = [this._boxBlurPostprocess];
+            }
+        }
 
-                    if (this.getLight().needCube()) {
-                        this._effect.setFloat2("depthValues", scene.activeCamera.minZ, scene.activeCamera.maxZ);
-                    }
+        private _renderForShadowMap(opaqueSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>): void {
+            var index: number;
+            for (index = 0; index < opaqueSubMeshes.length; index++) {
+                this._renderSubMeshForShadowMap(opaqueSubMeshes.data[index]);
+            }
 
-                    // Alpha test
-                    if (material && material.needAlphaTesting()) {
-                        var alphaTexture = material.getAlphaTestTexture();
-                        this._effect.setTexture("diffuseSampler", alphaTexture);
-                        this._effect.setMatrix("diffuseMatrix", alphaTexture.getTextureMatrix());
-                    }
+            for (index = 0; index < alphaTestSubMeshes.length; index++) {
+                this._renderSubMeshForShadowMap(alphaTestSubMeshes.data[index]);
+            }
 
-                    // Bones
-                    if (mesh.useBones && mesh.computeBonesUsingShaders) {
-                        this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
-                    }
+            if (this._transparencyShadow) {
+                for (index = 0; index < transparentSubMeshes.length; index++) {
+                    this._renderSubMeshForShadowMap(transparentSubMeshes.data[index]);
+                }
+            }
+        }
 
-                    if (this.forceBackFacesOnly) {
-                        engine.setState(true, 0, false, true);
-                    }
+        private _renderSubMeshForShadowMap(subMesh: SubMesh): void {
+            var mesh = subMesh.getRenderingMesh();
+            var scene = this._scene;
+            var engine = scene.getEngine();
 
-                    // Draw
-                    mesh._processRendering(subMesh, this._effect, Material.TriangleFillMode, batch, hardwareInstancedRendering,
-                        (isInstance, world) => this._effect.setMatrix("world", world));
+            // Culling
+            engine.setState(subMesh.getMaterial().backFaceCulling);
 
-                    if (this.forceBackFacesOnly) {
-                        engine.setState(true, 0, false, false);
-                    }
-                } else {
-                    // Need to reset refresh rate of the shadowMap
-                    this._shadowMap.resetRefreshCounter();
-                }
-            };
+            // Managing instances
+            var batch = mesh._getInstancesRenderList(subMesh._id);
+            if (batch.mustReturn) {
+                return;
+            }
 
-            this._shadowMap.customRenderFunction = (opaqueSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>): void => {
-                var index: number;
+            var hardwareInstancedRendering = (engine.getCaps().instancedArrays !== null) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
+            if (this.isReady(subMesh, hardwareInstancedRendering)) {
+                engine.enableEffect(this._effect);
+                mesh._bind(subMesh, this._effect, Material.TriangleFillMode);
+                var material = subMesh.getMaterial();
 
-                for (index = 0; index < opaqueSubMeshes.length; index++) {
-                    renderSubMesh(opaqueSubMeshes.data[index]);
+                this._effect.setFloat2("biasAndScale", this.bias, this.depthScale);
+
+                this._effect.setMatrix("viewProjection", this.getTransformMatrix());
+                this._effect.setVector3("lightPosition", this.getLight().position);
+                
+                this._effect.setFloat2("depthValues", this.getLight().getDepthMinZ(scene.activeCamera), this.getLight().getDepthMinZ(scene.activeCamera) + this.getLight().getDepthMaxZ(scene.activeCamera));
+
+                // Alpha test
+                if (material && material.needAlphaTesting()) {
+                    var alphaTexture = material.getAlphaTestTexture();
+                    this._effect.setTexture("diffuseSampler", alphaTexture);
+                    this._effect.setMatrix("diffuseMatrix", alphaTexture.getTextureMatrix());
                 }
 
-                for (index = 0; index < alphaTestSubMeshes.length; index++) {
-                    renderSubMesh(alphaTestSubMeshes.data[index]);
+                // Bones
+                if (mesh.useBones && mesh.computeBonesUsingShaders) {
+                    this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
                 }
 
-                if (this._transparencyShadow) {
-                    for (index = 0; index < transparentSubMeshes.length; index++) {
-                        renderSubMesh(transparentSubMeshes.data[index]);
-                    }
+                if (this.forceBackFacesOnly) {
+                    engine.setState(true, 0, false, true);
                 }
-            };
 
-            this._shadowMap.onClearObservable.add((engine: Engine) => {
-                if (this.useExponentialShadowMap || this.useBlurExponentialShadowMap) {
-                    engine.clear(new Color4(0, 0, 0, 0), true, true, true);
-                } else {
-                    engine.clear(new Color4(1.0, 1.0, 1.0, 1.0), true, true, true);
+                // Draw
+                mesh._processRendering(subMesh, this._effect, Material.TriangleFillMode, batch, hardwareInstancedRendering,
+                    (isInstance, world) => this._effect.setMatrix("world", world));
+
+                if (this.forceBackFacesOnly) {
+                    engine.setState(true, 0, false, false);
                 }
-            });
+            } else {
+                // Need to reset refresh rate of the shadowMap
+                this._shadowMap.resetRefreshCounter();
+            }
         }
 
         private _applyFilterValues(): void {
-            if (this.usePoissonSampling || this.useExponentialShadowMap || this.useBlurExponentialShadowMap) {
-                this._shadowMap.anisotropicFilteringLevel = 16;
-                this._shadowMap.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
-            } else {
+            if (this.filter === ShadowGenerator.FILTER_NONE) {
                 this._shadowMap.anisotropicFilteringLevel = 1;
                 this._shadowMap.updateSamplingMode(Texture.NEAREST_SAMPLINGMODE);
+            } else {
+                this._shadowMap.anisotropicFilteringLevel = 16;
+                this._shadowMap.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
             }
         }
 
-        public recreateShadowMap(): void {
-            // Track render list.
-            var renderList = this._shadowMap.renderList;
-            // Clean up existing data.
-            this._disposeRTTandPostProcesses();
-            // Reinitializes.
-            this._initializeGenerator(this.blurBoxOffset);
-            // Reaffect the blur ESM to ensure a correct fallback if necessary.
-            if (this.useBlurExponentialShadowMap) {
-                this.useBlurExponentialShadowMap = true;
-            }
-            // Reaffect the filter.
-            this._applyFilterValues();
-            // Reaffect Render List.
-            this._shadowMap.renderList = renderList;
-        }
-
         /**
          * Boolean : true when the ShadowGenerator is finally computed.  
          */
@@ -366,10 +493,6 @@
                 defines.push("#define ESM");
             }
 
-            if (this.getLight().needCube()) {
-                defines.push("#define CUBEMAP");
-            }
-
             var attribs = [VertexBuffer.PositionKind];
 
             var mesh = subMesh.getMesh();
@@ -415,7 +538,7 @@
                 attribs.push("world3");
             }
 
-            // Get correct effect      
+            // Get correct effect
             var join = defines.join("\n");
             if (this._cachedDefines !== join) {
                 this._cachedDefines = join;
@@ -429,28 +552,51 @@
         }
 
         /**
-         * Returns a RenderTargetTexture object : the shadow map texture.  
+         * This creates the defines related to the standard BJS materials.
          */
-        public getShadowMap(): RenderTargetTexture {
-            return this._shadowMap;
-        }
+        public prepareDefines(defines: MaterialDefines, lightIndex: number): void {
+            var scene = this._scene;
+            var light = this._light;
 
-        /**
-         * Returns the most ready computed shadow map as a RenderTargetTexture object.  
-         */
-        public getShadowMapForRendering(): RenderTargetTexture {
-            if (this._shadowMap2) {
-                return this._shadowMap2;
+            if (!scene.shadowsEnabled || !light.shadowEnabled) {
+                return;
             }
 
-            return this._shadowMap;
+            defines["SHADOW" + lightIndex] = true;
+
+            if (this.usePoissonSampling) {
+                defines["SHADOWPCF" + lightIndex] = true;
+            } 
+            else if (this.useExponentialShadowMap || this.useBlurExponentialShadowMap) {
+                defines["SHADOWESM" + lightIndex] = true;
+            }
+            else if (this.useCloseExponentialShadowMap || this.useBlurCloseExponentialShadowMap) {
+                defines["SHADOWCLOSEESM" + lightIndex] = true;
+            }
+
+            if (light.needCube()) {
+                defines["SHADOWCUBE" + lightIndex] = true;
+            }
         }
 
         /**
-         * Returns the associated light object.  
+         * This binds shadow lights related to the standard BJS materials.
+         * It implies the unifroms available on the materials are the standard BJS ones.
          */
-        public getLight(): IShadowLight {
-            return this._light;
+        public bindShadowLight(lightIndex: string, effect: Effect): void {
+            var light = this._light;
+            var scene = this._scene;
+
+            if (!scene.shadowsEnabled || !light.shadowEnabled) {
+                return;
+            }
+
+            if (!light.needCube()) {
+                effect.setMatrix("lightMatrix" + lightIndex, this.getTransformMatrix());
+            } 
+            effect.setTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering());
+            light._uniformBuffer.updateFloat3("shadowsInfo", this.getDarkness(), this.blurScale / this.getShadowMap().getSize().width, this.depthScale, lightIndex);
+            light._uniformBuffer.updateFloat2("depthValues", this.getLight().getDepthMinZ(scene.activeCamera), this.getLight().getDepthMinZ(scene.activeCamera) + this.getLight().getDepthMaxZ(scene.activeCamera), lightIndex);
         }
 
         // Methods
@@ -491,47 +637,22 @@
             return this._transformMatrix;
         }
 
-        /**
-         * Returns the darkness value (float).  
-         */
-        public getDarkness(): number {
-            return this._darkness;
-        }
-        /**
-         * Sets the ShadowGenerator darkness value (float <= 1.0).  
-         * Returns the ShadowGenerator.  
-         */
-        public setDarkness(darkness: number): ShadowGenerator {
-            if (darkness >= 1.0)
-                this._darkness = 1.0;
-            else if (darkness <= 0.0)
-                this._darkness = 0.0;
-            else
-                this._darkness = darkness;
-            return this;
-        }
-        /**
-         * Sets the ability to have transparent shadow (boolean).  
-         * Returns the ShadowGenerator.  
-         */
-        public setTransparencyShadow(hasShadow: boolean): ShadowGenerator {
-            this._transparencyShadow = hasShadow;
-            return this;
-        }
-
-        private _packHalf(depth: number): Vector2 {
-            var scale = depth * 255.0;
-            var fract = scale - Math.floor(scale);
-
-            return new Vector2(depth - fract / 255.0, fract);
+        public recreateShadowMap(): void {
+            // Track render list.
+            var renderList = this._shadowMap.renderList;
+            // Clean up existing data.
+            this._disposeRTTandPostProcesses();
+            // Reinitializes.
+            this._initializeGenerator();
+            // Reaffect the filter to ensure a correct fallback if necessary.
+            this.filter = this.filter;
+            // Reaffect the filter.
+            this._applyFilterValues();
+            // Reaffect Render List.
+            this._shadowMap.renderList = renderList;
         }
 
-        private _disposeRTTandPostProcesses(): void {
-            if (this._shadowMap) {
-                this._shadowMap.dispose();
-                this._shadowMap = null;
-            }
-
+        private _disposeBlurPostProcesses(): void {
             if (this._shadowMap2) {
                 this._shadowMap2.dispose();
                 this._shadowMap2 = null;
@@ -546,6 +667,27 @@
                 this._boxBlurPostprocess.dispose();
                 this._boxBlurPostprocess = null;
             }
+
+            if (this._kernelBlurXPostprocess) {
+                this._kernelBlurXPostprocess.dispose();
+                this._kernelBlurXPostprocess = null;
+            }
+
+            if (this._kernelBlurYPostprocess) {
+                this._kernelBlurYPostprocess.dispose();
+                this._kernelBlurYPostprocess = null;
+            }
+
+            this._blurPostProcesses = null;
+        }
+
+        private _disposeRTTandPostProcesses(): void {
+            if (this._shadowMap) {
+                this._shadowMap.dispose();
+                this._shadowMap = null;
+            }
+
+            this._disposeBlurPostProcesses();
         }
 
         /**
@@ -569,10 +711,17 @@
             serializationObject.mapSize = shadowMap.getRenderSize();
             serializationObject.useExponentialShadowMap = this.useExponentialShadowMap;
             serializationObject.useBlurExponentialShadowMap = this.useBlurExponentialShadowMap;
+            serializationObject.useCloseExponentialShadowMap = this.useBlurExponentialShadowMap;
+            serializationObject.useBlurCloseExponentialShadowMap = this.useBlurExponentialShadowMap;
             serializationObject.usePoissonSampling = this.usePoissonSampling;
             serializationObject.forceBackFacesOnly = this.forceBackFacesOnly;
             serializationObject.depthScale = this.depthScale;
             serializationObject.darkness = this.getDarkness();
+            serializationObject.blurBoxOffset = this.blurBoxOffset;
+            serializationObject.blurKernel = this.blurKernel;
+            serializationObject.blurScale = this.blurScale;
+            serializationObject.useKernelBlur = this.useKernelBlur;
+            serializationObject.transparencyShadow = this._transparencyShadow;
 
             serializationObject.renderList = [];
             for (var meshIndex = 0; meshIndex < shadowMap.renderList.length; meshIndex++) {
@@ -607,7 +756,14 @@
             }
             else if (parsedShadowGenerator.useBlurExponentialShadowMap) {
                 shadowGenerator.useBlurExponentialShadowMap = true;
-            }            
+            }
+            else if (parsedShadowGenerator.useCloseExponentialShadowMap) {
+                shadowGenerator.useCloseExponentialShadowMap = true;
+            }
+            else if (parsedShadowGenerator.useBlurExponentialShadowMap) {
+                shadowGenerator.useBlurCloseExponentialShadowMap = true;
+            }
+
             // Backward compat
             else if (parsedShadowGenerator.useVarianceShadowMap) {
                 shadowGenerator.useExponentialShadowMap = true;
@@ -627,69 +783,30 @@
             if (parsedShadowGenerator.blurBoxOffset) {
                 shadowGenerator.blurBoxOffset = parsedShadowGenerator.blurBoxOffset;
             }
-                
-            if (parsedShadowGenerator.bias !== undefined) {
-                shadowGenerator.bias = parsedShadowGenerator.bias;
-            }
 
-            if (parsedShadowGenerator.darkness) {
-                shadowGenerator.setDarkness(parsedShadowGenerator.darkness);
+            if (parsedShadowGenerator.useKernelBlur) {
+                shadowGenerator.useKernelBlur = parsedShadowGenerator.useKernelBlur;
             }
 
-            shadowGenerator.forceBackFacesOnly = parsedShadowGenerator.forceBackFacesOnly;
-
-            return shadowGenerator;
-        }
-
-        /**
-         * This creates the defines related to the standard BJS materials.
-         */
-        public prepareDefines(defines: MaterialDefines, lightIndex: number): void {
-            var scene = this._scene;
-            var light = this._light;
-
-            if (!scene.shadowsEnabled || !light.shadowEnabled) {
-                return;
+            if (parsedShadowGenerator.blurKernel) {
+                shadowGenerator.blurKernel = parsedShadowGenerator.blurKernel;
             }
 
-            defines["SHADOW" + lightIndex] = true;
-
-            if (this.usePoissonSampling) {
-                defines["SHADOWPCF" + lightIndex] = true;
-            } 
-            else if (this.useExponentialShadowMap || this.useBlurExponentialShadowMap) {
-                defines["SHADOWESM" + lightIndex] = true;
+            if (parsedShadowGenerator.bias !== undefined) {
+                shadowGenerator.bias = parsedShadowGenerator.bias;
             }
 
-            if (light.needCube()) {
-                defines["SHADOWCUBE" + lightIndex] = true;
+            if (parsedShadowGenerator.darkness) {
+                shadowGenerator.setDarkness(parsedShadowGenerator.darkness);
             }
-        }
-
-        /**
-         * This binds shadow lights related to the standard BJS materials.
-         * It implies the unifroms available on the materials are the standard BJS ones.
-         */
-        public bindShadowLight(lightIndex: string, effect: Effect, depthValuesAlreadySet: boolean): boolean {
-            var light = this._light;
-            var scene = this._scene;
 
-            if (!scene.shadowsEnabled || !light.shadowEnabled) {
-                return;
+            if (parsedShadowGenerator.transparencyShadow) {
+                shadowGenerator.setTransparencyShadow(true);
             }
 
-            if (!light.needCube()) {
-                effect.setMatrix("lightMatrix" + lightIndex, this.getTransformMatrix());
-            } else {
-                if (!depthValuesAlreadySet) {
-                    depthValuesAlreadySet = true;
-                    effect.setFloat2("depthValues", scene.activeCamera.minZ, scene.activeCamera.maxZ);
-                }
-            }
-            effect.setTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering());
-            light._uniformBuffer.updateFloat3("shadowsInfo", this.getDarkness(), this.blurScale / this.getShadowMap().getSize().width, this.depthScale, lightIndex);
+            shadowGenerator.forceBackFacesOnly = parsedShadowGenerator.forceBackFacesOnly;
 
-            return depthValuesAlreadySet;
+            return shadowGenerator;
         }
     }
 } 

+ 17 - 0
src/Lights/babylon.directionalLight.ts

@@ -148,6 +148,7 @@ module BABYLON {
              this._uniformBuffer.addUniform("vLightDiffuse", 4);
              this._uniformBuffer.addUniform("vLightSpecular", 3);
              this._uniformBuffer.addUniform("shadowsInfo", 3);
+             this._uniformBuffer.addUniform("depthValues", 2);
              this._uniformBuffer.create();
         }
 
@@ -163,5 +164,21 @@ module BABYLON {
             this._uniformBuffer.updateFloat4("vLightData", this.direction.x, this.direction.y, this.direction.z, 1, lightIndex);
             return this;
         }
+
+        /**
+         * Gets the minZ used for shadow according to both the scene and the light.
+         * @param activeCamera 
+         */
+        public getDepthMinZ(activeCamera: Camera): number {
+            return 0.5;
+        }
+
+        /**
+         * Gets the maxZ used for shadow according to both the scene and the light.
+         * @param activeCamera 
+         */
+        public getDepthMaxZ(activeCamera: Camera): number {
+             return 1.5;
+        }
     }
 }  

+ 1 - 0
src/Lights/babylon.hemisphericLight.ts

@@ -25,6 +25,7 @@
             this._uniformBuffer.addUniform("vLightSpecular", 3);
             this._uniformBuffer.addUniform("vLightGround", 3);
             this._uniformBuffer.addUniform("shadowsInfo", 3);
+            this._uniformBuffer.addUniform("depthValues", 2);
             this._uniformBuffer.create();
         }
 

+ 2 - 1
src/Lights/babylon.pointLight.ts

@@ -111,7 +111,7 @@
         protected _setDefaultShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array<AbstractMesh>): void {
             var activeCamera = this.getScene().activeCamera;
             Matrix.PerspectiveFovLHToRef(this.shadowAngle, 1.0, 
-            this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera.minZ, this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera.maxZ, matrix);
+            this.getDepthMinZ(activeCamera), this.getDepthMaxZ(activeCamera), matrix);
         }
 
         protected _buildUniformLayout(): void {
@@ -119,6 +119,7 @@
             this._uniformBuffer.addUniform("vLightDiffuse", 4);
             this._uniformBuffer.addUniform("vLightSpecular", 3);
             this._uniformBuffer.addUniform("shadowsInfo", 3);
+            this._uniformBuffer.addUniform("depthValues", 2);
             this._uniformBuffer.create();
         }
 

+ 29 - 1
src/Lights/babylon.shadowLight.ts

@@ -21,6 +21,18 @@
         forceProjectionMatrixCompute(): void;
 
         getShadowDirection(faceIndex?: number): Vector3;
+
+        /**
+         * Gets the minZ used for shadow according to both the scene and the light.
+         * @param activeCamera 
+         */
+         getDepthMinZ(activeCamera: Camera): number;
+
+        /**
+         * Gets the minZ used for shadow according to both the scene and the light.
+         * @param activeCamera 
+         */
+        getDepthMaxZ(activeCamera: Camera): number;
     }
 
     export abstract class ShadowLight extends Light implements IShadowLight {
@@ -94,7 +106,7 @@
          * Return the depth scale used for the shadow map.
          */
         public getDepthScale(): number {
-            return 30.0;
+            return 50.0;
         }
 
         /**
@@ -165,6 +177,22 @@
         }
 
         /**
+         * Gets the minZ used for shadow according to both the scene and the light.
+         * @param activeCamera 
+         */
+        public getDepthMinZ(activeCamera: Camera): number {
+            return this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera.minZ;
+        }
+
+        /**
+         * Gets the maxZ used for shadow according to both the scene and the light.
+         * @param activeCamera 
+         */
+        public getDepthMaxZ(activeCamera: Camera): number {
+             return this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera.maxZ;
+        }
+
+        /**
          * Sets the projection matrix according to the type of light and custom projection matrix definition.
          * Returns the light.
          */

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

@@ -73,7 +73,7 @@
             var angle = this._shadowAngleScale * this._angle;
             
             Matrix.PerspectiveFovLHToRef(angle, 1.0, 
-            this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera.minZ, this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera.maxZ, matrix);
+            this.getDepthMinZ(activeCamera), this.getDepthMaxZ(activeCamera), matrix);
         }
 
         protected _buildUniformLayout(): void {
@@ -82,6 +82,7 @@
             this._uniformBuffer.addUniform("vLightSpecular", 3);
             this._uniformBuffer.addUniform("vLightDirection", 3);
             this._uniformBuffer.addUniform("shadowsInfo", 3);
+            this._uniformBuffer.addUniform("depthValues", 2);
             this._uniformBuffer.create();
         }
 

+ 1 - 3
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -479,7 +479,6 @@
 
         public static BindLights(scene: Scene, mesh: AbstractMesh, effect: Effect, defines: MaterialDefines, useScalarInLinearSpace: boolean, maxSimultaneousLights: number, usePhysicalLightFalloff: boolean) {
             var lightIndex = 0;
-            var depthValuesAlreadySet = false;
             for (var light of mesh._lightSources) {
                 let useUbo = light._uniformBuffer.useUbo;
                 let scaledIntensity = light.getScaledIntensity();
@@ -502,7 +501,7 @@
 
                 // Shadows
                 if (scene.shadowsEnabled) {
-                    depthValuesAlreadySet = MaterialHelper.BindLightShadow(light, scene, mesh, lightIndex + "", effect, depthValuesAlreadySet);
+                    MaterialHelper.BindLightShadow(light, scene, mesh, lightIndex + "", effect);
                 }
 
                 light._uniformBuffer.update();
@@ -931,7 +930,6 @@
                         "vAlbedoInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vEmissiveInfos", "vReflectivityInfos", "vMicroSurfaceSamplerInfos", "vBumpInfos", "vLightmapInfos", "vRefractionInfos",
                         "mBones",
                         "vClipPlane", "albedoMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "reflectivityMatrix", "microSurfaceSamplerMatrix", "bumpMatrix", "lightmapMatrix", "refractionMatrix",
-                        "depthValues",
                         "opacityParts", "emissiveLeftColor", "emissiveRightColor",
                         "vLightingIntensity",
                         "logarithmicDepthConstant",

+ 5 - 8
src/Materials/babylon.materialHelper.ts

@@ -216,7 +216,8 @@
                     "vLightDirection" + lightIndex,
                     "vLightGround" + lightIndex,
                     "lightMatrix" + lightIndex,
-                    "shadowsInfo" + lightIndex
+                    "shadowsInfo" + lightIndex,
+                    "depthValues" + lightIndex,
                 );
 
                 if (uniformBuffersList) {
@@ -308,15 +309,13 @@
         }
 
         // Bindings
-        public static BindLightShadow(light: Light, scene: Scene, mesh: AbstractMesh, lightIndex: string, effect: Effect, depthValuesAlreadySet: boolean): boolean {
+        public static BindLightShadow(light: Light, scene: Scene, mesh: AbstractMesh, lightIndex: string, effect: Effect): void {
             if (light.shadowEnabled && mesh.receiveShadows) {
                 var shadowGenerator = light.getShadowGenerator();
                 if (shadowGenerator) {
-                    depthValuesAlreadySet = shadowGenerator.bindShadowLight(lightIndex, effect, depthValuesAlreadySet);
+                    shadowGenerator.bindShadowLight(lightIndex, effect);
                 }
             }
-
-            return depthValuesAlreadySet;
         }
 
         public static BindLightProperties(light: Light, effect: Effect, lightIndex: number): void {
@@ -325,8 +324,6 @@
 
         public static BindLights(scene: Scene, mesh: AbstractMesh, effect: Effect, defines: MaterialDefines, maxSimultaneousLights = 4) {
             var lightIndex = 0;
-            var depthValuesAlreadySet = false;
-
             for (var light of mesh._lightSources) {
                 light._uniformBuffer.bindToEffect(effect, "Light" + lightIndex);
 
@@ -341,7 +338,7 @@
 
                 // Shadows
                 if (scene.shadowsEnabled) {
-                    depthValuesAlreadySet = this.BindLightShadow(light, scene, mesh, lightIndex + "", effect, depthValuesAlreadySet);
+                    this.BindLightShadow(light, scene, mesh, lightIndex + "", effect);
                 }
                 light._uniformBuffer.update();
                 lightIndex++;

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

@@ -701,7 +701,6 @@ module BABYLON {
                     "vDiffuseInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vEmissiveInfos", "vSpecularInfos", "vBumpInfos", "vLightmapInfos", "vRefractionInfos",
                     "mBones",
                     "vClipPlane", "diffuseMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "specularMatrix", "bumpMatrix", "lightmapMatrix", "refractionMatrix",
-                    "depthValues",
                     "diffuseLeftColor", "diffuseRightColor", "opacityParts", "reflectionLeftColor", "reflectionRightColor", "emissiveLeftColor", "emissiveRightColor", "refractionLeftColor", "refractionRightColor",
                     "logarithmicDepthConstant"
                 ];

+ 5 - 4
src/Materials/babylon.uniformBuffer.ts

@@ -46,8 +46,9 @@ module BABYLON {
          * @param {string} name Name of the uniform, as used in the uniform block in the shader.
          * @param {number} x
          * @param {number} y
+         * @param {string} [suffix] Suffix to add to the uniform name.
          */
-        public updateFloat2: (name: string, x: number, y: number) => void;
+        public updateFloat2: (name: string, x: number, y: number, suffix?: string) => void;
 
         /**
          * Wrapper for updateUniform.
@@ -484,11 +485,11 @@ module BABYLON {
             this.updateUniform(name, UniformBuffer._tempBuffer, 1);
         }
 
-        private _updateFloat2ForEffect(name: string, x: number, y: number) {
-            this._currentEffect.setFloat2(name, x, y);
+        private _updateFloat2ForEffect(name: string, x: number, y: number, suffix = "") {
+            this._currentEffect.setFloat2(name + suffix, x, y);
         }
 
-        private _updateFloat2ForUniform(name: string, x: number, y: number) {
+        private _updateFloat2ForUniform(name: string, x: number, y: number, suffix = "") {
             UniformBuffer._tempBuffer[0] = x;
             UniformBuffer._tempBuffer[1] = y;
             this.updateUniform(name, UniformBuffer._tempBuffer, 2);

+ 19 - 11
src/Shaders/ShadersInclude/lightFragment.fx

@@ -25,24 +25,32 @@
 		#endif
     #endif
 	#ifdef SHADOW{X}
-		#ifdef SHADOWESM{X}
+		#ifdef SHADOWCLOSEESM{X}
 			#if defined(SHADOWCUBE{X})
-				shadow = computeShadowWithESMCube(light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z);
+				shadow = computeShadowWithCloseESMCube(light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.depthValues);
 			#else
-				shadow = computeShadowWithESM(vPositionFromLight{X}, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z);
+				shadow = computeShadowWithCloseESM(vPositionFromLight{X}, vDepthMetric{X}, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z);
 			#endif
-		#else	
-			#ifdef SHADOWPCF{X}
+		#else
+			#ifdef SHADOWESM{X}
 				#if defined(SHADOWCUBE{X})
-					shadow = computeShadowWithPCFCube(light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.x);
+					shadow = computeShadowWithESMCube(light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.depthValues);
 				#else
-					shadow = computeShadowWithPCF(vPositionFromLight{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.x);
+					shadow = computeShadowWithESM(vPositionFromLight{X}, vDepthMetric{X}, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z);
 				#endif
-			#else
-				#if defined(SHADOWCUBE{X})
-					shadow = computeShadowCube(light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.x);
+			#else	
+				#ifdef SHADOWPCF{X}
+					#if defined(SHADOWCUBE{X})
+						shadow = computeShadowWithPCFCube(light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.x, light{X}.depthValues);
+					#else
+						shadow = computeShadowWithPCF(vPositionFromLight{X}, vDepthMetric{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.x);
+					#endif
 				#else
-					shadow = computeShadow(vPositionFromLight{X}, shadowSampler{X}, light{X}.shadowsInfo.x);
+					#if defined(SHADOWCUBE{X})
+						shadow = computeShadowCube(light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.depthValues);
+					#else
+						shadow = computeShadow(vPositionFromLight{X}, vDepthMetric{X}, shadowSampler{X}, light{X}.shadowsInfo.x);
+					#endif
 				#endif
 			#endif
 		#endif

+ 4 - 0
src/Shaders/ShadersInclude/lightFragmentDeclaration.fx

@@ -11,9 +11,13 @@
 			uniform samplerCube shadowSampler{X};
 		#else
 			varying vec4 vPositionFromLight{X};
+			varying float vDepthMetric{X};
+
 			uniform sampler2D shadowSampler{X};
+			uniform mat4 lightMatrix{X};
 		#endif
 		uniform vec3 shadowsInfo{X};
+		uniform vec2 depthValues{X};
 	#endif
 	#ifdef SPOTLIGHT{X}
 		uniform vec4 vLightDirection{X};

+ 4 - 0
src/Shaders/ShadersInclude/lightUboDeclaration.fx

@@ -11,6 +11,7 @@
 			vec3 vLightGround;
 		#endif
 		vec3 shadowsInfo;
+		vec2 depthValues;
 	} light{X};
 
 #ifdef SHADOW{X}
@@ -18,7 +19,10 @@
 		uniform samplerCube shadowSampler{X};
 	#else
 		varying vec4 vPositionFromLight{X};
+		varying float vDepthMetric{X};
+
 		uniform sampler2D shadowSampler{X};
+		uniform mat4 lightMatrix{X};
 	#endif
 #endif
 

+ 90 - 32
src/Shaders/ShadersInclude/shadowsFragmentFunctions.fx

@@ -7,13 +7,12 @@
 		}
 	#endif
 
-	uniform vec2 depthValues;
-
-	float computeShadowCube(vec3 lightPosition, samplerCube shadowSampler, float darkness)
+	float computeShadowCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, vec2 depthValues)
 	{
 		vec3 directionToLight = vPositionW - lightPosition;
 		float depth = length(directionToLight);
-		depth = (depth - depthValues.x) / (depthValues.y - depthValues.x);
+		//depth = (depth - depthValues.x) / (depthValues.y - depthValues.x);
+		depth = (depth + depthValues.x) / (depthValues.y);
 		depth = clamp(depth, 0., 1.0);
 
 		directionToLight = normalize(directionToLight);
@@ -32,12 +31,12 @@
 		return 1.0;
 	}
 
-	float computeShadowWithPCFCube(vec3 lightPosition, samplerCube shadowSampler, float mapSize, float darkness)
+	float computeShadowWithPCFCube(vec3 lightPosition, samplerCube shadowSampler, float mapSize, float darkness, vec2 depthValues)
 	{
 		vec3 directionToLight = vPositionW - lightPosition;
 		float depth = length(directionToLight);
-
-		depth = (depth - depthValues.x) / (depthValues.y - depthValues.x);
+		//depth = (depth - depthValues.x) / (depthValues.y - depthValues.x);
+		depth = (depth + depthValues.x) / (depthValues.y);
 		depth = clamp(depth, 0., 1.0);
 
 		directionToLight = normalize(directionToLight);
@@ -68,11 +67,12 @@
 		return  min(1.0, visibility + darkness);
 	}
 
-	float computeShadowWithESMCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, float depthScale)
+	float computeShadowWithESMCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, float depthScale, vec2 depthValues)
 	{
 		vec3 directionToLight = vPositionW - lightPosition;
 		float depth = length(directionToLight);
-		depth = (depth - depthValues.x) / (depthValues.y - depthValues.x);
+		//depth = (depth - depthValues.x) / (depthValues.y - depthValues.x);
+		depth = (depth + depthValues.x) / (depthValues.y);
 		float shadowPixelDepth = clamp(depth, 0., 1.0);
 
 		directionToLight = normalize(directionToLight);
@@ -88,41 +88,67 @@
 		return esm;
 	}
 
-	float computeShadow(vec4 vPositionFromLight, sampler2D shadowSampler, float darkness)
+	float computeShadowWithCloseESMCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, float depthScale, vec2 depthValues)
+	{
+		vec3 directionToLight = vPositionW - lightPosition;
+		float depth = length(directionToLight);
+		//depth = (depth - depthValues.x) / (depthValues.y - depthValues.x);
+		depth = (depth + depthValues.x) / (depthValues.y);
+		float shadowPixelDepth = clamp(depth, 0., 1.0);
+
+		directionToLight = normalize(directionToLight);
+		directionToLight.y = -directionToLight.y;
+		
+		#ifndef SHADOWFLOAT
+			float shadowMapSample = unpack(textureCube(shadowSampler, directionToLight));
+		#else
+			float shadowMapSample = textureCube(shadowSampler, directionToLight).x;
+		#endif
+
+		float esm = clamp(exp(min(87., -depthScale * (shadowPixelDepth - shadowMapSample))) - darkness, 0., 1.);
+
+		return esm;
+	}
+
+	float computeShadow(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float darkness)
 	{
-		vec3 depth = vPositionFromLight.xyz / vPositionFromLight.w;
-		depth = 0.5 * depth + vec3(0.5);
-		vec2 uv = depth.xy;
+		vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
+		clipSpace = 0.5 * clipSpace + vec3(0.5);
+		vec2 uv = clipSpace.xy;
 
 		if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0)
 		{
 			return 1.0;
 		}
 
+		float shadowPixelDepth = clamp(depthMetric, 0., 1.0);
+
 		#ifndef SHADOWFLOAT
 			float shadow = unpack(texture2D(shadowSampler, uv));
 		#else
 			float shadow = texture2D(shadowSampler, uv).x;
 		#endif
 
-		if (depth.z > shadow)
+		if (shadowPixelDepth > shadow)
 		{
 			return darkness;
 		}
 		return 1.;
 	}
 
-	float computeShadowWithPCF(vec4 vPositionFromLight, sampler2D shadowSampler, float mapSize, float darkness)
+	float computeShadowWithPCF(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float mapSize, float darkness)
 	{
-		vec3 depth = vPositionFromLight.xyz / vPositionFromLight.w;
-		depth = 0.5 * depth + vec3(0.5);
-		vec2 uv = depth.xy;
+		vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
+		clipSpace = 0.5 * clipSpace + vec3(0.5);
+		vec2 uv = clipSpace.xy;
 
 		if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0)
 		{
 			return 1.0;
 		}
 
+		float shadowPixelDepth = clamp(depthMetric, 0., 1.0);
+
 		float visibility = 1.;
 
 		vec2 poissonDisk[4];
@@ -134,39 +160,71 @@
 		// Poisson Sampling
 
 		#ifndef SHADOWFLOAT
-			if (unpack(texture2D(shadowSampler, uv + poissonDisk[0] * mapSize)) < depth.z) visibility -= 0.25;
-			if (unpack(texture2D(shadowSampler, uv + poissonDisk[1] * mapSize)) < depth.z) visibility -= 0.25;
-			if (unpack(texture2D(shadowSampler, uv + poissonDisk[2] * mapSize)) < depth.z) visibility -= 0.25;
-			if (unpack(texture2D(shadowSampler, uv + poissonDisk[3] * mapSize)) < depth.z) visibility -= 0.25;
+			if (unpack(texture2D(shadowSampler, uv + poissonDisk[0] * mapSize)) < shadowPixelDepth) visibility -= 0.25;
+			if (unpack(texture2D(shadowSampler, uv + poissonDisk[1] * mapSize)) < shadowPixelDepth) visibility -= 0.25;
+			if (unpack(texture2D(shadowSampler, uv + poissonDisk[2] * mapSize)) < shadowPixelDepth) visibility -= 0.25;
+			if (unpack(texture2D(shadowSampler, uv + poissonDisk[3] * mapSize)) < shadowPixelDepth) visibility -= 0.25;
 		#else
-			if (texture2D(shadowSampler, uv + poissonDisk[0] * mapSize).x < depth.z) visibility -= 0.25;
-			if (texture2D(shadowSampler, uv + poissonDisk[1] * mapSize).x < depth.z) visibility -= 0.25;
-			if (texture2D(shadowSampler, uv + poissonDisk[2] * mapSize).x < depth.z) visibility -= 0.25;
-			if (texture2D(shadowSampler, uv + poissonDisk[3] * mapSize).x < depth.z) visibility -= 0.25;
+			if (texture2D(shadowSampler, uv + poissonDisk[0] * mapSize).x < shadowPixelDepth) visibility -= 0.25;
+			if (texture2D(shadowSampler, uv + poissonDisk[1] * mapSize).x < shadowPixelDepth) visibility -= 0.25;
+			if (texture2D(shadowSampler, uv + poissonDisk[2] * mapSize).x < shadowPixelDepth) visibility -= 0.25;
+			if (texture2D(shadowSampler, uv + poissonDisk[3] * mapSize).x < shadowPixelDepth) visibility -= 0.25;
 		#endif
 
 		return  min(1.0, visibility + darkness);
 	}
 
-	float computeShadowWithESM(vec4 vPositionFromLight, sampler2D shadowSampler, float darkness, float depthScale)
+	float computeShadowWithESM(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float darkness, float depthScale)
+	{
+		vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
+		clipSpace = 0.5 * clipSpace + vec3(0.5);
+		vec2 uv = clipSpace.xy;
+
+		if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0)
+		{
+			return 1.0;
+		}
+
+		float shadowPixelDepth = clamp(depthMetric, 0., 1.0);
+
+		#ifndef SHADOWFLOAT
+			float shadowMapSample = unpack(texture2D(shadowSampler, uv));
+		#else
+			float shadowMapSample = texture2D(shadowSampler, uv).x;
+		#endif
+		
+		float esm = 1.0 - clamp(exp(min(87., depthScale * shadowPixelDepth)) * shadowMapSample - darkness, 0., 1.);
+
+		// Apply fade out at frustum edge
+		// const float fadeDistance = 0.07;
+		// vec2 cs2 = clipSpace.xy * clipSpace.xy; //squarish falloff
+		// float mask = smoothstep(1.0, 1.0 - fadeDistance, dot(cs2, cs2));
+
+		// esm = mix(1.0, esm, mask);
+
+		return esm;
+	}
+
+	float computeShadowWithCloseESM(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float darkness, float depthScale)
 	{
 		vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
-		vec3 depth = 0.5 * clipSpace + vec3(0.5);
-		vec2 uv = depth.xy;
-		float shadowPixelDepth = depth.z;
+		clipSpace = 0.5 * clipSpace + vec3(0.5);
+		vec2 uv = clipSpace.xy;
 
 		if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0)
 		{
 			return 1.0;
 		}
-	
+
+		float shadowPixelDepth = clamp(depthMetric, 0., 1.0);		
+		
 		#ifndef SHADOWFLOAT
 			float shadowMapSample = unpack(texture2D(shadowSampler, uv));
 		#else
 			float shadowMapSample = texture2D(shadowSampler, uv).x;
 		#endif
 		
-		float esm = 1.0 - clamp(exp(min(87., depthScale * shadowPixelDepth)) * shadowMapSample - darkness, 0., 1.);		
+		float esm = clamp(exp(min(87., -depthScale * (shadowPixelDepth - shadowMapSample))) - darkness, 0., 1.);
 
 		// Apply fade out at frustum edge
 		// const float fadeDistance = 0.07;

+ 2 - 1
src/Shaders/ShadersInclude/shadowsVertex.fx

@@ -1,5 +1,6 @@
 #ifdef SHADOWS
-	#if !defined(SHADOWCUBE{X})
+	#if defined(SHADOW{X}) && !defined(SHADOWCUBE{X})
 		vPositionFromLight{X} = lightMatrix{X} * worldPos;
+		vDepthMetric{X} =  ((vPositionFromLight{X}.z + light{X}.depthValues.x) / (light{X}.depthValues.y));
 	#endif
 #endif

+ 0 - 6
src/Shaders/ShadersInclude/shadowsVertexDeclaration.fx

@@ -1,6 +0,0 @@
-#ifdef SHADOWS
-	#if !defined(SHADOWCUBE{X})
-		uniform mat4 lightMatrix{X};
-		varying vec4 vPositionFromLight{X};
-	#endif
-#endif

+ 1 - 2
src/Shaders/default.vertex.fx

@@ -1,5 +1,4 @@
 #include<__decl__defaultVertex>
-
 // Attributes
 attribute vec3 position;
 #ifdef NORMAL
@@ -66,7 +65,7 @@ varying vec4 vColor;
 #include<clipPlaneVertexDeclaration>
 
 #include<fogVertexDeclaration>
-#include<shadowsVertexDeclaration>[0..maxSimultaneousLights]
+#include<__decl__lightFragment>[0..maxSimultaneousLights]
 
 #include<morphTargetsVertexGlobalDeclaration>
 #include<morphTargetsVertexDeclaration>[0..maxSimultaneousMorphTargets]

+ 1 - 1
src/Shaders/pbr.vertex.fx

@@ -70,7 +70,7 @@ varying vec4 vColor;
 #include<bumpVertexDeclaration>
 #include<clipPlaneVertexDeclaration>
 #include<fogVertexDeclaration>
-#include<shadowsVertexDeclaration>[0..maxSimultaneousLights]
+#include<__decl__lightFragment>[0..maxSimultaneousLights]
 
 #include<morphTargetsVertexGlobalDeclaration>
 #include<morphTargetsVertexDeclaration>[0..maxSimultaneousMorphTargets]

+ 4 - 19
src/Shaders/shadowMap.fragment.fx

@@ -11,19 +11,15 @@ vec4 pack(float depth)
 }
 #endif
 
-varying vec4 vPosition;
+varying float vDepthMetric;
 
 #ifdef ALPHATEST
 varying vec2 vUV;
 uniform sampler2D diffuseSampler;
 #endif
 
-#ifdef CUBEMAP
-uniform vec3 lightPosition;
-uniform vec2 depthValues;
-#endif
-
 uniform vec2 biasAndScale;
+uniform vec2 depthValues;
 
 void main(void)
 {
@@ -32,21 +28,10 @@ void main(void)
 		discard;
 #endif
 
-#ifdef CUBEMAP
-	vec3 directionToLight = vPosition.xyz - lightPosition;
-	
-	float depth = length(directionToLight);
-	depth = (depth - depthValues.x) / (depthValues.y - depthValues.x);
-	depth = clamp(depth, 0., 1.0);
-#else
-	float depth = vPosition.z / vPosition.w;
-	depth = depth * 0.5 + 0.5;	
-#endif
-
-	depth += biasAndScale.x;
+	float depth = vDepthMetric;
 
 #ifdef ESM
-	depth = exp(-min(87., biasAndScale.y * depth));
+	depth = clamp(exp(-min(87., biasAndScale.y * depth)), 0., 1.);
 #endif
 
 #ifdef FLOAT

+ 12 - 14
src/Shaders/shadowMap.vertex.fx

@@ -7,8 +7,10 @@ attribute vec3 position;
 #include<instancesDeclaration>
 
 uniform mat4 viewProjection;
+uniform vec2 biasAndScale;
+uniform vec2 depthValues;
 
-varying vec4 vPosition;
+varying float vDepthMetric;
 
 #ifdef ALPHATEST
 varying vec2 vUV;
@@ -26,20 +28,16 @@ void main(void)
 #include<instancesVertex>
 #include<bonesVertex>
 
-#ifdef CUBEMAP
-	vPosition = finalWorld * vec4(position, 1.0);
-	gl_Position = viewProjection * finalWorld * vec4(position, 1.0);
-#else
-	vPosition = viewProjection * finalWorld * vec4(position, 1.0);
-	gl_Position = vPosition;
-#endif
+vec4 worldPos = finalWorld * vec4(position, 1.0);
+gl_Position = viewProjection * worldPos;
+vDepthMetric = ((gl_Position.z + depthValues.x) / (depthValues.y)) + biasAndScale.x;
 
 #ifdef ALPHATEST
-#ifdef UV1
-	vUV = vec2(diffuseMatrix * vec4(uv, 1.0, 0.0));
-#endif
-#ifdef UV2
-	vUV = vec2(diffuseMatrix * vec4(uv2, 1.0, 0.0));
-#endif
+	#ifdef UV1
+		vUV = vec2(diffuseMatrix * vec4(uv, 1.0, 0.0));
+	#endif
+	#ifdef UV2
+		vUV = vec2(diffuseMatrix * vec4(uv2, 1.0, 0.0));
+	#endif
 #endif
 }

+ 0 - 9
src/babylon.engine.ts

@@ -2825,14 +2825,6 @@
             var framebuffer = gl.createFramebuffer();
             this.bindUnboundFramebuffer(framebuffer);
 
-            var colorRenderbuffer = gl.createRenderbuffer();
-            gl.bindRenderbuffer(gl.RENDERBUFFER, colorRenderbuffer);
-            gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, width, height);
-            gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
-            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorRenderbuffer);
-            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.RENDERBUFFER, colorRenderbuffer);
-            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, colorRenderbuffer);
-
             var width = size.width || size;
             var height = size.height || size;
             
@@ -2877,7 +2869,6 @@
             
                 gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, attachment, gl.TEXTURE_2D, texture, 0);
 
-
                 if (generateMipMaps) {
                     this._gl.generateMipmap(this._gl.TEXTURE_2D);
                 }