sebastien 7 years ago
parent
commit
e50047c09b

+ 37 - 26
src/Lights/Shadows/babylon.shadowGenerator.ts

@@ -283,10 +283,6 @@
                     this.usePoissonSampling = true;
                     return;
                 }
-                this._useDepthStencilTexture = true;
-            }
-            else {
-                this._useDepthStencilTexture = false;
             }
 
             if (this._filter === value) {
@@ -434,6 +430,22 @@
             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;
+        }
+
         /**
          * Gets if the current filter is set to "PCSS" (contact hardening).
          */
@@ -441,7 +453,7 @@
             return this.filter === ShadowGenerator.FILTER_PCSS;
         }
         /**
-         * Sets the current filter to "PCF" (contact hardening).
+         * Sets the current filter to "PCSS" (contact hardening).
          */
         public set useContactHardeningShadow(value: boolean) {
             if (!value && this.filter !== ShadowGenerator.FILTER_PCSS) {
@@ -450,20 +462,20 @@
             this.filter = (value ? ShadowGenerator.FILTER_PCSS : ShadowGenerator.FILTER_NONE);
         }
 
-        private _percentageCloserFilteringQuality = ShadowGenerator.PCF_HIGH_QUALITY;
+        private _contactHardeningLightSize = 10;
         /**
-         * Gets the PCF Quality.
-         * Only valid if usePercentageCloserFiltering is true.
+         * Gets the Light Size used in PCSS to determine the blocker search area and the penumbra size.
+         * Only valid if useContactHardeningShadow is true.
          */
-        public get percentageCloserFilteringQuality(): number {
-            return this._percentageCloserFilteringQuality;
+        public get contactHardeningLightSize(): number {
+            return this._contactHardeningLightSize;
         }
         /**
-         * Sets the PCF Quality.
-         * Only valid if usePercentageCloserFiltering is true.
+         * Sets the Light Size used in PCSS to determine the blocker search area and the penumbra size.
+         * Only valid if useContactHardeningShadow is true.
          */
-        public set percentageCloserFilteringQuality(percentageCloserFilteringQuality: number) {
-            this._percentageCloserFilteringQuality = percentageCloserFilteringQuality;
+        public set contactHardeningLightSize(contactHardeningLightSize: number) {
+            this._contactHardeningLightSize = contactHardeningLightSize;
         }
 
         private _darkness = 0;
@@ -615,7 +627,6 @@
         private _currentFaceIndexCache = 0;
         private _textureType: number;
         private _defaultTextureMatrix = Matrix.Identity();
-        private _useDepthStencilTexture = false;
 
         /**
          * Creates a ShadowGenerator object.
@@ -686,8 +697,8 @@
             // Record Face Index before render.
             this._shadowMap.onBeforeRenderObservable.add((faceIndex: number) => {
                 this._currentFaceIndex = faceIndex;
-                if (this._useDepthStencilTexture) {
-                    //engine.setColorWrite(false);
+                if (this._filter === ShadowGenerator.FILTER_PCF) {
+                    engine.setColorWrite(false);
                 }
             });
 
@@ -696,7 +707,7 @@
 
             // Blur if required afer render.
             this._shadowMap.onAfterUnbindObservable.add(() => {
-                if (this._useDepthStencilTexture) {
+                if (this._filter === ShadowGenerator.FILTER_PCF) {
                     engine.setColorWrite(true);
                 }
                 if (!this.useBlurExponentialShadowMap && !this.useBlurCloseExponentialShadowMap) {
@@ -714,8 +725,8 @@
 
             // Clear according to the chosen filter.
             this._shadowMap.onClearObservable.add((engine: Engine) => {
-                if (this._useDepthStencilTexture) {
-                    engine.clear(clearOne, true, true, true);
+                if (this._filter === ShadowGenerator.FILTER_PCF) {
+                    engine.clear(clearOne, false, true, false);
                 }
                 else if (this.useExponentialShadowMap || this.useBlurExponentialShadowMap) {
                     engine.clear(clearZero, true, true, false);
@@ -1120,20 +1131,20 @@
             }
 
             // Only PCF uses depth stencil texture.
-            if (this._useDepthStencilTexture) {
+            if (this._filter === ShadowGenerator.FILTER_PCF) {
                 effect.setDepthStencilTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering());
                 light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), shadowMap.getSize().width, 1 / shadowMap.getSize().width, this.frustumEdgeFalloff, lightIndex);
             }
+            else if (this._filter === ShadowGenerator.FILTER_PCSS) {
+                effect.setDepthStencilTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering());
+                effect.setTexture("depthSampler" + lightIndex, this.getShadowMapForRendering());
+                light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), 1 / shadowMap.getSize().width, this._contactHardeningLightSize, this.frustumEdgeFalloff, lightIndex);
+            }
             else {
                 effect.setTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering());
                 light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), this.blurScale / shadowMap.getSize().width, this.depthScale, this.frustumEdgeFalloff, lightIndex);
             }
 
-            if (this._filter === ShadowGenerator.FILTER_PCSS) {
-                effect.setDepthStencilTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering());
-                effect.setTexture("depthSampler" + lightIndex, this.getShadowMapForRendering());
-            }
-
             light._uniformBuffer.updateFloat2("depthValues", this.getLight().getDepthMinZ(camera), this.getLight().getDepthMinZ(camera) + this.getLight().getDepthMaxZ(camera), lightIndex);
         }
 

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

@@ -55,7 +55,7 @@
 				shadow = computeShadowWithPCF5(vPositionFromLight{X}, shadowSampler{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w);
 			#endif
 		#elif defined(SHADOWPCSS{X})
-			shadow = computeShadowWithPCSS(vPositionFromLight{X}, depthSampler{X}, shadowSampler{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w);
+			shadow = computeShadowWithPCSS(vPositionFromLight{X}, vDepthMetric{X}, depthSampler{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w);
 		#else
 			#if defined(SHADOWCUBE{X})
 				shadow = computeShadowCube(light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.depthValues);

+ 23 - 17
src/Shaders/ShadersInclude/shadowsFragmentFunctions.fx

@@ -334,21 +334,26 @@
 			return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
 		}
 
-		float computeShadowWithPCSS(vec4 vPositionFromLight, sampler2D depthSampler, sampler2DShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float 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)
 		{
+			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 softness = 50.;
-
-			float searchSize = softness * clamp(uvDepth.z - .02, 0., 1.) / uvDepth.z;
-
 			float blockerDepth = 0.0;
 			float sumBlockerDepth = 0.0;
 			float numBlocker = 0.0;
 			for (int i = 0; i < 16; i++) {
-                blockerDepth = texture(depthSampler, uvDepth.xy + (searchSize * shadowMapSizeAndInverse.y * PCFSamplers[i].xy)).r;
-                if (blockerDepth < uvDepth.z) {
+                blockerDepth = texture(depthSampler, uvDepth.xy + (lightSizeUV * shadowMapSizeInverse * PCFSamplers[i].xy)).r;
+                if (blockerDepth < depthMetric) {
                     sumBlockerDepth += blockerDepth;
                     numBlocker++;
                 }
@@ -357,23 +362,24 @@
 			if (numBlocker < 1.0) {
 				return 1.0;
 			}
-
 			float avgBlockerDepth = sumBlockerDepth / numBlocker;
-			float penumbra = uvDepth.z - avgBlockerDepth;
-			float filterRadiusUV = penumbra * softness;
 
-			float shadow = 0.;
+			// float penumbraRatio = (depthMetric - avgBlockerDepth) / avgBlockerDepth;
+			// Do not dividing by z despite being physically incorrect looks better due to the limited kernel size.
+			float AAOffset = shadowMapSizeInverse * 10.;
+			float penumbraRatio = ((depthMetric - avgBlockerDepth) + AAOffset);
+			float filterRadius = penumbraRatio * lightSizeUV * shadowMapSizeInverse;
 
-			float random = getRand(gl_FragCoord.xy / 1024.);
+			float random = getRand(gl_FragCoord.xy);
 			float rotationAngle = random * 3.1415926;
-			vec2 rotationTrig = vec2(cos(rotationAngle), sin(rotationAngle));
+			vec2 rotationVector = vec2(cos(rotationAngle), sin(rotationAngle));
 
+			float shadow = 0.;
 			for (int i = 0; i < 32; i++) {
 				vec3 offset = PCFSamplers[i];
-
-				offset = vec3(offset.x * rotationTrig.x - offset.y * rotationTrig.y, offset.y * rotationTrig.x + offset.x * rotationTrig.y, 0.);
-
-				shadow += texture2D(shadowSampler, uvDepth + offset * filterRadiusUV * shadowMapSizeAndInverse.y);
+				// 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 /= 32.;