Przeglądaj źródła

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

David Catuhe 5 lat temu
rodzic
commit
446c89f95a

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

@@ -16,6 +16,7 @@
 - Added support for Offscreen canvas [Doc](https://doc.babylonjs.com/how_to/using_offscreen_canvas) ([Deltakosh](https://github.com/deltakosh/)
 - Added support for multiple canvases with one engine [Doc](https://doc.babylonjs.com/how_to/multi_canvases) ([Deltakosh](https://github.com/deltakosh/)
 - Added useReverseDepthBuffer to Engine which can provide greater z depth for distant objects without the cost of a logarithmic depth buffer ([BenAdams](https://github.com/benaadams/))
+- Added the "Cascaded Shadow Mapping" (CSM) shadow rendering technic ([Popov72](https://github.com/Popov72) (initiated by [lockphase](https://github.com/lockphase/)))
 
 ## Updates
 

+ 0 - 1
src/Engines/Extensions/engine.multiRender.ts

@@ -243,7 +243,6 @@ ThinEngine.prototype.createMultipleRenderTarget = function(size: any, options: I
     }
 
     gl.drawBuffers(attachments);
-    gl.bindRenderbuffer(gl.RENDERBUFFER, null);
     this._bindUnboundFramebuffer(null);
 
     this.resetTextureCache();

+ 61 - 53
src/Engines/Extensions/engine.renderTarget.ts

@@ -13,7 +13,7 @@ declare module "../../Engines/thinEngine" {
          * @param options defines the options used to create the texture
          * @returns a new render target texture stored in an InternalTexture
          */
-        createRenderTargetTexture(size: number | { width: number, height: number }, options: boolean | RenderTargetCreationOptions): InternalTexture;
+        createRenderTargetTexture(size: number | { width: number, height: number, layers?: number }, options: boolean | RenderTargetCreationOptions): InternalTexture;
 
         /**
          * Creates a depth stencil texture.
@@ -22,16 +22,15 @@ declare module "../../Engines/thinEngine" {
          * @param options The options defining the texture.
          * @returns The texture
          */
-        createDepthStencilTexture(size: number | { width: number, height: number }, options: DepthTextureCreationOptions): InternalTexture;
+        createDepthStencilTexture(size: number | { width: number, height: number, layers?: number }, options: DepthTextureCreationOptions): InternalTexture;
 
         /** @hidden */
-        _createDepthStencilTexture(size: number | { width: number, height: number }, options: DepthTextureCreationOptions): InternalTexture;
+        _createDepthStencilTexture(size: number | { width: number, height: number, layers?: number }, options: DepthTextureCreationOptions): InternalTexture;
     }
 }
 
-ThinEngine.prototype.createRenderTargetTexture = function(this: ThinEngine, size: number | { width: number, height: number }, options: boolean | RenderTargetCreationOptions): InternalTexture {
-    let fullOptions = new RenderTargetCreationOptions();
-
+ThinEngine.prototype.createRenderTargetTexture = function(this: ThinEngine, size: number | { width: number, height: number, layers?: number }, options: boolean | RenderTargetCreationOptions): InternalTexture {
+    const fullOptions = new RenderTargetCreationOptions();
     if (options !== undefined && typeof options === "object") {
         fullOptions.generateMipMaps = options.generateMipMaps;
         fullOptions.generateDepthBuffer = !!options.generateDepthBuffer;
@@ -56,50 +55,63 @@ ThinEngine.prototype.createRenderTargetTexture = function(this: ThinEngine, size
         // if floating point linear (HALF_FLOAT) then force to NEAREST_SAMPLINGMODE
         fullOptions.samplingMode = Constants.TEXTURE_NEAREST_SAMPLINGMODE;
     }
-    var gl = this._gl;
-
-    var texture = new InternalTexture(this, InternalTextureSource.RenderTarget);
-    this._bindTextureDirectly(gl.TEXTURE_2D, texture, true);
-
-    var width = (<{ width: number, height: number }>size).width || <number>size;
-    var height = (<{ width: number, height: number }>size).height || <number>size;
-
-    var filters = this._getSamplingParameters(fullOptions.samplingMode, fullOptions.generateMipMaps ? true : false);
-
     if (fullOptions.type === Constants.TEXTURETYPE_FLOAT && !this._caps.textureFloat) {
         fullOptions.type = Constants.TEXTURETYPE_UNSIGNED_INT;
         Logger.Warn("Float textures are not supported. Render target forced to TEXTURETYPE_UNSIGNED_BYTE type");
     }
 
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filters.mag);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filters.min);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+    const gl = this._gl;
+    const texture = new InternalTexture(this, InternalTextureSource.RenderTarget);
+    const width = (<{ width: number, height: number, layers?: number }>size).width || <number>size;
+    const height = (<{ width: number, height: number, layers?: number }>size).height || <number>size;
+    const layers = (<{ width: number, height: number, layers?: number }>size).layers || 0;
+    const filters = this._getSamplingParameters(fullOptions.samplingMode, fullOptions.generateMipMaps ? true : false);
+    const target = layers !== 0 ? gl.TEXTURE_2D_ARRAY : gl.TEXTURE_2D;
+    const sizedFormat = this._getRGBABufferInternalSizedFormat(fullOptions.type, fullOptions.format);
+    const internalFormat = this._getInternalFormat(fullOptions.format);
+    const type = this._getWebGLTextureType(fullOptions.type);
+
+    // Bind
+    this._bindTextureDirectly(target, texture);
+
+    if (layers !== 0) {
+        texture.is2DArray = true;
+        gl.texImage3D(target, 0, sizedFormat, width, height, layers, 0, internalFormat, type, null);
+    }
+    else {
+        gl.texImage2D(target, 0, sizedFormat, width, height, 0, internalFormat, type, null);
+    }
+
+    gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, filters.mag);
+    gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, filters.min);
+    gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+    gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
 
-    gl.texImage2D(gl.TEXTURE_2D, 0, this._getRGBABufferInternalSizedFormat(fullOptions.type, fullOptions.format), width, height, 0, this._getInternalFormat(fullOptions.format), this._getWebGLTextureType(fullOptions.type), null);
+    // MipMaps
+    if (fullOptions.generateMipMaps) {
+        this._gl.generateMipmap(target);
+    }
+
+    this._bindTextureDirectly(target, null);
 
     // Create the framebuffer
-    var currentFrameBuffer = this._currentFramebuffer;
-    var framebuffer = gl.createFramebuffer();
+    const framebuffer = gl.createFramebuffer();
     this._bindUnboundFramebuffer(framebuffer);
-    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture._webGLTexture, 0);
-
     texture._depthStencilBuffer = this._setupFramebufferDepthAttachments(fullOptions.generateStencilBuffer ? true : false, fullOptions.generateDepthBuffer, width, height);
 
-    if (fullOptions.generateMipMaps) {
-        this._gl.generateMipmap(this._gl.TEXTURE_2D);
+    // No need to rebind on every frame
+    if (!texture.is2DArray) {
+        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture._webGLTexture, 0);
     }
 
-    // Unbind
-    this._bindTextureDirectly(gl.TEXTURE_2D, null);
-    gl.bindRenderbuffer(gl.RENDERBUFFER, null);
-    this._bindUnboundFramebuffer(currentFrameBuffer);
+    this._bindUnboundFramebuffer(null);
 
     texture._framebuffer = framebuffer;
     texture.baseWidth = width;
     texture.baseHeight = height;
     texture.width = width;
     texture.height = height;
+    texture.depth = layers;
     texture.isReady = true;
     texture.samples = 1;
     texture.generateMipMaps = fullOptions.generateMipMaps ? true : false;
@@ -109,14 +121,12 @@ ThinEngine.prototype.createRenderTargetTexture = function(this: ThinEngine, size
     texture._generateDepthBuffer = fullOptions.generateDepthBuffer;
     texture._generateStencilBuffer = fullOptions.generateStencilBuffer ? true : false;
 
-    // this.resetTextureCache();
-
     this._internalTexturesCache.push(texture);
 
     return texture;
 };
 
-ThinEngine.prototype.createDepthStencilTexture = function(size: number | { width: number, height: number }, options: DepthTextureCreationOptions): InternalTexture {
+ThinEngine.prototype.createDepthStencilTexture = function(size: number | { width: number, height: number, layers?: number }, options: DepthTextureCreationOptions): InternalTexture {
     if (options.isCube) {
         let width = (<{ width: number, height: number }>size).width || <number>size;
         return this._createDepthStencilCubeTexture(width, options);
@@ -126,44 +136,42 @@ ThinEngine.prototype.createDepthStencilTexture = function(size: number | { width
     }
 };
 
-ThinEngine.prototype._createDepthStencilTexture = function(size: number | { width: number, height: number }, options: DepthTextureCreationOptions): InternalTexture {
-    var internalTexture = new InternalTexture(this, InternalTextureSource.Depth);
-
+ThinEngine.prototype._createDepthStencilTexture = function(size: number | { width: number, height: number, layers?: number }, options: DepthTextureCreationOptions): InternalTexture {
+    const gl = this._gl;
+    const layers = (<{ width: number, height: number, layers?: number }>size).layers || 0;
+    const target = layers !== 0 ? gl.TEXTURE_2D_ARRAY : gl.TEXTURE_2D;
+    const internalTexture = new InternalTexture(this, InternalTextureSource.Depth);
     if (!this._caps.depthTextureExtension) {
         Logger.Error("Depth texture is not supported by your browser or hardware.");
         return internalTexture;
     }
 
-    var internalOptions = {
+    const internalOptions = {
         bilinearFiltering: false,
         comparisonFunction: 0,
         generateStencil: false,
         ...options
     };
 
-    var gl = this._gl;
-    this._bindTextureDirectly(gl.TEXTURE_2D, internalTexture, true);
+    this._bindTextureDirectly(target, internalTexture, true);
 
     this._setupDepthStencilTexture(internalTexture, size, internalOptions.generateStencil, internalOptions.bilinearFiltering, internalOptions.comparisonFunction);
 
+    const type = internalOptions.generateStencil ? gl.UNSIGNED_INT_24_8 : gl.UNSIGNED_INT;
+    const internalFormat = internalOptions.generateStencil ? gl.DEPTH_STENCIL : gl.DEPTH_COMPONENT;
+    let sizedFormat = internalFormat;
     if (this.webGLVersion > 1) {
-        if (internalOptions.generateStencil) {
-            gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH24_STENCIL8, internalTexture.width, internalTexture.height, 0, gl.DEPTH_STENCIL, gl.UNSIGNED_INT_24_8, null);
-        }
-        else {
-            gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT24, internalTexture.width, internalTexture.height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, null);
-        }
+        sizedFormat = internalOptions.generateStencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT24;
+    }
+
+    if (internalTexture.is2DArray) {
+        gl.texImage3D(target, 0, sizedFormat, internalTexture.width, internalTexture.height, layers, 0, internalFormat, type, null);
     }
     else {
-        if (internalOptions.generateStencil) {
-            gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_STENCIL, internalTexture.width, internalTexture.height, 0, gl.DEPTH_STENCIL, gl.UNSIGNED_INT_24_8, null);
-        }
-        else {
-            gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, internalTexture.width, internalTexture.height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, null);
-        }
+        gl.texImage2D(target, 0, sizedFormat, internalTexture.width, internalTexture.height, 0, internalFormat, type, null);
     }
 
-    this._bindTextureDirectly(gl.TEXTURE_2D, null);
+    this._bindTextureDirectly(target, null);
 
     return internalTexture;
 };

+ 0 - 1
src/Engines/Extensions/engine.renderTargetCube.ts

@@ -70,7 +70,6 @@ ThinEngine.prototype.createRenderTargetCubeTexture = function(size: number, opti
 
     // Unbind
     this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
-    gl.bindRenderbuffer(gl.RENDERBUFFER, null);
     this._bindUnboundFramebuffer(null);
 
     texture._framebuffer = framebuffer;

+ 0 - 35
src/Engines/engine.ts

@@ -1630,40 +1630,6 @@ export class Engine extends ThinEngine {
     }
 
     /**
-     * Sets the frame buffer Depth / Stencil attachement of the render target to the defined depth stencil texture.
-     * @param renderTarget The render target to set the frame buffer for
-     */
-    public setFrameBufferDepthStencilTexture(renderTarget: RenderTargetTexture): void {
-        // Create the framebuffer
-        var internalTexture = renderTarget.getInternalTexture();
-        if (!internalTexture || !internalTexture._framebuffer || !renderTarget.depthStencilTexture) {
-            return;
-        }
-
-        var gl = this._gl;
-        var depthStencilTexture = renderTarget.depthStencilTexture;
-
-        this._bindUnboundFramebuffer(internalTexture._framebuffer);
-        if (depthStencilTexture.isCube) {
-            if (depthStencilTexture._generateStencilBuffer) {
-                gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.TEXTURE_CUBE_MAP_POSITIVE_X, depthStencilTexture._webGLTexture, 0);
-            }
-            else {
-                gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_CUBE_MAP_POSITIVE_X, depthStencilTexture._webGLTexture, 0);
-            }
-        }
-        else {
-            if (depthStencilTexture._generateStencilBuffer) {
-                gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.TEXTURE_2D, depthStencilTexture._webGLTexture, 0);
-            }
-            else {
-                gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthStencilTexture._webGLTexture, 0);
-            }
-        }
-        this._bindUnboundFramebuffer(null);
-    }
-
-    /**
      * Update a dynamic index buffer
      * @param indexBuffer defines the target index buffer
      * @param indices defines the data to update
@@ -1751,7 +1717,6 @@ export class Engine extends ThinEngine {
         texture.samples = samples;
         texture._depthStencilBuffer = this._setupFramebufferDepthAttachments(texture._generateStencilBuffer, texture._generateDepthBuffer, texture.width, texture.height, samples);
 
-        gl.bindRenderbuffer(gl.RENDERBUFFER, null);
         this._bindUnboundFramebuffer(null);
 
         return samples;

+ 0 - 1
src/Engines/nullEngine.ts

@@ -647,7 +647,6 @@ export class NullEngine extends Engine {
      * @param requiredWidth The width of the target to render to
      * @param requiredHeight The height of the target to render to
      * @param forceFullscreenViewport Forces the viewport to be the entire texture/screen if true
-     * @param depthStencilTexture The depth stencil texture to use to render
      * @param lodLevel defines le lod level to bind to the frame buffer
      */
     public bindFramebuffer(texture: InternalTexture, faceIndex?: number, requiredWidth?: number, requiredHeight?: number, forceFullscreenViewport?: boolean): void {

+ 35 - 23
src/Engines/thinEngine.ts

@@ -1290,29 +1290,35 @@ export class ThinEngine {
      * @param requiredWidth The width of the target to render to
      * @param requiredHeight The height of the target to render to
      * @param forceFullscreenViewport Forces the viewport to be the entire texture/screen if true
-     * @param depthStencilTexture The depth stencil texture to use to render
-     * @param lodLevel defines le lod level to bind to the frame buffer
+     * @param lodLevel defines the lod level to bind to the frame buffer
+     * @param layer defines the 2d array index to bind to frame buffer to
      */
-    public bindFramebuffer(texture: InternalTexture, faceIndex?: number, requiredWidth?: number, requiredHeight?: number, forceFullscreenViewport?: boolean, depthStencilTexture?: InternalTexture, lodLevel = 0): void {
+    public bindFramebuffer(texture: InternalTexture, faceIndex: number = 0, requiredWidth?: number, requiredHeight?: number, forceFullscreenViewport?: boolean, lodLevel = 0, layer = 0): void {
         if (this._currentRenderTarget) {
             this.unBindFramebuffer(this._currentRenderTarget);
         }
         this._currentRenderTarget = texture;
         this._bindUnboundFramebuffer(texture._MSAAFramebuffer ? texture._MSAAFramebuffer : texture._framebuffer);
-        var gl = this._gl;
-        if (texture.isCube) {
-            if (faceIndex === undefined) {
-                faceIndex = 0;
-            }
+
+        const gl = this._gl;
+        if (texture.is2DArray) {
+            gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texture._webGLTexture, lodLevel, layer);
+        }
+        else if (texture.isCube) {
             gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, texture._webGLTexture, lodLevel);
+        }
 
-            if (depthStencilTexture) {
-                if (depthStencilTexture._generateStencilBuffer) {
-                    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, depthStencilTexture._webGLTexture, lodLevel);
-                }
-                else {
-                    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, depthStencilTexture._webGLTexture, lodLevel);
-                }
+        const depthStencilTexture = texture._depthStencilTexture;
+        if (depthStencilTexture) {
+            const attachment = (depthStencilTexture._generateStencilBuffer) ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT;
+            if (texture.is2DArray) {
+                gl.framebufferTextureLayer(gl.FRAMEBUFFER, attachment, depthStencilTexture._webGLTexture, lodLevel, layer);
+            }
+            else if (texture.isCube) {
+                gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, depthStencilTexture._webGLTexture, lodLevel);
+            }
+            else {
+                gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, depthStencilTexture._webGLTexture, lodLevel);
             }
         }
 
@@ -3144,13 +3150,17 @@ export class ThinEngine {
     }
 
     /** @hidden */
-    public _setupDepthStencilTexture(internalTexture: InternalTexture, size: number | { width: number, height: number }, generateStencil: boolean, bilinearFiltering: boolean, comparisonFunction: number): void {
-        var width = (<{ width: number, height: number }>size).width || <number>size;
-        var height = (<{ width: number, height: number }>size).height || <number>size;
+    public _setupDepthStencilTexture(internalTexture: InternalTexture, size: number | { width: number, height: number, layers?: number }, generateStencil: boolean, bilinearFiltering: boolean, comparisonFunction: number): void {
+        const width = (<{ width: number, height: number, layers?: number }>size).width || <number>size;
+        const height = (<{ width: number, height: number, layers?: number }>size).height || <number>size;
+        const layers = (<{ width: number, height: number, layers?: number }>size).layers || 0;
+
         internalTexture.baseWidth = width;
         internalTexture.baseHeight = height;
         internalTexture.width = width;
         internalTexture.height = height;
+        internalTexture.is2DArray = layers > 0;
+        internalTexture.depth = layers;
         internalTexture.isReady = true;
         internalTexture.samples = 1;
         internalTexture.generateMipMaps = false;
@@ -3160,9 +3170,9 @@ export class ThinEngine {
         internalTexture.type = Constants.TEXTURETYPE_UNSIGNED_INT;
         internalTexture._comparisonFunction = comparisonFunction;
 
-        var gl = this._gl;
-        var target = internalTexture.isCube ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D;
-        var samplingParameters = this._getSamplingParameters(internalTexture.samplingMode, false);
+        const gl = this._gl;
+        const target = this._getTextureTarget(internalTexture);
+        const samplingParameters = this._getSamplingParameters(internalTexture.samplingMode, false);
         gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, samplingParameters.mag);
         gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, samplingParameters.min);
         gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
@@ -3326,10 +3336,9 @@ export class ThinEngine {
             return this._getDepthStencilBuffer(width, height, samples, gl.DEPTH_STENCIL, gl.DEPTH24_STENCIL8, gl.DEPTH_STENCIL_ATTACHMENT);
         }
         if (generateDepthBuffer) {
-            let anyGl = <any>gl;
             let depthFormat = gl.DEPTH_COMPONENT16;
             if (this._webGLVersion > 1) {
-                depthFormat = anyGl.DEPTH_COMPONENT32F;
+                depthFormat = gl.DEPTH_COMPONENT32F;
             }
 
             return this._getDepthStencilBuffer(width, height, samples, depthFormat, depthFormat, gl.DEPTH_ATTACHMENT);
@@ -3354,6 +3363,9 @@ export class ThinEngine {
         }
 
         gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, depthStencilBuffer);
+
+        gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+
         return depthStencilBuffer;
     }
 

+ 3 - 0
src/LibDeclarations/webgl2.d.ts

@@ -54,11 +54,14 @@ interface WebGLRenderingContext {
     readonly UNSIGNED_INT_10F_11F_11F_REV: number;
     readonly UNSIGNED_INT_5_9_9_9_REV: number;
     readonly FLOAT_32_UNSIGNED_INT_24_8_REV: number;
+    readonly DEPTH_COMPONENT32F: number;
 
     texImage3D(target: number, level: number, internalformat: number, width: number, height: number, depth: number, border: number, format: number, type: number, pixels: ArrayBufferView | null): void;
     texImage3D(target: number, level: number, internalformat: number, width: number, height: number, depth: number, border: number, format: number, type: number, pixels: ArrayBufferView, offset: number): void;
     texImage3D(target: number, level: number, internalformat: number, width: number, height: number, depth: number, border: number, format: number, type: number, pixels: ImageBitmap | ImageData | HTMLVideoElement | HTMLImageElement | HTMLCanvasElement): void;
 
+    framebufferTextureLayer(target: number, attachment: number, texture: WebGLTexture | null, level: number, layer: number): void;
+
     compressedTexImage3D(target: number, level: number, internalformat: number, width: number, height: number, depth: number, border: number, data: ArrayBufferView, offset?: number, length?: number): void;
 
     readonly TRANSFORM_FEEDBACK: number;

Plik diff jest za duży
+ 1707 - 0
src/Lights/Shadows/cascadedShadowGenerator.ts


+ 1 - 0
src/Lights/Shadows/index.ts

@@ -1,2 +1,3 @@
 export * from "./shadowGenerator";
+export * from "./cascadedShadowGenerator";
 export * from "./shadowGeneratorSceneComponent";

+ 4 - 8
src/Lights/Shadows/shadowGenerator.ts

@@ -69,11 +69,6 @@ export interface IShadowGenerator {
      * @returns The render target texture if present otherwise, null
      */
     getShadowMap(): Nullable<RenderTargetTexture>;
-    /**
-     * Gets the RTT used during rendering (can be a blurred version of the shadow map or the shadow map itself).
-     * @returns The render target texture if the shadow map is present otherwise, null
-     */
-    getShadowMapForRendering(): Nullable<RenderTargetTexture>;
 
     /**
      * Determine wheter the shadow generator is ready or not (mainly all effects and related post processes needs to be ready).
@@ -115,7 +110,7 @@ export interface IShadowGenerator {
      * @param onCompiled Callback triggered at the and of the effects compilation
      * @param options Sets of optional options forcing the compilation with different modes
      */
-    forceCompilation(onCompiled?: (generator: ShadowGenerator) => void, options?: Partial<{ useInstances: boolean }>): void;
+    forceCompilation(onCompiled?: (generator: IShadowGenerator) => void, options?: Partial<{ useInstances: boolean }>): void;
 
     /**
      * Forces all the attached effect to compile to enable rendering only once ready vs. lazyly compiling effects.
@@ -936,12 +931,13 @@ export class ShadowGenerator implements IShadowGenerator {
         var index: number;
         let engine = this._scene.getEngine();
 
+        const colorWrite = engine.getColorWrite();
         if (depthOnlySubMeshes.length) {
             engine.setColorWrite(false);
             for (index = 0; index < depthOnlySubMeshes.length; index++) {
                 this._renderSubMeshForShadowMap(depthOnlySubMeshes.data[index]);
             }
-            engine.setColorWrite(true);
+            engine.setColorWrite(colorWrite);
         }
 
         for (index = 0; index < opaqueSubMeshes.length; index++) {
@@ -1074,7 +1070,7 @@ export class ShadowGenerator implements IShadowGenerator {
      * @param onCompiled Callback triggered at the and of the effects compilation
      * @param options Sets of optional options forcing the compilation with different modes
      */
-    public forceCompilation(onCompiled?: (generator: ShadowGenerator) => void, options?: Partial<{ useInstances: boolean }>): void {
+    public forceCompilation(onCompiled?: (generator: IShadowGenerator) => void, options?: Partial<{ useInstances: boolean }>): void {
         let localOptions = {
             useInstances: false,
             ...options

+ 9 - 0
src/Lights/Shadows/shadowGeneratorSceneComponent.ts

@@ -2,6 +2,7 @@ import { SmartArrayNoDuplicate } from "../../Misc/smartArray";
 import { Scene } from "../../scene";
 import { RenderTargetTexture } from "../../Materials/Textures/renderTargetTexture";
 import { ShadowGenerator } from "./shadowGenerator";
+import { CascadedShadowGenerator } from "./cascadedShadowGenerator";
 import { SceneComponentConstants, ISceneSerializableComponent } from "../../sceneComponent";
 import { _TimeToken } from "../../Instrumentation/timeToken";
 import { AbstractScene } from "../../abstractScene";
@@ -121,4 +122,12 @@ ShadowGenerator._SceneComponentInitialization = (scene: Scene) => {
         component = new ShadowGeneratorSceneComponent(scene);
         scene._addComponent(component);
     }
+};
+
+CascadedShadowGenerator._SceneComponentInitialization = (scene: Scene) => {
+    let component = scene._getComponent(SceneComponentConstants.NAME_SHADOWGENERATOR);
+    if (!component) {
+        component = new ShadowGeneratorSceneComponent(scene);
+        scene._addComponent(component);
+    }
 };

+ 12 - 2
src/Materials/Textures/internalTexture.ts

@@ -221,6 +221,8 @@ export class InternalTexture {
     public _lodGenerationScale: number = 0;
     /** @hidden */
     public _lodGenerationOffset: number = 0;
+    /** @hidden */
+    public _depthStencilTexture: Nullable<InternalTexture>;
 
     // Multiview
     /** @hidden */
@@ -372,7 +374,8 @@ export class InternalTexture {
                 } else {
                     let size = {
                         width: this.width,
-                        height: this.height
+                        height: this.height,
+                        layers: this.is2DArray ? this.depth : undefined
                     };
 
                     proxy = (this._engine as Engine).createRenderTargetTexture(size, options);
@@ -389,7 +392,12 @@ export class InternalTexture {
                     isCube: this.isCube
                 };
 
-                proxy = this._engine.createDepthStencilTexture({ width: this.width, height: this.height }, depthTextureOptions);
+                let size = {
+                    width: this.width,
+                    height: this.height,
+                    layers: this.is2DArray ? this.depth : undefined
+                };
+                proxy = this._engine.createDepthStencilTexture(size, depthTextureOptions);
                 proxy._swapAndDie(this);
 
                 this.isReady = true;
@@ -442,6 +450,8 @@ export class InternalTexture {
             target._depthStencilBuffer = this._depthStencilBuffer;
         }
 
+        target._depthStencilTexture = this._depthStencilTexture;
+
         if (this._lodTextureHigh) {
             if (target._lodTextureHigh) {
                 target._lodTextureHigh.dispose();

+ 41 - 13
src/Materials/Textures/renderTargetTexture.ts

@@ -208,7 +208,7 @@ export class RenderTargetTexture extends Texture {
      * Define the clear color of the Render Target if it should be different from the scene.
      */
     public clearColor: Color4;
-    protected _size: number | { width: number, height: number };
+    protected _size: number | { width: number, height: number, layers?: number };
     protected _initialSizeParameter: number | { width: number, height: number } | { ratio: number };
     protected _sizeRatio: Nullable<number>;
     /** @hidden */
@@ -270,7 +270,9 @@ export class RenderTargetTexture extends Texture {
      * depth texture.
      * Otherwise, return null.
      */
-    public depthStencilTexture: Nullable<InternalTexture>;
+    public get depthStencilTexture(): Nullable<InternalTexture> {
+        return this.getInternalTexture()?._depthStencilTexture || null;
+    }
 
     /**
      * Instantiate a render target texture. This is mainly used to render of screen the scene to for instance apply post processse
@@ -289,7 +291,7 @@ export class RenderTargetTexture extends Texture {
      * @param format The internal format of the buffer in the RTT (RED, RG, RGB, RGBA, ALPHA...)
      * @param delayAllocation if the texture allocation should be delayed (default: false)
      */
-    constructor(name: string, size: number | { width: number, height: number } | { ratio: number }, scene: Nullable<Scene>, generateMipMaps?: boolean, doNotChangeAspectRatio: boolean = true, type: number = Constants.TEXTURETYPE_UNSIGNED_INT, public isCube = false, samplingMode = Texture.TRILINEAR_SAMPLINGMODE, generateDepthBuffer = true, generateStencilBuffer = false, isMulti = false, format = Constants.TEXTUREFORMAT_RGBA, delayAllocation = false) {
+    constructor(name: string, size: number | { width: number, height: number, layers?: number } | { ratio: number }, scene: Nullable<Scene>, generateMipMaps?: boolean, doNotChangeAspectRatio: boolean = true, type: number = Constants.TEXTURETYPE_UNSIGNED_INT, public isCube = false, samplingMode = Texture.TRILINEAR_SAMPLINGMODE, generateDepthBuffer = true, generateStencilBuffer = false, isMulti = false, format = Constants.TEXTUREFORMAT_RGBA, delayAllocation = false) {
         super(null, scene, !generateMipMaps);
         scene = this.getScene();
 
@@ -352,18 +354,18 @@ export class RenderTargetTexture extends Texture {
      * @param generateStencil Specifies whether or not a stencil should be allocated in the texture
      */
     public createDepthStencilTexture(comparisonFunction: number = 0, bilinearFiltering: boolean = true, generateStencil: boolean = false): void {
-        if (!this.getScene()) {
+        const internalTexture = this.getInternalTexture();
+        if (!this.getScene() || !internalTexture) {
             return;
         }
 
         var engine = this.getScene()!.getEngine();
-        this.depthStencilTexture = engine.createDepthStencilTexture(this._size, {
+        internalTexture._depthStencilTexture = engine.createDepthStencilTexture(this._size, {
             bilinearFiltering,
             comparisonFunction,
             generateStencil,
             isCube: this.isCube
         });
-        engine.setFrameBufferDepthStencilTexture(this);
     }
 
     private _processSizeParameter(size: number | { width: number, height: number } | { ratio: number }): void {
@@ -374,7 +376,7 @@ export class RenderTargetTexture extends Texture {
                 height: this._bestReflectionRenderTargetDimension(this._engine.getRenderHeight(), this._sizeRatio)
             };
         } else {
-            this._size = <number | { width: number, height: number }>size;
+            this._size = <number | { width: number, height: number, layers?: number }>size;
         }
     }
 
@@ -528,6 +530,19 @@ export class RenderTargetTexture extends Texture {
     }
 
     /**
+     * Gets the actual number of layers of the texture.
+     * @returns the number of layers
+     */
+    public getRenderLayers(): number {
+        const layers = (<{ width: number, height: number, layers?: number }>this._size).layers;
+        if (layers) {
+            return layers;
+        }
+
+        return 0;
+    }
+
+    /**
      * Get if the texture can be rescaled or not.
      */
     public get canRescale(): boolean {
@@ -717,7 +732,14 @@ export class RenderTargetTexture extends Texture {
             }
         }
 
-        if (this.isCube) {
+        if (this.is2DArray) {
+            for (let layer = 0; layer < this.getRenderLayers(); layer++) {
+                this.renderToTarget(0, currentRenderList, useCameraPostProcess, dumpForDebug, layer);
+                scene.incrementRenderId();
+                scene.resetCachedMaterial();
+            }
+        }
+        else if (this.isCube) {
             for (var face = 0; face < 6; face++) {
                 this.renderToTarget(face, currentRenderList, useCameraPostProcess, dumpForDebug);
                 scene.incrementRenderId();
@@ -752,8 +774,9 @@ export class RenderTargetTexture extends Texture {
     /**
      * @hidden
      * @param faceIndex face index to bind to if this is a cubetexture
+     * @param layer defines the index of the texture to bind in the array
      */
-    public _bindFrameBuffer(faceIndex: number = 0) {
+    public _bindFrameBuffer(faceIndex: number = 0, layer = 0) {
         var scene = this.getScene();
         if (!scene) {
             return;
@@ -761,7 +784,7 @@ export class RenderTargetTexture extends Texture {
 
         var engine = scene.getEngine();
         if (this._texture) {
-            engine.bindFramebuffer(this._texture, this.isCube ? faceIndex : undefined, undefined, undefined, this.ignoreCameraViewport, this.depthStencilTexture ? this.depthStencilTexture : undefined);
+            engine.bindFramebuffer(this._texture, this.isCube ? faceIndex : undefined, undefined, undefined, this.ignoreCameraViewport, 0, layer);
         }
     }
 
@@ -774,7 +797,7 @@ export class RenderTargetTexture extends Texture {
         });
     }
 
-    private renderToTarget(faceIndex: number, currentRenderList: AbstractMesh[], useCameraPostProcess: boolean, dumpForDebug: boolean): void {
+    private renderToTarget(faceIndex: number, currentRenderList: AbstractMesh[], useCameraPostProcess: boolean, dumpForDebug: boolean, layer = 0): void {
         var scene = this.getScene();
 
         if (!scene) {
@@ -792,10 +815,15 @@ export class RenderTargetTexture extends Texture {
             this._postProcessManager._prepareFrame(this._texture, this._postProcesses);
         }
         else if (!useCameraPostProcess || !scene.postProcessManager._prepareFrame(this._texture)) {
-            this._bindFrameBuffer(faceIndex);
+            this._bindFrameBuffer(faceIndex, layer);
         }
 
-        this.onBeforeRenderObservable.notifyObservers(faceIndex);
+        if (this.is2DArray) {
+            this.onBeforeRenderObservable.notifyObservers(layer);
+        }
+        else {
+            this.onBeforeRenderObservable.notifyObservers(faceIndex);
+        }
 
         // Clear
         if (this.onClearObservable.hasObservers()) {

+ 17 - 0
src/Materials/materialHelper.ts

@@ -343,6 +343,10 @@ export class MaterialHelper {
 
         // Shadows
         defines["SHADOW" + lightIndex] = false;
+        defines["SHADOWCSM" + lightIndex] = false;
+        defines["SHADOWCSMDEBUG" + lightIndex] = false;
+        defines["SHADOWCSMNUM_CASCADES" + lightIndex] = false;
+        defines["SHADOWCSMUSESHADOWMAXZ" + lightIndex] = false;
         defines["SHADOWPCF" + lightIndex] = false;
         defines["SHADOWPCSS" + lightIndex] = false;
         defines["SHADOWPOISSON" + lightIndex] = false;
@@ -421,6 +425,10 @@ export class MaterialHelper {
                 defines["DIRLIGHT" + index] = false;
                 defines["SPOTLIGHT" + index] = false;
                 defines["SHADOW" + index] = false;
+                defines["SHADOWCSM" + index] = false;
+                defines["SHADOWCSMDEBUG" + index] = false;
+                defines["SHADOWCSMNUM_CASCADES" + index] = false;
+                defines["SHADOWCSMUSESHADOWMAXZ" + index] = false;
                 defines["SHADOWPCF" + index] = false;
                 defines["SHADOWPCSS" + index] = false;
                 defines["SHADOWPOISSON" + index] = false;
@@ -477,6 +485,15 @@ export class MaterialHelper {
         samplersList.push("shadowSampler" + lightIndex);
         samplersList.push("depthSampler" + lightIndex);
 
+        uniformsList.push(
+            "viewFrustumZ" + lightIndex,
+            "cascadeBlendFactor" + lightIndex,
+            "lightSizeUVCorrection" + lightIndex,
+            "depthCorrection" + lightIndex,
+            "penumbraDarkness" + lightIndex,
+            "frustumLengths" + lightIndex,
+        );
+
         if (projectedLightTexture) {
             samplersList.push("projectionLightSampler" + lightIndex);
             uniformsList.push(

+ 7 - 2
src/PostProcesses/postProcess.ts

@@ -136,6 +136,7 @@ export class PostProcess {
     private _options: number | PostProcessOptions;
     private _reusable = false;
     private _textureType: number;
+    private _textureFormat: number;
     /**
     * Smart array of input and output textures for the post process.
     * @hidden
@@ -301,12 +302,14 @@ export class PostProcess {
      * @param vertexUrl The url of the vertex shader to be used. (default: "postprocess")
      * @param indexParameters The index parameters to be used for babylons include syntax "#include<kernelBlurVaryingDeclaration>[0..varyingCount]". (default: undefined) See usage in babylon.blurPostProcess.ts and kernelBlur.vertex.fx
      * @param blockCompilation If the shader should not be compiled imediatly. (default: false)
+     * @param textureFormat Format of textures used when performing the post process. (default: TEXTUREFORMAT_RGBA)
      */
     constructor(
         /** Name of the PostProcess. */
         public name: string,
         fragmentUrl: string, parameters: Nullable<string[]>, samplers: Nullable<string[]>, options: number | PostProcessOptions, camera: Nullable<Camera>,
-        samplingMode: number = Constants.TEXTURE_NEAREST_SAMPLINGMODE, engine?: Engine, reusable?: boolean, defines: Nullable<string> = null, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT, vertexUrl: string = "postprocess", indexParameters?: any, blockCompilation = false) {
+        samplingMode: number = Constants.TEXTURE_NEAREST_SAMPLINGMODE, engine?: Engine, reusable?: boolean, defines: Nullable<string> = null, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT, vertexUrl: string = "postprocess",
+        indexParameters?: any, blockCompilation = false, textureFormat = Constants.TEXTUREFORMAT_RGBA) {
         if (camera != null) {
             this._camera = camera;
             this._scene = camera.getScene();
@@ -324,6 +327,7 @@ export class PostProcess {
         this.renderTargetSamplingMode = samplingMode ? samplingMode : Constants.TEXTURE_NEAREST_SAMPLINGMODE;
         this._reusable = reusable || false;
         this._textureType = textureType;
+        this._textureFormat = textureFormat;
 
         this._samplers = samplers || [];
         this._samplers.push("textureSampler");
@@ -495,7 +499,8 @@ export class PostProcess {
                     generateDepthBuffer: forceDepthStencil || camera._postProcesses.indexOf(this) === 0,
                     generateStencilBuffer: (forceDepthStencil || camera._postProcesses.indexOf(this) === 0) && this._engine.isStencilEnable,
                     samplingMode: this.renderTargetSamplingMode,
-                    type: this._textureType
+                    type: this._textureType,
+                    format: this._textureFormat
                 };
 
                 this._textures.push(this._engine.createRenderTargetTexture(textureSize, textureOptions));

+ 1 - 1
src/PostProcesses/postProcessManager.ts

@@ -110,7 +110,7 @@ export class PostProcessManager {
                 postProcesses[index + 1].activate(this._scene.activeCamera, targetTexture);
             } else {
                 if (targetTexture) {
-                    engine.bindFramebuffer(targetTexture, faceIndex, undefined, undefined, forceFullscreenViewport, undefined, lodLevel);
+                    engine.bindFramebuffer(targetTexture, faceIndex, undefined, undefined, forceFullscreenViewport, lodLevel);
                 } else {
                     engine.restoreDefaultFramebuffer();
                 }

+ 76 - 2
src/Shaders/ShadersInclude/lightFragment.fx

@@ -125,7 +125,77 @@
     #endif
 
     #ifdef SHADOW{X}
-        #ifdef SHADOWCLOSEESM{X}
+        #ifdef SHADOWCSM{X}
+            for (int i = 0; i < SHADOWCSMNUM_CASCADES{X}; i++) 
+            {
+                diff{X} = viewFrustumZ{X}[i] - vPositionFromCamera{X}.z;
+                if (diff{X} >= 0.) {
+                    index{X} = i;
+                    break;
+                }
+            }
+
+            #ifdef SHADOWCSMUSESHADOWMAXZ{X}
+            if (index{X} >= 0)
+            #endif
+            {
+                float frustumLength = frustumLengths{X}[index{X}];
+                #if defined(SHADOWPCF{X})
+                    #if defined(SHADOWLOWQUALITY{X})
+                        shadow = computeShadowWithCSMPCF1(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w);
+                    #elif defined(SHADOWMEDIUMQUALITY{X})
+                        shadow = computeShadowWithCSMPCF3(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowSampler{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w);
+                    #else
+                        shadow = computeShadowWithCSMPCF5(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowSampler{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w);
+                    #endif
+                #elif defined(SHADOWPCSS{X})
+                    #if defined(SHADOWLOWQUALITY{X})
+                        shadow = computeShadowWithCSMPCSS16(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthSampler{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X});
+                    #elif defined(SHADOWMEDIUMQUALITY{X})
+                        shadow = computeShadowWithCSMPCSS32(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthSampler{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X});
+                    #else
+                        shadow = computeShadowWithCSMPCSS64(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthSampler{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X});
+                    #endif
+                #else
+                    shadow = computeShadowCSM(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w);
+                #endif
+
+                #ifdef SHADOWCSMDEBUG{X}
+                    shadowDebug{X} = vec3(shadow) * vCascadeColorsMultiplier{X}[index{X}];
+                #endif
+
+                float diffRatio = clamp(diff{X} / frustumLength, 0., 1.) * cascadeBlendFactor{X};
+                if (index{X} < (SHADOWCSMNUM_CASCADES{X} - 1) && diffRatio < 1.)
+                {
+                    index{X} += 1;
+                    float nextShadow = 0.;
+                    #if defined(SHADOWPCF{X})
+                        #if defined(SHADOWLOWQUALITY{X})
+                            nextShadow = computeShadowWithCSMPCF1(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w);
+                        #elif defined(SHADOWMEDIUMQUALITY{X})
+                            nextShadow = computeShadowWithCSMPCF3(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowSampler{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w);
+                        #else
+                            nextShadow = computeShadowWithCSMPCF5(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowSampler{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w);
+                        #endif
+                    #elif defined(SHADOWPCSS{X})
+                        #if defined(SHADOWLOWQUALITY{X})
+                            nextShadow = computeShadowWithCSMPCSS16(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthSampler{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X});
+                        #elif defined(SHADOWMEDIUMQUALITY{X})
+                            nextShadow = computeShadowWithCSMPCSS32(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthSampler{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X});
+                        #else
+                            nextShadow = computeShadowWithCSMPCSS64(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthSampler{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X});
+                        #endif
+                    #else
+                        nextShadow = computeShadowCSM(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w);
+                    #endif
+
+                    shadow = mix(nextShadow, shadow, diffRatio);
+                    #ifdef SHADOWCSMDEBUG{X}
+                        shadowDebug{X} = mix(vec3(nextShadow) * vCascadeColorsMultiplier{X}[index{X}], shadowDebug{X}, diffRatio);
+                    #endif
+                }
+            }
+        #elif defined(SHADOWCLOSEESM{X})
             #if defined(SHADOWCUBE{X})
                 shadow = computeShadowWithCloseESMCube(light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.depthValues);
             #else
@@ -202,7 +272,11 @@
                 #endif
             #endif
         #else
-            diffuseBase += info.diffuse * shadow;
+            #ifdef SHADOWCSMDEBUG{X}
+                diffuseBase += info.diffuse * shadowDebug{X};
+            #else        
+                diffuseBase += info.diffuse * shadow;
+            #endif
             #ifdef SPECULARTERM
                 specularBase += info.specular * shadow;
             #endif

+ 45 - 1
src/Shaders/ShadersInclude/lightFragmentDeclaration.fx

@@ -8,7 +8,51 @@
 		vec4 vLightSpecular{X} = vec4(0.);
 	#endif
 	#ifdef SHADOW{X}
-		#if defined(SHADOWCUBE{X})
+        #ifdef SHADOWCSM{X}
+            uniform mat4 lightMatrix{X}[SHADOWCSMNUM_CASCADES{X}];
+            uniform float viewFrustumZ{X}[SHADOWCSMNUM_CASCADES{X}];
+            uniform float frustumLengths{X}[SHADOWCSMNUM_CASCADES{X}];
+            uniform float cascadeBlendFactor{X};
+
+            varying vec4 vPositionFromLight{X}[SHADOWCSMNUM_CASCADES{X}];
+            varying float vDepthMetric{X}[SHADOWCSMNUM_CASCADES{X}];
+            varying vec4 vPositionFromCamera{X};
+
+            #if defined(SHADOWPCSS{X})
+                uniform highp sampler2DArrayShadow shadowSampler{X};
+                uniform highp sampler2DArray depthSampler{X};
+                uniform vec2 lightSizeUVCorrection{X}[SHADOWCSMNUM_CASCADES{X}];
+                uniform float depthCorrection{X}[SHADOWCSMNUM_CASCADES{X}];
+                uniform float penumbraDarkness{X};
+            #elif defined(SHADOWPCF{X})
+                uniform highp sampler2DArrayShadow shadowSampler{X};
+            #else
+                uniform highp sampler2DArray shadowSampler{X};
+            #endif
+
+            #ifdef SHADOWCSMDEBUG{X}
+                const vec3 vCascadeColorsMultiplier{X}[8] = vec3[8]
+                (
+                    vec3 ( 1.5, 0.0, 0.0 ),
+                    vec3 ( 0.0, 1.5, 0.0 ),
+                    vec3 ( 0.0, 0.0, 5.5 ),
+                    vec3 ( 1.5, 0.0, 5.5 ),
+                    vec3 ( 1.5, 1.5, 0.0 ),
+                    vec3 ( 1.0, 1.0, 1.0 ),
+                    vec3 ( 0.0, 1.0, 5.5 ),
+                    vec3 ( 0.5, 3.5, 0.75 )
+                );
+                vec3 shadowDebug{X};
+            #endif
+
+            #ifdef SHADOWCSMUSESHADOWMAXZ{X}
+                int index{X} = -1;
+            #else
+                int index{X} = SHADOWCSMNUM_CASCADES{X} - 1;
+            #endif
+
+            float diff{X} = 0.;
+        #elif defined(SHADOWCUBE{X})
 			uniform samplerCube shadowSampler{X};
 		#else
 			varying vec4 vPositionFromLight{X};

+ 45 - 1
src/Shaders/ShadersInclude/lightUboDeclaration.fx

@@ -21,7 +21,51 @@
 	uniform sampler2D projectionLightSampler{X};
 #endif
 #ifdef SHADOW{X}
-	#if defined(SHADOWCUBE{X})
+	#ifdef SHADOWCSM{X}
+		uniform mat4 lightMatrix{X}[SHADOWCSMNUM_CASCADES{X}];
+		uniform float viewFrustumZ{X}[SHADOWCSMNUM_CASCADES{X}];
+        uniform float frustumLengths{X}[SHADOWCSMNUM_CASCADES{X}];
+        uniform float cascadeBlendFactor{X};
+
+		varying vec4 vPositionFromLight{X}[SHADOWCSMNUM_CASCADES{X}];
+		varying float vDepthMetric{X}[SHADOWCSMNUM_CASCADES{X}];
+		varying vec4 vPositionFromCamera{X};
+
+		#if defined(SHADOWPCSS{X})
+			uniform highp sampler2DArrayShadow shadowSampler{X};
+			uniform highp sampler2DArray depthSampler{X};
+            uniform vec2 lightSizeUVCorrection{X}[SHADOWCSMNUM_CASCADES{X}];
+            uniform float depthCorrection{X}[SHADOWCSMNUM_CASCADES{X}];
+            uniform float penumbraDarkness{X};
+		#elif defined(SHADOWPCF{X})
+			uniform highp sampler2DArrayShadow shadowSampler{X};
+		#else
+			uniform highp sampler2DArray shadowSampler{X};
+		#endif
+
+        #ifdef SHADOWCSMDEBUG{X}
+            const vec3 vCascadeColorsMultiplier{X}[8] = vec3[8]
+            (
+                vec3 ( 1.5, 0.0, 0.0 ),
+                vec3 ( 0.0, 1.5, 0.0 ),
+                vec3 ( 0.0, 0.0, 5.5 ),
+                vec3 ( 1.5, 0.0, 5.5 ),
+                vec3 ( 1.5, 1.5, 0.0 ),
+                vec3 ( 1.0, 1.0, 1.0 ),
+                vec3 ( 0.0, 1.0, 5.5 ),
+                vec3 ( 0.5, 3.5, 0.75 )
+            );
+            vec3 shadowDebug{X};
+        #endif
+
+        #ifdef SHADOWCSMUSESHADOWMAXZ{X}
+            int index{X} = -1;
+        #else
+            int index{X} = SHADOWCSMNUM_CASCADES{X} - 1;
+        #endif
+
+        float diff{X} = 0.;
+	#elif defined(SHADOWCUBE{X})
 		uniform samplerCube shadowSampler{X};		
 	#else
 		varying vec4 vPositionFromLight{X};

+ 218 - 1
src/Shaders/ShadersInclude/shadowsFragmentFunctions.fx

@@ -10,7 +10,7 @@
 
     float computeFallOff(float value, vec2 clipSpace, float frustumEdgeFalloff)
     {
-        float mask = smoothstep(1.0 - frustumEdgeFalloff, 1.0, clamp(dot(clipSpace, clipSpace), 0., 1.));
+        float mask = smoothstep(1.0 - frustumEdgeFalloff, 1.00000012, clamp(dot(clipSpace, clipSpace), 0., 1.));
         return mix(value, 1.0, mask);
     }
 
@@ -113,6 +113,34 @@
         return esm;
     }
 
+    #ifdef WEBGL2
+        float computeShadowCSM(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArray shadowSampler, float darkness, float frustumEdgeFalloff)
+        {
+            vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
+            vec2 uv = 0.5 * clipSpace.xy + vec2(0.5);
+            vec3 uvLayer = vec3(uv.x, uv.y, layer);
+
+            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, uvLayer));
+            #else
+                float shadow = texture2D(shadowSampler, uvLayer).x;
+            #endif
+
+            if (shadowPixelDepth > shadow)
+            {
+                return computeFallOff(darkness, clipSpace.xy, frustumEdgeFalloff);
+            }
+            return 1.;
+        }
+    #endif
+
     float computeShadow(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float darkness, float frustumEdgeFalloff)
     {
         vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -222,6 +250,115 @@
     }
 
     #ifdef WEBGL2
+        #define GREATEST_LESS_THAN_ONE 0.99999994
+
+        // Shadow PCF kernel size 1 with a single tap (lowest quality)
+        float computeShadowWithCSMPCF1(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArrayShadow shadowSampler, float darkness, float frustumEdgeFalloff)
+        {
+            vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
+            vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
+
+            if (uvDepth.x < 0. || uvDepth.x > 1.0 || uvDepth.y < 0. || uvDepth.y > 1.0)
+            {
+                return 1.0;
+            }
+
+            uvDepth.z = clamp(uvDepth.z, 0., GREATEST_LESS_THAN_ONE);
+
+            vec4 uvDepthLayer = vec4(uvDepth.x, uvDepth.y, layer, uvDepth.z);
+
+            float shadow = texture(shadowSampler, uvDepthLayer);
+            shadow = mix(darkness, 1., shadow);
+            return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
+        }
+
+        // Shadow PCF kernel 3*3 in only 4 taps (medium quality)
+        // This uses a well distributed taps to allow a gaussian distribution covering a 3*3 kernel
+        // https://mynameismjp.wordpress.com/2013/09/10/shadow-maps/
+        float computeShadowWithCSMPCF3(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArrayShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float frustumEdgeFalloff)
+        {
+            vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
+            vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
+
+            if (uvDepth.x < 0. || uvDepth.x > 1.0 || uvDepth.y < 0. || uvDepth.y > 1.0)
+            {
+                return 1.0;
+            }
+
+            uvDepth.z = clamp(uvDepth.z, 0., GREATEST_LESS_THAN_ONE);
+
+            vec2 uv = uvDepth.xy * shadowMapSizeAndInverse.x;	// uv in texel units
+            uv += 0.5;											// offset of half to be in the center of the texel
+            vec2 st = fract(uv);								// how far from the center
+            vec2 base_uv = floor(uv) - 0.5;						// texel coord
+            base_uv *= shadowMapSizeAndInverse.y;				// move back to uv coords
+
+            // Equation resolved to fit in a 3*3 distribution like 
+            // 1 2 1
+            // 2 4 2 
+            // 1 2 1
+            vec2 uvw0 = 3. - 2. * st;
+            vec2 uvw1 = 1. + 2. * st;
+            vec2 u = vec2((2. - st.x) / uvw0.x - 1., st.x / uvw1.x + 1.) * shadowMapSizeAndInverse.y;
+            vec2 v = vec2((2. - st.y) / uvw0.y - 1., st.y / uvw1.y + 1.) * shadowMapSizeAndInverse.y;
+
+            float shadow = 0.;
+            shadow += uvw0.x * uvw0.y * texture2D(shadowSampler, vec4(base_uv.xy + vec2(u[0], v[0]), layer, uvDepth.z));
+            shadow += uvw1.x * uvw0.y * texture2D(shadowSampler, vec4(base_uv.xy + vec2(u[1], v[0]), layer, uvDepth.z));
+            shadow += uvw0.x * uvw1.y * texture2D(shadowSampler, vec4(base_uv.xy + vec2(u[0], v[1]), layer, uvDepth.z));
+            shadow += uvw1.x * uvw1.y * texture2D(shadowSampler, vec4(base_uv.xy + vec2(u[1], v[1]), layer, uvDepth.z));
+            shadow = shadow / 16.;
+
+            shadow = mix(darkness, 1., shadow);
+            return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
+        }
+
+        // Shadow PCF kernel 5*5 in only 9 taps (high quality)
+        // This uses a well distributed taps to allow a gaussian distribution covering a 5*5 kernel
+        // https://mynameismjp.wordpress.com/2013/09/10/shadow-maps/
+        float computeShadowWithCSMPCF5(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArrayShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float frustumEdgeFalloff)
+        {
+            vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
+            vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
+
+            if (uvDepth.x < 0. || uvDepth.x > 1.0 || uvDepth.y < 0. || uvDepth.y > 1.0)
+            {
+                return 1.0;
+            }
+
+            uvDepth.z = clamp(uvDepth.z, 0., GREATEST_LESS_THAN_ONE);
+
+            vec2 uv = uvDepth.xy * shadowMapSizeAndInverse.x;	// uv in texel units
+            uv += 0.5;											// offset of half to be in the center of the texel
+            vec2 st = fract(uv);								// how far from the center
+            vec2 base_uv = floor(uv) - 0.5;						// texel coord
+            base_uv *= shadowMapSizeAndInverse.y;				// move back to uv coords
+
+            // Equation resolved to fit in a 5*5 distribution like 
+            // 1 2 4 2 1
+            vec2 uvw0 = 4. - 3. * st;
+            vec2 uvw1 = vec2(7.);
+            vec2 uvw2 = 1. + 3. * st;
+
+            vec3 u = vec3((3. - 2. * st.x) / uvw0.x - 2., (3. + st.x) / uvw1.x, st.x / uvw2.x + 2.) * shadowMapSizeAndInverse.y;
+            vec3 v = vec3((3. - 2. * st.y) / uvw0.y - 2., (3. + st.y) / uvw1.y, st.y / uvw2.y + 2.) * shadowMapSizeAndInverse.y;
+
+            float shadow = 0.;
+            shadow += uvw0.x * uvw0.y * texture2D(shadowSampler, vec4(base_uv.xy + vec2(u[0], v[0]), layer, uvDepth.z));
+            shadow += uvw1.x * uvw0.y * texture2D(shadowSampler, vec4(base_uv.xy + vec2(u[1], v[0]), layer, uvDepth.z));
+            shadow += uvw2.x * uvw0.y * texture2D(shadowSampler, vec4(base_uv.xy + vec2(u[2], v[0]), layer, uvDepth.z));
+            shadow += uvw0.x * uvw1.y * texture2D(shadowSampler, vec4(base_uv.xy + vec2(u[0], v[1]), layer, uvDepth.z));
+            shadow += uvw1.x * uvw1.y * texture2D(shadowSampler, vec4(base_uv.xy + vec2(u[1], v[1]), layer, uvDepth.z));
+            shadow += uvw2.x * uvw1.y * texture2D(shadowSampler, vec4(base_uv.xy + vec2(u[2], v[1]), layer, uvDepth.z));
+            shadow += uvw0.x * uvw2.y * texture2D(shadowSampler, vec4(base_uv.xy + vec2(u[0], v[2]), layer, uvDepth.z));
+            shadow += uvw1.x * uvw2.y * texture2D(shadowSampler, vec4(base_uv.xy + vec2(u[1], v[2]), layer, uvDepth.z));
+            shadow += uvw2.x * uvw2.y * texture2D(shadowSampler, vec4(base_uv.xy + vec2(u[2], v[2]), layer, uvDepth.z));
+            shadow = shadow / 144.;
+
+            shadow = mix(darkness, 1., shadow);
+            return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
+        }
+
         // Shadow PCF kernel size 1 with a single tap (lowest quality)
         float computeShadowWithPCF1(vec4 vPositionFromLight, float depthMetric, sampler2DShadow shadowSampler, float darkness, float frustumEdgeFalloff)
         {
@@ -458,6 +595,71 @@
         // It uses 16 Taps for search and a 32 PCF taps in a randomly rotating poisson sampling disc.
         // This is heavily inspired from http://developer.download.nvidia.com/shaderlibrary/docs/shadow_PCSS.pdf
         // and http://developer.download.nvidia.com/whitepapers/2008/PCSS_Integration.pdf
+        float computeShadowWithCSMPCSS(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArray depthSampler, highp sampler2DArrayShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff, int searchTapCount, int pcfTapCount, vec3[64] poissonSamplers, vec2 lightSizeUVCorrection, float depthCorrection, float penumbraDarkness)
+        {
+            vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
+            vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
+
+            if (uvDepth.x < 0. || uvDepth.x > 1.0 || uvDepth.y < 0. || uvDepth.y > 1.0)
+            {
+                return 1.0;
+            }
+
+            uvDepth.z = clamp(uvDepth.z, 0., GREATEST_LESS_THAN_ONE);
+
+            vec4 uvDepthLayer = vec4(uvDepth.x, uvDepth.y, layer, uvDepth.z);
+
+            float blockerDepth = 0.0;
+            float sumBlockerDepth = 0.0;
+            float numBlocker = 0.0;
+            for (int i = 0; i < searchTapCount; i ++) {
+                blockerDepth = texture(depthSampler, vec3(uvDepth.xy + (lightSizeUV * lightSizeUVCorrection * shadowMapSizeInverse * PoissonSamplers32[i].xy), layer)).r;
+                if (blockerDepth < depthMetric) {
+                    sumBlockerDepth += blockerDepth;
+                    numBlocker++;
+                }
+            }
+
+            if (numBlocker < 1.0) {
+                return 1.0;
+            }
+            float avgBlockerDepth = sumBlockerDepth / numBlocker;
+
+            // Offset preventing aliasing on contact.
+            float AAOffset = shadowMapSizeInverse * 10.;
+            // Do not dividing by z despite being physically incorrect looks better due to the limited kernel size.
+            // float penumbraRatio = (depthMetric - avgBlockerDepth) / avgBlockerDepth;
+            float penumbraRatio = ((depthMetric - avgBlockerDepth) * depthCorrection + AAOffset);
+            vec4 filterRadius = vec4(penumbraRatio * lightSizeUV * lightSizeUVCorrection * shadowMapSizeInverse, 0., 0.);
+
+            float random = getRand(vPositionFromLight.xy);
+            float rotationAngle = random * 3.1415926;
+            vec2 rotationVector = vec2(cos(rotationAngle), sin(rotationAngle));
+
+            float shadow = 0.;
+            for (int i = 0; i < pcfTapCount; i++) {
+                vec4 offset = vec4(poissonSamplers[i], 0.);
+                // Rotated offset.
+                offset = vec4(offset.x * rotationVector.x - offset.y * rotationVector.y, offset.y * rotationVector.x + offset.x * rotationVector.y, 0., 0.);
+                shadow += texture2D(shadowSampler, uvDepthLayer + offset * filterRadius);
+            }
+            shadow /= float(pcfTapCount);
+
+            // Blocker distance falloff
+            shadow = mix(shadow, 1., min((depthMetric - avgBlockerDepth) * depthCorrection * penumbraDarkness, 1.));
+
+            // Apply darkness
+            shadow = mix(darkness, 1., shadow);
+
+            // Apply light frustrum fallof
+            return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
+        }
+
+        // PCSS
+        // This helps to achieve a contact hardening effect on the shadow
+        // It uses 16 Taps for search and a 32 PCF taps in a randomly rotating poisson sampling disc.
+        // This is heavily inspired from http://developer.download.nvidia.com/shaderlibrary/docs/shadow_PCSS.pdf
+        // and http://developer.download.nvidia.com/whitepapers/2008/PCSS_Integration.pdf
         float computeShadowWithPCSS(vec4 vPositionFromLight, float depthMetric, sampler2D depthSampler, sampler2DShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff, int searchTapCount, int pcfTapCount, vec3[64] poissonSamplers)
         {
             if (depthMetric > 1.0 || depthMetric < 0.0) {
@@ -527,5 +729,20 @@
         {
             return computeShadowWithPCSS(vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 32, 64, PoissonSamplers64);
         }
+
+        float computeShadowWithCSMPCSS16(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArray depthSampler, highp sampler2DArrayShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff, vec2 lightSizeUVCorrection, float depthCorrection, float penumbraDarkness)
+        {
+            return computeShadowWithCSMPCSS(layer, vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 16, 16, PoissonSamplers32, lightSizeUVCorrection, depthCorrection, penumbraDarkness);
+        }
+
+        float computeShadowWithCSMPCSS32(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArray depthSampler, highp sampler2DArrayShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff, vec2 lightSizeUVCorrection, float depthCorrection, float penumbraDarkness)
+        {
+            return computeShadowWithCSMPCSS(layer, vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 16, 32, PoissonSamplers32, lightSizeUVCorrection, depthCorrection, penumbraDarkness);
+        }
+
+        float computeShadowWithCSMPCSS64(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArray depthSampler, highp sampler2DArrayShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff, vec2 lightSizeUVCorrection, float depthCorrection, float penumbraDarkness)
+        {
+            return computeShadowWithCSMPCSS(layer, vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 32, 64, PoissonSamplers64, lightSizeUVCorrection, depthCorrection, penumbraDarkness);
+        }
     #endif
 #endif

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

@@ -1,5 +1,11 @@
 #ifdef SHADOWS
-	#if defined(SHADOW{X}) && !defined(SHADOWCUBE{X})
+	#if defined(SHADOWCSM{X})
+		vPositionFromCamera{X} = view * worldPos;
+		for (int i = 0; i < SHADOWCSMNUM_CASCADES{X}; i++) {
+			vPositionFromLight{X}[i] = lightMatrix{X}[i] * worldPos;
+			vDepthMetric{X}[i] = ((vPositionFromLight{X}[i].z + light{X}.depthValues.x) / (light{X}.depthValues.y));
+		}
+	#elif 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

+ 9 - 0
src/Shaders/shadowMap.fragment.fx

@@ -12,6 +12,10 @@ uniform sampler2D diffuseSampler;
 uniform vec3 biasAndScale;
 uniform vec2 depthValues;
 
+#ifdef DEPTHCLAMP
+varying float z;
+#endif
+
 void main(void)
 {
 #ifdef ALPHATEST
@@ -21,6 +25,11 @@ void main(void)
 
     float depth = vDepthMetric;
 
+#ifdef DEPTHCLAMP
+    depth = clamp(((z + depthValues.x) / (depthValues.y)) + biasAndScale.x, 0.0, 1.0);
+    gl_FragDepth = depth;
+#endif
+
 #ifdef ESM
     depth = clamp(exp(-min(87., biasAndScale.z * depth)), 0., 1.);
 #endif

+ 9 - 0
src/Shaders/shadowMap.vertex.fx

@@ -32,6 +32,10 @@ attribute vec2 uv2;
 #endif
 #endif
 
+#ifdef DEPTHCLAMP
+varying float z;
+#endif
+
 void main(void)
 {
 vec3 positionUpdated = position;
@@ -78,8 +82,13 @@ gl_Position = viewProjection * worldPos;
     gl_Position.z += biasAndScale.x * gl_Position.w;
 #endif
 
+#ifdef DEPTHCLAMP
+    z = gl_Position.z;
+    gl_Position.z = 0.0;
+#else
     // Color Texture Linear bias.
     vDepthMetric = ((gl_Position.z + depthValues.x) / (depthValues.y)) + biasAndScale.x;
+#endif
 
 #ifdef ALPHATEST
     #ifdef UV1