Forráskód Böngészése

VSM Shadows: Add support for setting the number of blur samples (#22272)

* remove unneeded dir light add call

* Add support for blur iterations to VSM shadows

* Add samples options to vsm example

* Add folders

* Docs update

* Support blurSamples === 1.0

* fix early out

* improve example arguments
Garrett Johnson 4 éve
szülő
commit
ea1a342619

+ 6 - 1
docs/api/en/lights/shadows/LightShadow.html

@@ -42,7 +42,12 @@
 		<h3>[property:Float bias]</h3>
 		<p>
 			Shadow map bias, how much to add or subtract from the normalized depth when deciding whether a surface is in shadow.<br />
-			The default is 0. Very tiny adjustments here (in the order of 0.0001) may help reduce artefacts in shadows
+			The default is 0. Very tiny adjustments here (in the order of 0.0001) may help reduce artifacts in shadows
+		</p>
+
+		<h3>[property:Integer blurSamples]</h3>
+		<p>
+			The amount of samples to use when blurring a VSM shadow map.
 		</p>
 
 		<h3>[property:WebGLRenderTarget map]</h3>

+ 5 - 0
docs/api/zh/lights/shadows/LightShadow.html

@@ -43,6 +43,11 @@
 			默认值为0.此处非常小的调整(大约0.0001)可能有助于减少阴影中的伪影
 		</p>
 
+		<h3>[property:Integer blurSamples]</h3>
+		<p>
+			The amount of samples to use when blurring a VSM shadow map.
+		</p>
+
 		<h3>[property:WebGLRenderTarget map]</h3>
 		<p>
 			使用内置摄像头生成的深度图;超出像素深度的位置在阴影中。在渲染期间内部计算。

+ 22 - 5
examples/webgl_shadowmap_vsm.html

@@ -36,22 +36,40 @@
 				const gui = new GUI();
 
 				const config = {
-					'Spotlight Radius': 4,
-					'Directional light Radius': 4,
+					spotlightRadius: 4,
+					spotlightSamples: 8,
+					dirlightRadius: 4,
+					dirlightSamples: 8
 				};
 
-				gui.add( config, 'Spotlight Radius' ).min( 2 ).max( 8 ).onChange( function ( value ) {
+				const spotlightFolder = gui.addFolder( 'Spotlight' );
+				spotlightFolder.add( config, 'spotlightRadius' ).name( 'radius' ).min( 0 ).max( 25 ).onChange( function ( value ) {
 
 					spotLight.shadow.radius = value;
 
 				} );
 
-				gui.add( config, 'Directional light Radius' ).min( 2 ).max( 8 ).onChange( function ( value ) {
+				spotlightFolder.add( config, 'spotlightSamples', 1, 25, 1 ).name( 'samples' ).onChange( function ( value ) {
+
+					spotLight.shadow.blurSamples = value;
+
+				} );
+				spotlightFolder.open();
+
+				const dirlightFolder = gui.addFolder( 'Directional Light' );
+				dirlightFolder.add( config, 'dirlightRadius' ).name( 'radius' ).min( 0 ).max( 25 ).onChange( function ( value ) {
 
 					dirLight.shadow.radius = value;
 
 				} );
 
+				dirlightFolder.add( config, 'dirlightSamples', 1, 25, 1 ).name( 'samples' ).onChange( function ( value ) {
+
+					dirLight.shadow.blurSamples = value;
+
+				} );
+				dirlightFolder.open();
+
 				document.body.appendChild( renderer.domElement );
 				window.addEventListener( 'resize', onWindowResize );
 
@@ -98,7 +116,6 @@
 				dirLight.shadow.mapSize.height = 512;
 				dirLight.shadow.radius = 4;
 				dirLight.shadow.bias = - 0.0005;
-				scene.add( dirLight );
 
 				dirGroup = new THREE.Group();
 				dirGroup.add( dirLight );

+ 1 - 0
src/lights/LightShadow.js

@@ -17,6 +17,7 @@ class LightShadow {
 		this.bias = 0;
 		this.normalBias = 0;
 		this.radius = 1;
+		this.blurSamples = 8;
 
 		this.mapSize = new Vector2( 512, 512 );
 

+ 11 - 6
src/renderers/shaders/ShaderLib/vsm_frag.glsl.js

@@ -2,6 +2,7 @@ export default /* glsl */`
 uniform sampler2D shadow_pass;
 uniform vec2 resolution;
 uniform float radius;
+uniform float samples;
 
 #include <packing>
 
@@ -11,19 +12,23 @@ void main() {
 	float squared_mean = 0.0;
 
 	// This seems totally useless but it's a crazy work around for a Adreno compiler bug
-	float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy ) / resolution ) );
+	// float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy ) / resolution ) );
 
-	for ( float i = -1.0; i < 1.0 ; i += SAMPLE_RATE) {
+	float uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 );
+	float uvStart = samples <= 1.0 ? 0.0 : - 1.0;
+	for ( float i = 0.0; i < samples; i ++ ) {
+
+		float uvOffset = uvStart + i * uvStride;
 
 		#ifdef HORIZONTAL_PASS
 
-			vec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( i, 0.0 ) * radius ) / resolution ) );
+			vec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) );
 			mean += distribution.x;
 			squared_mean += distribution.y * distribution.y + distribution.x * distribution.x;
 
 		#else
 
-			float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, i ) * radius ) / resolution ) );
+			float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) );
 			mean += depth;
 			squared_mean += depth * depth;
 
@@ -31,8 +36,8 @@ void main() {
 
 	}
 
-	mean = mean * HALF_SAMPLE_RATE;
-	squared_mean = squared_mean * HALF_SAMPLE_RATE;
+	mean = mean / samples;
+	squared_mean = squared_mean / samples;
 
 	float std_dev = sqrt( squared_mean - mean * mean );
 

+ 4 - 6
src/renderers/webgl/WebGLShadowMap.js

@@ -33,15 +33,11 @@ function WebGLShadowMap( _renderer, _objects, _capabilities ) {
 
 	const shadowMaterialVertical = new ShaderMaterial( {
 
-		defines: {
-			SAMPLE_RATE: 2.0 / 8.0,
-			HALF_SAMPLE_RATE: 1.0 / 8.0
-		},
-
 		uniforms: {
 			shadow_pass: { value: null },
 			resolution: { value: new Vector2() },
-			radius: { value: 4.0 }
+			radius: { value: 4.0 },
+			samples: { value: 8.0 }
 		},
 
 		vertexShader: vsm_vert,
@@ -213,6 +209,7 @@ function WebGLShadowMap( _renderer, _objects, _capabilities ) {
 		shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture;
 		shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize;
 		shadowMaterialVertical.uniforms.radius.value = shadow.radius;
+		shadowMaterialVertical.uniforms.samples.value = shadow.blurSamples;
 		_renderer.setRenderTarget( shadow.mapPass );
 		_renderer.clear();
 		_renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null );
@@ -222,6 +219,7 @@ function WebGLShadowMap( _renderer, _objects, _capabilities ) {
 		shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture;
 		shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize;
 		shadowMaterialHorizontal.uniforms.radius.value = shadow.radius;
+		shadowMaterialHorizontal.uniforms.samples.value = shadow.blurSamples;
 		_renderer.setRenderTarget( shadow.map );
 		_renderer.clear();
 		_renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null );