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