From ea1a342619ed44475e1f46401660abba7f7b17d7 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 5 Aug 2021 02:28:40 -0700 Subject: [PATCH] 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 --- docs/api/en/lights/shadows/LightShadow.html | 7 ++++- docs/api/zh/lights/shadows/LightShadow.html | 5 ++++ examples/webgl_shadowmap_vsm.html | 27 +++++++++++++++---- src/lights/LightShadow.js | 1 + .../shaders/ShaderLib/vsm_frag.glsl.js | 17 +++++++----- src/renderers/webgl/WebGLShadowMap.js | 10 +++---- 6 files changed, 49 insertions(+), 18 deletions(-) diff --git a/docs/api/en/lights/shadows/LightShadow.html b/docs/api/en/lights/shadows/LightShadow.html index a64caecb92..aa7d86d18f 100644 --- a/docs/api/en/lights/shadows/LightShadow.html +++ b/docs/api/en/lights/shadows/LightShadow.html @@ -42,7 +42,12 @@

[property:Float bias]

Shadow map bias, how much to add or subtract from the normalized depth when deciding whether a surface is in shadow.
- 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 +

+ +

[property:Integer blurSamples]

+

+ The amount of samples to use when blurring a VSM shadow map.

[property:WebGLRenderTarget map]

diff --git a/docs/api/zh/lights/shadows/LightShadow.html b/docs/api/zh/lights/shadows/LightShadow.html index c5e1b576eb..672e91ee35 100644 --- a/docs/api/zh/lights/shadows/LightShadow.html +++ b/docs/api/zh/lights/shadows/LightShadow.html @@ -43,6 +43,11 @@ 默认值为0.此处非常小的调整(大约0.0001)可能有助于减少阴影中的伪影

+

[property:Integer blurSamples]

+

+ The amount of samples to use when blurring a VSM shadow map. +

+

[property:WebGLRenderTarget map]

使用内置摄像头生成的深度图;超出像素深度的位置在阴影中。在渲染期间内部计算。 diff --git a/examples/webgl_shadowmap_vsm.html b/examples/webgl_shadowmap_vsm.html index f70dc13fcd..375f0932e1 100644 --- a/examples/webgl_shadowmap_vsm.html +++ b/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 ); diff --git a/src/lights/LightShadow.js b/src/lights/LightShadow.js index 60e79e6e32..e00f1ca55f 100644 --- a/src/lights/LightShadow.js +++ b/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 ); diff --git a/src/renderers/shaders/ShaderLib/vsm_frag.glsl.js b/src/renderers/shaders/ShaderLib/vsm_frag.glsl.js index 34ee7022e2..e5aa382523 100644 --- a/src/renderers/shaders/ShaderLib/vsm_frag.glsl.js +++ b/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 @@ -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 ); diff --git a/src/renderers/webgl/WebGLShadowMap.js b/src/renderers/webgl/WebGLShadowMap.js index 609cd267e6..b5ec5c9c63 100644 --- a/src/renderers/webgl/WebGLShadowMap.js +++ b/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 ); -- GitLab