瀏覽代碼

Merge pull request #8154 from Popov72/fix-shadowonlymaterial-with-csm

Draft: inline functions in shader code
David Catuhe 5 年之前
父節點
當前提交
6adcf34c72

+ 2 - 1
src/Engines/Processors/index.ts

@@ -5,4 +5,5 @@ export * from "./shaderCodeCursor";
 export * from "./shaderCodeNode";
 export * from "./shaderCodeTestNode";
 export * from "./shaderProcessingOptions";
-export * from "./shaderProcessor";
+export * from "./shaderProcessor";
+export * from "./shaderCodeInliner";

+ 408 - 0
src/Engines/Processors/shaderCodeInliner.ts

@@ -0,0 +1,408 @@
+interface IInlineFunctionDescr {
+    name: string;
+    type: string;
+    parameters: string[];
+    body: string;
+    callIndex: number;
+}
+
+/** @hidden */
+export class ShaderCodeInliner {
+
+    static readonly InlineToken = "#define inline";
+    static readonly RegexpFindFunctionNameAndType = /(?<=\s+?(\w+)\s+(\w+)\s*?)$/;
+
+    private _sourceCode: string;
+    private _functionDescr: IInlineFunctionDescr[];
+    private _numMaxIterations: number;
+
+    public debug: boolean = false;
+
+    public get code(): string {
+        return this._sourceCode;
+    }
+
+    constructor(sourceCode: string, numMaxIterations = 20) {
+        this._sourceCode = sourceCode;
+        this._numMaxIterations = numMaxIterations;
+        this._functionDescr = [];
+    }
+
+    public processCode() {
+        if (this.debug) {
+            console.log(`Start inlining process (code size=${this._sourceCode.length})...`);
+        }
+        this._collectFunctions();
+        this._processInlining(this._numMaxIterations);
+        if (this.debug) {
+            console.log("End of inlining process.");
+        }
+    }
+
+    private _collectFunctions() {
+        let startIndex = 0;
+
+        while (startIndex < this._sourceCode.length) {
+            // locate the function to inline and extract its name
+            const inlineTokenIndex = this._sourceCode.indexOf(ShaderCodeInliner.InlineToken, startIndex);
+            if (inlineTokenIndex < 0) {
+                break;
+            }
+
+            const funcParamsStartIndex = this._sourceCode.indexOf("(", inlineTokenIndex + ShaderCodeInliner.InlineToken.length);
+            if (funcParamsStartIndex < 0) {
+                if (this.debug) {
+                    console.warn(`Could not find the opening parenthesis after the token. startIndex=${startIndex}`);
+                }
+                startIndex = inlineTokenIndex + ShaderCodeInliner.InlineToken.length;
+                continue;
+            }
+
+            const funcNameMatch = ShaderCodeInliner.RegexpFindFunctionNameAndType.exec(this._sourceCode.substring(inlineTokenIndex + ShaderCodeInliner.InlineToken.length, funcParamsStartIndex));
+            if (!funcNameMatch) {
+                if (this.debug) {
+                    console.warn(`Could not extract the name/type of the function from: ${this._sourceCode.substring(inlineTokenIndex + ShaderCodeInliner.InlineToken.length, funcParamsStartIndex)}`);
+                }
+                startIndex = inlineTokenIndex + ShaderCodeInliner.InlineToken.length;
+                continue;
+            }
+            const [funcType, funcName] = [funcNameMatch[1], funcNameMatch[2]];
+
+            // extract the parameters of the function as a whole string (without the leading / trailing parenthesis)
+            const funcParamsEndIndex = this._extractBetweenMarkers('(', ')', this._sourceCode, funcParamsStartIndex);
+            if (funcParamsEndIndex < 0) {
+                if (this.debug) {
+                    console.warn(`Could not extract the parameters the function '${funcName}' (type=${funcType}). funcParamsStartIndex=${funcParamsStartIndex}`);
+                }
+                startIndex = inlineTokenIndex + ShaderCodeInliner.InlineToken.length;
+                continue;
+            }
+            const funcParams = this._sourceCode.substring(funcParamsStartIndex + 1, funcParamsEndIndex);
+
+            // extract the body of the function (with the curly brackets)
+            const funcBodyStartIndex = this._skipWhitespaces(this._sourceCode, funcParamsEndIndex + 1);
+            if (funcBodyStartIndex === this._sourceCode.length) {
+                if (this.debug) {
+                    console.warn(`Could not extract the body of the function '${funcName}' (type=${funcType}). funcParamsEndIndex=${funcParamsEndIndex}`);
+                }
+                startIndex = inlineTokenIndex + ShaderCodeInliner.InlineToken.length;
+                continue;
+            }
+
+            const funcBodyEndIndex = this._extractBetweenMarkers('{', '}', this._sourceCode, funcBodyStartIndex);
+            if (funcBodyEndIndex < 0) {
+                if (this.debug) {
+                    console.warn(`Could not extract the body of the function '${funcName}' (type=${funcType}). funcBodyStartIndex=${funcBodyStartIndex}`);
+                }
+                startIndex = inlineTokenIndex + ShaderCodeInliner.InlineToken.length;
+                continue;
+            }
+            const funcBody = this._sourceCode.substring(funcBodyStartIndex, funcBodyEndIndex + 1);
+
+            // process the parameters: extract each names
+            const params = this._removeComments(funcParams).split(",");
+            const paramNames = [];
+
+            for (let p = 0; p < params.length; ++p) {
+                const param = params[p].trim();
+                const idx = param.lastIndexOf(" ");
+
+                if (idx >= 0) {
+                    paramNames.push(param.substring(idx + 1));
+                }
+            }
+
+            if (funcType !== 'void') {
+                // for functions that return a value, we will replace "return" by "tempvarname = ", tempvarname being a unique generated name
+                paramNames.push('return');
+            }
+
+            // collect the function
+            this._functionDescr.push({
+                "name": funcName,
+                "type": funcType,
+                "parameters": paramNames,
+                "body": funcBody,
+                "callIndex": 0,
+            });
+
+            startIndex = funcBodyEndIndex + 1;
+
+            // remove the function from the source code
+            const partBefore = inlineTokenIndex > 0 ? this._sourceCode.substring(0, inlineTokenIndex) : "";
+            const partAfter = funcBodyEndIndex + 1 < this._sourceCode.length - 1 ? this._sourceCode.substring(funcBodyEndIndex + 1) : "";
+
+            this._sourceCode = partBefore + partAfter;
+
+            startIndex -= funcBodyEndIndex + 1 - inlineTokenIndex;
+        }
+
+        if (this.debug) {
+            console.log(`Collect functions: ${this._functionDescr.length} functions found. functionDescr=`, this._functionDescr);
+        }
+    }
+
+    private _processInlining(numMaxIterations: number = 20): boolean {
+        while (numMaxIterations-- >= 0) {
+            if (!this._replaceFunctionCallsByCode()) {
+                break;
+            }
+        }
+
+        if (this.debug) {
+            console.log(`numMaxIterations is ${numMaxIterations} after inlining process`);
+        }
+
+        return numMaxIterations >= 0;
+    }
+
+    private _extractBetweenMarkers(markerOpen: string, markerClose: string, block: string, startIndex: number): number {
+        let currPos = startIndex,
+            openMarkers = 0,
+            waitForChar = '';
+
+        while (currPos < block.length) {
+            let currChar = block.charAt(currPos);
+
+            if (!waitForChar) {
+                switch (currChar) {
+                    case markerOpen:
+                        openMarkers++;
+                        break;
+                    case markerClose:
+                        openMarkers--;
+                        break;
+                    case '"':
+                    case "'":
+                    case "`":
+                        waitForChar = currChar;
+                        break;
+                    case '/':
+                        if (currPos + 1 < block.length) {
+                            const nextChar = block.charAt(currPos + 1);
+                            if (nextChar === '/') {
+                                waitForChar = '\n';
+                            } else if (nextChar === '*') {
+                                waitForChar = '*/';
+                            }
+                        }
+                        break;
+                }
+            } else {
+                if (currChar === waitForChar) {
+                    if (waitForChar === '"' || waitForChar === "'") {
+                        block.charAt(currPos - 1) !== '\\' && (waitForChar = '');
+                    } else {
+                        waitForChar = '';
+                    }
+                } else if (waitForChar === '*/' && currChar === '*' && currPos + 1 < block.length) {
+                    block.charAt(currPos + 1) === '/' && (waitForChar = '');
+                    if (waitForChar === '') {
+                        currPos++;
+                    }
+                }
+            }
+
+            currPos++ ;
+            if (openMarkers === 0) {
+                break;
+            }
+        }
+
+        return openMarkers === 0 ? currPos - 1 : -1;
+    }
+
+    private _skipWhitespaces(s: string, index: number): number {
+        while (index < s.length) {
+            const c = s[index];
+            if (c !== ' ' && c !== '\n' && c !== '\r' && c !== '\t' && c !== '\u000a' && c !== '\u00a0') {
+                break;
+            }
+            index++;
+        }
+
+        return index;
+    }
+
+    private _removeComments(block: string): string {
+        let currPos = 0,
+            waitForChar = '',
+            inComments = false,
+            s = [];
+
+        while (currPos < block.length) {
+            let currChar = block.charAt(currPos);
+
+            if (!waitForChar) {
+                switch (currChar) {
+                    case '"':
+                    case "'":
+                    case "`":
+                        waitForChar = currChar;
+                        break;
+                    case '/':
+                        if (currPos + 1 < block.length) {
+                            const nextChar = block.charAt(currPos + 1);
+                            if (nextChar === '/') {
+                                waitForChar = '\n';
+                                inComments = true;
+                            } else if (nextChar === '*') {
+                                waitForChar = '*/';
+                                inComments = true;
+                            }
+                        }
+                        break;
+                }
+                if (!inComments) {
+                    s.push(currChar);
+                }
+            } else {
+                if (currChar === waitForChar) {
+                    if (waitForChar === '"' || waitForChar === "'") {
+                        block.charAt(currPos - 1) !== '\\' && (waitForChar = '');
+                        s.push(currChar);
+                    } else {
+                        waitForChar = '';
+                        inComments = false;
+                    }
+                } else if (waitForChar === '*/' && currChar === '*' && currPos + 1 < block.length) {
+                    block.charAt(currPos + 1) === '/' && (waitForChar = '');
+                    if (waitForChar === '') {
+                        inComments = false;
+                        currPos++;
+                    }
+                } else {
+                    if (!inComments) {
+                        s.push(currChar);
+                    }
+                }
+            }
+
+            currPos++ ;
+        }
+
+        return s.join('');
+    }
+
+    private _replaceFunctionCallsByCode(): boolean {
+        let doAgain = false;
+
+        for (const func of this._functionDescr) {
+            const { name, type, parameters, body } = func;
+
+            let startIndex = 0;
+
+            while (startIndex < this._sourceCode.length) {
+                // Look for the function name in the source code
+                const functionCallIndex = this._sourceCode.indexOf(name, startIndex);
+
+                if (functionCallIndex < 0) {
+                    break;
+                }
+
+                // Find the opening parenthesis
+                const callParamsStartIndex = this._skipWhitespaces(this._sourceCode, functionCallIndex + name.length);
+                if (callParamsStartIndex === this._sourceCode.length || this._sourceCode.charAt(callParamsStartIndex) !== '(') {
+                    startIndex = functionCallIndex + name.length;
+                    continue;
+                }
+
+                // extract the parameters of the function call as a whole string (without the leading / trailing parenthesis)
+                const callParamsEndIndex = this._extractBetweenMarkers('(', ')', this._sourceCode, callParamsStartIndex);
+                if (callParamsEndIndex < 0) {
+                    if (this.debug) {
+                        console.warn(`Could not extract the parameters of the function call. Function '${name}' (type=${type}). callParamsStartIndex=${callParamsStartIndex}`);
+                    }
+                    startIndex = functionCallIndex + name.length;
+                    continue;
+                }
+                const callParams = this._sourceCode.substring(callParamsStartIndex + 1, callParamsEndIndex);
+
+                // process the parameter call: extract each names
+                const params = this._removeComments(callParams).split(",");
+                const paramNames = [];
+
+                for (let p = 0; p < params.length; ++p) {
+                    const param = params[p].trim();
+                    paramNames.push(param);
+                }
+
+                const retParamName = type !== 'void' ? name + '_' + (func.callIndex++) : null;
+
+                if (retParamName) {
+                    paramNames.push(retParamName + ' =');
+                }
+
+                if (paramNames.length !== parameters.length) {
+                    if (this.debug) {
+                        console.warn(`Invalid function call: not the same number of parameters for the call than the number expected by the function. Function '${name}' (type=${type}). function parameters=${parameters}, call parameters=${paramNames}`);
+                    }
+                    startIndex = functionCallIndex + name.length;
+                    continue;
+                }
+
+                startIndex = callParamsEndIndex + 1;
+
+                // replace the function call by the body function
+                const funcBody = this._replaceNames(body, parameters, paramNames);
+
+                let partBefore = functionCallIndex > 0 ? this._sourceCode.substring(0, functionCallIndex) : "";
+                let partAfter = callParamsEndIndex + 1 < this._sourceCode.length - 1 ? this._sourceCode.substring(callParamsEndIndex + 1) : "";
+
+                if (retParamName) {
+                    // case where the function returns a value. We generate:
+                    // FUNCTYPE retParamName;
+                    // {function body}
+                    // and replace the function call by retParamName
+                    const injectDeclarationIndex = this._findBackward(this._sourceCode, functionCallIndex - 1, '\n');
+
+                    partBefore = this._sourceCode.substring(0, injectDeclarationIndex + 1);
+                    let partBetween = this._sourceCode.substring(injectDeclarationIndex + 1, functionCallIndex);
+
+                    this._sourceCode = partBefore + type + " " + retParamName + ";\n" + funcBody + "\n" + partBetween + retParamName + partAfter;
+
+                    if (this.debug) {
+                        console.log(`Replace function call by code. Function '${name}' (type=${type}). injectDeclarationIndex=${injectDeclarationIndex}`);
+                    }
+                } else {
+                    // simple case where the return value of the function is "void"
+                    this._sourceCode = partBefore + funcBody + partAfter;
+
+                    startIndex += funcBody.length - (callParamsEndIndex + 1 - functionCallIndex);
+
+                    if (this.debug) {
+                        console.log(`Replace function call by code. Function '${name}' (type=${type}). functionCallIndex=${functionCallIndex}`);
+                    }
+                }
+
+                doAgain = true;
+            }
+        }
+
+        return doAgain;
+    }
+
+    private _findBackward(s: string, index: number, c: string): number {
+        while (index >= 0 && s.charAt(index) !== c) {
+            index--;
+        }
+
+        return index;
+    }
+
+    private _escapeRegExp(s: string): string {
+        return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+    }
+
+    private _replaceNames(code: string, sources: string[], destinations: string[]): string {
+
+        for (let i = 0; i < sources.length; ++i) {
+            const source = new RegExp(this._escapeRegExp(sources[i]), 'g'),
+                  destination = destinations[i];
+
+            code = code.replace(source, destination);
+        }
+
+        return code;
+    }
+}

+ 1 - 0
src/Shaders/ShadersInclude/bonesDeclaration.fx

@@ -14,6 +14,7 @@
 	#endif
 
 	#ifdef BONETEXTURE
+        #define inline
 		mat4 readMatrixFromRawSampler(sampler2D smp, float index)
 		{
 			float offset = index  * 4.0;	

+ 1 - 0
src/Shaders/ShadersInclude/imageProcessingFunctions.fx

@@ -4,6 +4,7 @@
 	* sampler3dSetting.x = textureOffset (0.5 / textureSize).
 	* sampler3dSetting.y = textureSize.
 	*/
+    #define inline
 	vec3 sampleTexture3D(sampler2D colorTransform, vec3 color, vec2 sampler3dSetting)
 	{
 		float sliceSize = 2.0 * sampler3dSetting.x; // Size of 1 slice relative to the texture, for example 1/8

+ 1 - 0
src/Shaders/ShadersInclude/lightsFragmentFunctions.fx

@@ -109,6 +109,7 @@ lightingInfo computeHemisphericLighting(vec3 viewDirectionW, vec3 vNormal, vec4
 		return result;
 }
 
+#define inline
 vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler, mat4 textureProjectionMatrix){
 	vec4 strq = textureProjectionMatrix * vec4(vPositionW, 1.0);
 	strq /= strq.w;

+ 1 - 0
src/Shaders/ShadersInclude/pbrBlockClearcoat.fx

@@ -29,6 +29,7 @@ struct clearcoatOutParams
 };
 
 #ifdef CLEARCOAT
+    #define inline
     void clearcoatBlock(
         const in vec3 vPositionW,
         const in vec3 geometricNormalW,

+ 2 - 0
src/Shaders/ShadersInclude/pbrBlockReflection.fx

@@ -52,6 +52,7 @@
         #endif
     }
 
+    #define inline
     void sampleReflectionTexture(
         const in float alphaG,
         const in vec3 vReflectionMicrosurfaceInfos,
@@ -146,6 +147,7 @@
         environmentRadiance.rgb *= vReflectionColor.rgb;
     }
 
+    #define inline
     void reflectionBlock(
         const in vec3 vPositionW,
         const in vec3 normalW,

+ 1 - 0
src/Shaders/ShadersInclude/pbrBlockSheen.fx

@@ -19,6 +19,7 @@
     #endif
     };
 
+    #define inline
     void sheenBlock(
         const in vec4 vSheenColor,
     #ifdef SHEEN_ROUGHNESS

+ 1 - 0
src/Shaders/ShadersInclude/pbrBlockSubSurface.fx

@@ -25,6 +25,7 @@ struct subSurfaceOutParams
 };
 
 #ifdef SUBSURFACE
+    #define inline
     void subSurfaceBlock(
         const in vec3 vSubSurfaceIntensity,
         const in vec2 vThicknessParam,

+ 1 - 0
src/Shaders/ShadersInclude/pbrDirectLightingFunctions.fx

@@ -39,6 +39,7 @@ vec3 computeDiffuseLighting(preLightingInfo info, vec3 lightColor) {
     return diffuseTerm * info.attenuation * info.NdotL * lightColor;
 }
 
+#define inline
 vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler, mat4 textureProjectionMatrix){
     vec4 strq = textureProjectionMatrix * vec4(vPositionW, 1.0);
     strq /= strq.w;

+ 234 - 201
src/Shaders/ShadersInclude/shadowsFragmentFunctions.fx

@@ -14,6 +14,7 @@
         return mix(value, 1.0, mask);
     }
 
+    #define inline
     float computeShadowCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, vec2 depthValues)
     {
         vec3 directionToLight = vPositionW - lightPosition;
@@ -30,13 +31,10 @@
             float shadow = textureCube(shadowSampler, directionToLight).x;
         #endif
 
-        if (depth > shadow)
-        {
-            return darkness;
-        }
-        return 1.0;
+        return depth > shadow ? darkness : 1.0;
     }
 
+    #define inline
     float computeShadowWithPoissonSamplingCube(vec3 lightPosition, samplerCube shadowSampler, float mapSize, float darkness, vec2 depthValues)
     {
         vec3 directionToLight = vPositionW - lightPosition;
@@ -72,6 +70,7 @@
         return  min(1.0, visibility + darkness);
     }
 
+    #define inline
     float computeShadowWithESMCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, float depthScale, vec2 depthValues)
     {
         vec3 directionToLight = vPositionW - lightPosition;
@@ -92,6 +91,7 @@
         return esm;
     }
 
+    #define inline
     float computeShadowWithCloseESMCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, float depthScale, vec2 depthValues)
     {
         vec3 directionToLight = vPositionW - lightPosition;
@@ -114,6 +114,7 @@
     }
 
     #ifdef WEBGL2
+        #define inline
         float computeShadowCSM(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArray shadowSampler, float darkness, float frustumEdgeFalloff)
         {
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -128,14 +129,11 @@
                 float shadow = texture2D(shadowSampler, uvLayer).x;
             #endif
 
-            if (shadowPixelDepth > shadow)
-            {
-                return computeFallOff(darkness, clipSpace.xy, frustumEdgeFalloff);
-            }
-            return 1.;
+            return shadowPixelDepth > shadow ? computeFallOff(darkness, clipSpace.xy, frustumEdgeFalloff) : 1.;
         }
     #endif
 
+    #define inline
     float computeShadow(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float darkness, float frustumEdgeFalloff)
     {
         vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -145,22 +143,21 @@
         {
             return 1.0;
         }
+        else
+        {
+            float shadowPixelDepth = clamp(depthMetric, 0., 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
+            #ifndef SHADOWFLOAT
+                float shadow = unpack(texture2D(shadowSampler, uv));
+            #else
+                float shadow = texture2D(shadowSampler, uv).x;
+            #endif
 
-        if (shadowPixelDepth > shadow)
-        {
-            return computeFallOff(darkness, clipSpace.xy, frustumEdgeFalloff);
+            return shadowPixelDepth > shadow ? computeFallOff(darkness, clipSpace.xy, frustumEdgeFalloff) : 1.;
         }
-        return 1.;
     }
 
+    #define inline
     float computeShadowWithPoissonSampling(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float mapSize, float darkness, float frustumEdgeFalloff)
     {
         vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -170,34 +167,37 @@
         {
             return 1.0;
         }
+        else
+        {
+            float shadowPixelDepth = clamp(depthMetric, 0., 1.0);
 
-        float shadowPixelDepth = clamp(depthMetric, 0., 1.0);
-
-        float visibility = 1.;
+            float visibility = 1.;
 
-        vec2 poissonDisk[4];
-        poissonDisk[0] = vec2(-0.94201624, -0.39906216);
-        poissonDisk[1] = vec2(0.94558609, -0.76890725);
-        poissonDisk[2] = vec2(-0.094184101, -0.92938870);
-        poissonDisk[3] = vec2(0.34495938, 0.29387760);
+            vec2 poissonDisk[4];
+            poissonDisk[0] = vec2(-0.94201624, -0.39906216);
+            poissonDisk[1] = vec2(0.94558609, -0.76890725);
+            poissonDisk[2] = vec2(-0.094184101, -0.92938870);
+            poissonDisk[3] = vec2(0.34495938, 0.29387760);
 
-        // Poisson Sampling
+            // Poisson Sampling
 
-        #ifndef SHADOWFLOAT
-            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 < 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
+            #ifndef SHADOWFLOAT
+                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 < 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 computeFallOff(min(1.0, visibility + darkness), clipSpace.xy, frustumEdgeFalloff);
+            return computeFallOff(min(1.0, visibility + darkness), clipSpace.xy, frustumEdgeFalloff);
+        }
     }
 
+    #define inline
     float computeShadowWithESM(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float darkness, float depthScale, float frustumEdgeFalloff)
     {
         vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -207,20 +207,23 @@
         {
             return 1.0;
         }
+        else
+        {
+            float shadowPixelDepth = clamp(depthMetric, 0., 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, 0., 1. - darkness);
+            #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, 0., 1. - darkness);
 
-        return computeFallOff(esm, clipSpace.xy, frustumEdgeFalloff);
+            return computeFallOff(esm, clipSpace.xy, frustumEdgeFalloff);
+        }
     }
 
+    #define inline
     float computeShadowWithCloseESM(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float darkness, float depthScale, float frustumEdgeFalloff)
     {
         vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -230,24 +233,27 @@
         {
             return 1.0;
         }
+        else
+        {
+            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 = clamp(exp(min(87., -depthScale * (shadowPixelDepth - shadowMapSample))), darkness, 1.);
 
-        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 = clamp(exp(min(87., -depthScale * (shadowPixelDepth - shadowMapSample))), darkness, 1.);
-
-        return computeFallOff(esm, clipSpace.xy, frustumEdgeFalloff);
+            return computeFallOff(esm, clipSpace.xy, frustumEdgeFalloff);
+        }
     }
 
     #ifdef WEBGL2
         #define GREATEST_LESS_THAN_ONE 0.99999994
 
         // Shadow PCF kernel size 1 with a single tap (lowest quality)
+        #define inline
         float computeShadowWithCSMPCF1(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArrayShadow shadowSampler, float darkness, float frustumEdgeFalloff)
         {
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -265,6 +271,7 @@
         // 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/
+        #define inline
         float computeShadowWithCSMPCF3(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArrayShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float frustumEdgeFalloff)
         {
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -301,6 +308,7 @@
         // 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/
+        #define inline
         float computeShadowWithCSMPCF5(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArrayShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float frustumEdgeFalloff)
         {
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -340,99 +348,108 @@
         }
 
         // Shadow PCF kernel size 1 with a single tap (lowest quality)
+        #define inline
         float computeShadowWithPCF1(vec4 vPositionFromLight, float depthMetric, sampler2DShadow shadowSampler, float darkness, float frustumEdgeFalloff)
         {
             if (depthMetric > 1.0 || depthMetric < 0.0) {
                 return 1.0;
             }
+            else
+            {
+                vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
+                vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
 
-            vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
-            vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
-
-            float shadow = texture2D(shadowSampler, uvDepth);
-            shadow = mix(darkness, 1., shadow);
-            return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
+                float shadow = texture2D(shadowSampler, uvDepth);
+                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/
+        #define inline
         float computeShadowWithPCF3(vec4 vPositionFromLight, float depthMetric, sampler2DShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float frustumEdgeFalloff)
         {
             if (depthMetric > 1.0 || depthMetric < 0.0) {
                 return 1.0;
             }
-
-            vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
-            vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
-
-            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, vec3(base_uv.xy + vec2(u[0], v[0]), uvDepth.z));
-            shadow += uvw1.x * uvw0.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[0]), uvDepth.z));
-            shadow += uvw0.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[0], v[1]), uvDepth.z));
-            shadow += uvw1.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[1]), uvDepth.z));
-            shadow = shadow / 16.;
-
-            shadow = mix(darkness, 1., shadow);
-            return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
+            else
+            {
+                vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
+                vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
+
+                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, vec3(base_uv.xy + vec2(u[0], v[0]), uvDepth.z));
+                shadow += uvw1.x * uvw0.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[0]), uvDepth.z));
+                shadow += uvw0.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[0], v[1]), uvDepth.z));
+                shadow += uvw1.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[1]), 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/
+        #define inline
         float computeShadowWithPCF5(vec4 vPositionFromLight, float depthMetric, sampler2DShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float frustumEdgeFalloff)
         {
             if (depthMetric > 1.0 || depthMetric < 0.0) {
                 return 1.0;
             }
-
-            vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
-            vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
-
-            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, vec3(base_uv.xy + vec2(u[0], v[0]), uvDepth.z));
-            shadow += uvw1.x * uvw0.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[0]), uvDepth.z));
-            shadow += uvw2.x * uvw0.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[2], v[0]), uvDepth.z));
-            shadow += uvw0.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[0], v[1]), uvDepth.z));
-            shadow += uvw1.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[1]), uvDepth.z));
-            shadow += uvw2.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[2], v[1]), uvDepth.z));
-            shadow += uvw0.x * uvw2.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[0], v[2]), uvDepth.z));
-            shadow += uvw1.x * uvw2.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[2]), uvDepth.z));
-            shadow += uvw2.x * uvw2.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[2], v[2]), uvDepth.z));
-            shadow = shadow / 144.;
-
-            shadow = mix(darkness, 1., shadow);
-            return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
+            else
+            {
+                vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
+                vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
+
+                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, vec3(base_uv.xy + vec2(u[0], v[0]), uvDepth.z));
+                shadow += uvw1.x * uvw0.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[0]), uvDepth.z));
+                shadow += uvw2.x * uvw0.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[2], v[0]), uvDepth.z));
+                shadow += uvw0.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[0], v[1]), uvDepth.z));
+                shadow += uvw1.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[1]), uvDepth.z));
+                shadow += uvw2.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[2], v[1]), uvDepth.z));
+                shadow += uvw0.x * uvw2.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[0], v[2]), uvDepth.z));
+                shadow += uvw1.x * uvw2.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[2]), uvDepth.z));
+                shadow += uvw2.x * uvw2.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[2], v[2]), uvDepth.z));
+                shadow = shadow / 144.;
+
+                shadow = mix(darkness, 1., shadow);
+                return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
+            }
         }
 
         const vec3 PoissonSamplers32[64] = vec3[64](
@@ -575,6 +592,7 @@
         // 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
+        #define inline
         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;
@@ -598,36 +616,39 @@
             if (numBlocker < 1.0) {
                 return 1.0;
             }
-            float avgBlockerDepth = sumBlockerDepth / numBlocker;
+            else
+            {
+                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);
 
-            // 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.);
+                // Blocker distance falloff
+                shadow = mix(shadow, 1., min((depthMetric - avgBlockerDepth) * depthCorrection * penumbraDarkness, 1.));
 
-            float random = getRand(vPositionFromLight.xy);
-            float rotationAngle = random * 3.1415926;
-            vec2 rotationVector = vec2(cos(rotationAngle), sin(rotationAngle));
+                // Apply darkness
+                shadow = mix(darkness, 1., shadow);
 
-            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);
+                // Apply light frustrum fallof
+                return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
             }
-            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
@@ -635,86 +656,98 @@
         // 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
+        #define inline
         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) {
                 return 1.0;
             }
-
-            vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
-            vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
-
-            float blockerDepth = 0.0;
-            float sumBlockerDepth = 0.0;
-            float numBlocker = 0.0;
-            for (int i = 0; i < searchTapCount; i ++) {
-                blockerDepth = texture(depthSampler, uvDepth.xy + (lightSizeUV * shadowMapSizeInverse * PoissonSamplers32[i].xy)).r;
-                if (blockerDepth < depthMetric) {
-                    sumBlockerDepth += blockerDepth;
-                    numBlocker++;
+            else
+            {
+                vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
+                vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
+
+                float blockerDepth = 0.0;
+                float sumBlockerDepth = 0.0;
+                float numBlocker = 0.0;
+                for (int i = 0; i < searchTapCount; i ++) {
+                    blockerDepth = texture(depthSampler, uvDepth.xy + (lightSizeUV * shadowMapSizeInverse * PoissonSamplers32[i].xy)).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) + AAOffset);
-            float filterRadius = penumbraRatio * lightSizeUV * shadowMapSizeInverse;
-
-            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++) {
-                vec3 offset = poissonSamplers[i];
-                // Rotated offset.
-                offset = vec3(offset.x * rotationVector.x - offset.y * rotationVector.y, offset.y * rotationVector.x + offset.x * rotationVector.y, 0.);
-                shadow += texture2D(shadowSampler, uvDepth + offset * filterRadius);
+                if (numBlocker < 1.0) {
+                    return 1.0;
+                }
+                else
+                {
+                    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) + AAOffset);
+                    float filterRadius = penumbraRatio * lightSizeUV * shadowMapSizeInverse;
+
+                    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++) {
+                        vec3 offset = poissonSamplers[i];
+                        // Rotated offset.
+                        offset = vec3(offset.x * rotationVector.x - offset.y * rotationVector.y, offset.y * rotationVector.x + offset.x * rotationVector.y, 0.);
+                        shadow += texture2D(shadowSampler, uvDepth + offset * filterRadius);
+                    }
+                    shadow /= float(pcfTapCount);
+
+                    // Blocker distance falloff
+                    shadow = mix(shadow, 1., depthMetric - avgBlockerDepth);
+
+                    // Apply darkness
+                    shadow = mix(darkness, 1., shadow);
+
+                    // Apply light frustrum fallof
+                    return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
+                }
             }
-            shadow /= float(pcfTapCount);
-
-            // Blocker distance falloff
-            shadow = mix(shadow, 1., depthMetric - avgBlockerDepth);
-
-            // Apply darkness
-            shadow = mix(darkness, 1., shadow);
-
-            // Apply light frustrum fallof
-            return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
         }
 
+        #define inline
         float computeShadowWithPCSS16(vec4 vPositionFromLight, float depthMetric, sampler2D depthSampler, sampler2DShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff)
         {
             return computeShadowWithPCSS(vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 16, 16, PoissonSamplers32);
         }
 
+        #define inline
         float computeShadowWithPCSS32(vec4 vPositionFromLight, float depthMetric, sampler2D depthSampler, sampler2DShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff)
         {
             return computeShadowWithPCSS(vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 16, 32, PoissonSamplers32);
         }
 
+        #define inline
         float computeShadowWithPCSS64(vec4 vPositionFromLight, float depthMetric, sampler2D depthSampler, sampler2DShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff)
         {
             return computeShadowWithPCSS(vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 32, 64, PoissonSamplers64);
         }
 
+        #define inline
         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);
         }
 
+        #define inline
         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);
         }
 
+        #define inline
         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);