Browse Source

PCF with configurable quality

sebastien 7 years ago
parent
commit
ba799e95c4

+ 56 - 40
src/Lights/Shadows/babylon.shadowGenerator.ts

@@ -81,71 +81,56 @@
      * Documentation: https://doc.babylonjs.com/babylon101/shadows
      * Documentation: https://doc.babylonjs.com/babylon101/shadows
      */
      */
     export class ShadowGenerator implements IShadowGenerator {
     export class ShadowGenerator implements IShadowGenerator {
-        private static _FILTER_NONE = 0;
-        private static _FILTER_EXPONENTIALSHADOWMAP = 1;
-        private static _FILTER_POISSONSAMPLING = 2;
-        private static _FILTER_BLUREXPONENTIALSHADOWMAP = 3;
-        private static _FILTER_CLOSEEXPONENTIALSHADOWMAP = 4;
-        private static _FILTER_BLURCLOSEEXPONENTIALSHADOWMAP = 5;
-        private static _FILTER_PCF = 6;
-
         /**
         /**
          * Shadow generator mode None: no filtering applied.
          * Shadow generator mode None: no filtering applied.
          */
          */
-        public static get FILTER_NONE(): number {
-            return ShadowGenerator._FILTER_NONE;
-        }
-
-        /**
-         * Shadow generator mode Poisson Sampling: Percentage Closer Filtering.
-         * (Multiple Tap around evenly distributed around the pixel are used to evaluate the shadow strength)
-         */
-        public static get FILTER_POISSONSAMPLING(): number {
-            return ShadowGenerator._FILTER_POISSONSAMPLING;
-        }
-
+        public static readonly FILTER_NONE = 0;
         /**
         /**
          * Shadow generator mode ESM: Exponential Shadow Mapping.
          * Shadow generator mode ESM: Exponential Shadow Mapping.
          * (http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf)
          * (http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf)
          */
          */
-        public static get FILTER_EXPONENTIALSHADOWMAP(): number {
-            return ShadowGenerator._FILTER_EXPONENTIALSHADOWMAP;
-        }
-
+        public static readonly FILTER_EXPONENTIALSHADOWMAP = 1;
+        /**
+         * Shadow generator mode Poisson Sampling: Percentage Closer Filtering.
+         * (Multiple Tap around evenly distributed around the pixel are used to evaluate the shadow strength)
+         */
+        public static readonly FILTER_POISSONSAMPLING = 2;
         /**
         /**
          * Shadow generator mode ESM: Blurred Exponential Shadow Mapping.
          * Shadow generator mode ESM: Blurred Exponential Shadow Mapping.
          * (http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf)
          * (http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf)
          */
          */
-        public static get FILTER_BLUREXPONENTIALSHADOWMAP(): number {
-            return ShadowGenerator._FILTER_BLUREXPONENTIALSHADOWMAP;
-        }
-
+        public static readonly FILTER_BLUREXPONENTIALSHADOWMAP = 3;
         /**
         /**
          * Shadow generator mode ESM: Exponential Shadow Mapping using the inverse of the exponential preventing 
          * Shadow generator mode ESM: Exponential Shadow Mapping using the inverse of the exponential preventing 
          * edge artifacts on steep falloff.
          * edge artifacts on steep falloff.
          * (http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf)
          * (http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf)
          */
          */
-        public static get FILTER_CLOSEEXPONENTIALSHADOWMAP(): number {
-            return ShadowGenerator._FILTER_CLOSEEXPONENTIALSHADOWMAP;
-        }
-
+        public static readonly FILTER_CLOSEEXPONENTIALSHADOWMAP = 4;
         /**
         /**
          * Shadow generator mode ESM: Blurred Exponential Shadow Mapping using the inverse of the exponential preventing 
          * Shadow generator mode ESM: Blurred Exponential Shadow Mapping using the inverse of the exponential preventing 
          * edge artifacts on steep falloff.
          * edge artifacts on steep falloff.
          * (http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf)
          * (http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf)
          */
          */
-        public static get FILTER_BLURCLOSEEXPONENTIALSHADOWMAP(): number {
-            return ShadowGenerator._FILTER_BLURCLOSEEXPONENTIALSHADOWMAP;
-        }
-
+        public static readonly FILTER_BLURCLOSEEXPONENTIALSHADOWMAP = 5;
         /**
         /**
          * Shadow generator mode PCF: Percentage Closer Filtering 
          * Shadow generator mode PCF: Percentage Closer Filtering 
          * benefits from Webgl 2 shadow samplers. Fallback to Poisson Sampling in Webgl 1
          * benefits from Webgl 2 shadow samplers. Fallback to Poisson Sampling in Webgl 1
          * (https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch11.html)
          * (https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch11.html)
          */
          */
-        public static get FILTER_PCF(): number {
-            return ShadowGenerator._FILTER_PCF;
-        }
+        public static readonly FILTER_PCF = 6;
+
+        /**
+         * Execute PCF on a 5*5 kernel improving a lot the shadow aliasing artifacts.
+         */
+        public static readonly PCF_HIGH_QUALITY = 0;
+        /**
+         * Execute PCF on a 3*3 kernel being a good tradeoff for quality/perf cross devices.
+         */
+        public static readonly PCF_MEDIUM_QUALITY = 1;
+        /**
+         * Execute PCF on a 1*1 kernel being a the lowest quality but the fastest.
+         */
+        public static readonly PCF_LOW_QUALITY = 2;
 
 
         private _bias = 0.00005;
         private _bias = 0.00005;
         /**
         /**
@@ -283,6 +268,11 @@
                     this.useCloseExponentialShadowMap = true;
                     this.useCloseExponentialShadowMap = true;
                     return;
                     return;
                 }
                 }
+                // PCF on cubemap would also be expensive
+                else if (value === ShadowGenerator.FILTER_PCF) {
+                    this.usePoissonSampling = true;
+                    return;
+                }
             }
             }
 
 
             // Weblg1 fallback for PCF.
             // Weblg1 fallback for PCF.
@@ -442,6 +432,22 @@
             this.filter = (value ? ShadowGenerator.FILTER_PCF : ShadowGenerator.FILTER_NONE);
             this.filter = (value ? ShadowGenerator.FILTER_PCF : ShadowGenerator.FILTER_NONE);
         }
         }
 
 
+        private _percentageCloserFilteringQuality = ShadowGenerator.PCF_HIGH_QUALITY;
+        /**
+         * Gets the PCF Quality.
+         * Only valid if usePercentageCloserFiltering is true.
+         */
+        public get percentageCloserFilteringQuality(): number {
+            return this._percentageCloserFilteringQuality;
+        }
+        /**
+         * Sets the PCF Quality.
+         * Only valid if usePercentageCloserFiltering is true.
+         */
+        public set percentageCloserFilteringQuality(percentageCloserFilteringQuality: number) {
+            this._percentageCloserFilteringQuality = percentageCloserFilteringQuality;
+        }
+
         private _darkness = 0;
         private _darkness = 0;
         /**
         /**
          * Returns the darkness value (float). This can only decrease the actual darkness of a shadow.
          * Returns the darkness value (float). This can only decrease the actual darkness of a shadow.
@@ -1040,6 +1046,13 @@
 
 
             if (this.usePercentageCloserFiltering) {
             if (this.usePercentageCloserFiltering) {
                 defines["SHADOWPCF" + lightIndex] = true;
                 defines["SHADOWPCF" + lightIndex] = true;
+                if (this._percentageCloserFilteringQuality === ShadowGenerator.PCF_LOW_QUALITY) {
+                    defines["SHADOWLOWQUALITY" + lightIndex] = true;
+                }
+                else if (this._percentageCloserFilteringQuality === ShadowGenerator.PCF_MEDIUM_QUALITY) {
+                    defines["SHADOWMEDIUMQUALITY" + lightIndex] = true;
+                }
+                // else default to low.
             }
             }
             else if (this.usePoissonSampling) {
             else if (this.usePoissonSampling) {
                 defines["SHADOWPOISSON" + lightIndex] = true;
                 defines["SHADOWPOISSON" + lightIndex] = true;
@@ -1085,13 +1098,16 @@
                 effect.setMatrix("lightMatrix" + lightIndex, this.getTransformMatrix());
                 effect.setMatrix("lightMatrix" + lightIndex, this.getTransformMatrix());
             }
             }
 
 
+            // Only PCF uses depth stencil texture.
             if (this._useDepthStencilTexture) {
             if (this._useDepthStencilTexture) {
                 effect.setDepthStencilTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering());
                 effect.setDepthStencilTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering());
+                light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), shadowMap.getSize().width, 1 / shadowMap.getSize().width, this.frustumEdgeFalloff, lightIndex);
             }
             }
             else {
             else {
                 effect.setTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering());
                 effect.setTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering());
+                light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), this.blurScale / shadowMap.getSize().width, this.depthScale, this.frustumEdgeFalloff, lightIndex);
             }
             }
-            light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), this.blurScale / shadowMap.getSize().width, this.depthScale, this.frustumEdgeFalloff, lightIndex);
+
             light._uniformBuffer.updateFloat2("depthValues", this.getLight().getDepthMinZ(camera), this.getLight().getDepthMinZ(camera) + this.getLight().getDepthMaxZ(camera), lightIndex);
             light._uniformBuffer.updateFloat2("depthValues", this.getLight().getDepthMinZ(camera), this.getLight().getDepthMinZ(camera) + this.getLight().getDepthMaxZ(camera), lightIndex);
         }
         }
 
 

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

@@ -242,6 +242,8 @@ module BABYLON {
                     defines["SHADOWPOISSON" + lightIndex] = false;
                     defines["SHADOWPOISSON" + lightIndex] = false;
                     defines["SHADOWESM" + lightIndex] = false;
                     defines["SHADOWESM" + lightIndex] = false;
                     defines["SHADOWCUBE" + lightIndex] = false;
                     defines["SHADOWCUBE" + lightIndex] = false;
+                    defines["SHADOWLOWQUALITY" + lightIndex] = false;
+                    defines["SHADOWMEDIUMQUALITY" + lightIndex] = false;
 
 
                     if (mesh && mesh.receiveShadows && scene.shadowsEnabled && light.shadowEnabled) {
                     if (mesh && mesh.receiveShadows && scene.shadowsEnabled && light.shadowEnabled) {
                         var shadowGenerator = light.getShadowGenerator();
                         var shadowGenerator = light.getShadowGenerator();

+ 5 - 3
src/Shaders/ShadersInclude/lightFragment.fx

@@ -50,10 +50,12 @@
 					#endif
 					#endif
 				#else
 				#else
 					#if defined(SHADOWPCF{X})
 					#if defined(SHADOWPCF{X})
-						#if defined(SHADOWCUBE{X})
-							shadow = computeShadowWithPCFCube(light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.depthValues);
+						#if defined(SHADOWLOWQUALITY{X})
+							shadow = computeShadowWithPCF1(vPositionFromLight{X}, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w);
+						#elif defined(SHADOWMEDIUMQUALITY{X})
+							shadow = computeShadowWithPCF3(vPositionFromLight{X}, shadowSampler{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w);
 						#else
 						#else
-							shadow = computeShadowWithPCF(vPositionFromLight{X}, vDepthMetric{X}, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w);
+							shadow = computeShadowWithPCF5(vPositionFromLight{X}, shadowSampler{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w);
 						#endif
 						#endif
 					#else
 					#else
 						#if defined(SHADOWCUBE{X})
 						#if defined(SHADOWCUBE{X})

+ 106 - 42
src/Shaders/ShadersInclude/shadowsFragmentFunctions.fx

@@ -65,26 +65,6 @@
 		return  min(1.0, visibility + darkness);
 		return  min(1.0, visibility + darkness);
 	}
 	}
 
 
-#ifdef WEBGL2
-	float computeShadowWithPCFCube(vec3 lightPosition, samplerCubeShadow shadowSampler, float darkness, vec2 depthValues)
-	{
-		vec4 directionToLight = vec4(vPositionW - lightPosition, 1.0);
-		directionToLight.w = length(directionToLight.xyz);
-		directionToLight.w = (directionToLight.w + depthValues.x) / (depthValues.y);
-		directionToLight.w = clamp(directionToLight.w, 0., 1.0);
-
-		directionToLight.xyz = normalize(directionToLight.xyz);
-		directionToLight.y = -directionToLight.y;
-		
-		float shadow = textureCube(shadowSampler, directionToLight);
-		if (shadow < 1.0)
-		{
-			return shadow * (1. - darkness) + darkness;
-		}
-		return 1.0;
-	}
-#endif
-
 	float computeShadowWithESMCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, float depthScale, vec2 depthValues)
 	float computeShadowWithESMCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, float depthScale, vec2 depthValues)
 	{
 	{
 		vec3 directionToLight = vPositionW - lightPosition;
 		vec3 directionToLight = vPositionW - lightPosition;
@@ -188,28 +168,6 @@
 		return computeFallOff(min(1.0, visibility + darkness), clipSpace.xy, frustumEdgeFalloff);
 		return computeFallOff(min(1.0, visibility + darkness), clipSpace.xy, frustumEdgeFalloff);
 	}
 	}
 
 
-#ifdef WEBGL2
-	float computeShadowWithPCF(vec4 vPositionFromLight, float depthMetric, sampler2DShadow shadowSampler, float darkness, float frustumEdgeFalloff)
-	{
-		vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
-		//vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
-		vec3 uvDepth = vec3(0.5 * clipSpace.xy + vec2(0.5), depthMetric);
-
-		if (uvDepth.x < 0. || uvDepth.x > 1.0 || uvDepth.y < 0. || uvDepth.y > 1.0)
-		{
-			return 1.0;
-		}
-
-		float shadow = texture2D(shadowSampler, uvDepth);
-		if (shadow < 1.0)
-		{
-			shadow = shadow * (1. - darkness) + darkness;
-			return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
-		}
-		return 1.0;
-	}
-#endif
-
 	float computeShadowWithESM(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float darkness, float depthScale, float frustumEdgeFalloff)
 	float computeShadowWithESM(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float darkness, float depthScale, float frustumEdgeFalloff)
 	{
 	{
 		vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
 		vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -255,4 +213,110 @@
 
 
 		return computeFallOff(esm, clipSpace.xy, frustumEdgeFalloff);
 		return computeFallOff(esm, clipSpace.xy, frustumEdgeFalloff);
 	}
 	}
+
+	#ifdef WEBGL2
+		const vec3 PCFSamplers[16] = vec3[16](
+			vec3( -0.94201624, -0.39906216, 0.),
+			vec3( 0.94558609, -0.76890725, 0.),
+			vec3( -0.094184101, -0.92938870, 0.),
+			vec3( 0.34495938, 0.29387760, 0.),
+			vec3( -0.91588581, 0.45771432, 0.),
+			vec3( -0.81544232, -0.87912464, 0.),
+			vec3( -0.38277543, 0.27676845, 0.),
+			vec3( 0.97484398, 0.75648379, 0.),
+			vec3( 0.44323325, -0.97511554, 0.),
+			vec3( 0.53742981, -0.47373420, 0.),
+			vec3( -0.26496911, -0.41893023, 0.),
+			vec3( 0.79197514, 0.19090188, 0.),
+			vec3( -0.24188840, 0.99706507, 0.),
+			vec3( -0.81409955, 0.91437590, 0.),
+			vec3( 0.19984126, 0.78641367, 0.),
+			vec3( 0.14383161, -0.14100790, 0.)
+		);
+
+		// Shadow PCF kernel size 1 with a single tap (lowest quality)
+		float computeShadowWithPCF1(vec4 vPositionFromLight, sampler2DShadow shadowSampler, float darkness, float frustumEdgeFalloff)
+		{
+			vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
+			vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
+
+			float shadow = texture2D(shadowSampler, uvDepth);
+			shadow = shadow * (1. - darkness) + darkness;
+			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 computeShadowWithPCF3(vec4 vPositionFromLight, sampler2DShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float frustumEdgeFalloff)
+		{
+			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 = shadow * (1. - darkness) + darkness;
+			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 computeShadowWithPCF5(vec4 vPositionFromLight, sampler2DShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float frustumEdgeFalloff)
+		{
+			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 = shadow * (1. - darkness) + darkness;
+			return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
+			
+		}
+	#endif
 #endif
 #endif

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

@@ -32,7 +32,7 @@ vec4 worldPos = finalWorld * vec4(position, 1.0);
 gl_Position = viewProjection * worldPos;
 gl_Position = viewProjection * worldPos;
 
 
 #ifdef PCF
 #ifdef PCF
-	gl_Position.z += biasAndScale.x * depthValues.y;
+	gl_Position.z += biasAndScale.x;
 #else
 #else
 	vDepthMetric = ((gl_Position.z + depthValues.x) / (depthValues.y)) + biasAndScale.x;
 	vDepthMetric = ((gl_Position.z + depthValues.x) / (depthValues.y)) + biasAndScale.x;
 #endif
 #endif