Explorar o código

Add shadow map normal offset

This feature offsets the position used to query shadow map occlusion along the object normal. This can be used to reduce shadow acne especially in large scenes.
Olli Etuaho %!s(int64=5) %!d(string=hai) anos
pai
achega
8925432851

+ 4 - 0
docs/api/en/lights/shadows/LightShadow.html

@@ -40,6 +40,10 @@
 			The default is 0. Very tiny adjustments here (in the order of 0.0001) may help reduce artefacts in shadows
 		</p>
 
+		<h3>[property:Float normalOffset]</h3>
+		<p>Defines how much the position used to query the shadow map is offset along the object normal.</p>
+		<p>The default is 0. Increasing this value can be used to reduce shadow acne especially in large scenes where light shines onto geometry at a shallow angle. The cost is that shadows may appear distorted.</p>
+
 		<h3>[property:WebGLRenderTarget map]</h3>
 		<p>
 			The depth map generated using the internal camera; a location beyond a pixel's depth is

+ 2 - 0
examples/jsm/objects/Water.js

@@ -120,6 +120,8 @@ var Water = function ( geometry, options ) {
 			'	vec4 mvPosition =  modelViewMatrix * vec4( position, 1.0 );',
 			'	gl_Position = projectionMatrix * mvPosition;',
 
+			'#include <beginnormal_vertex>',
+			'#include <defaultnormal_vertex>',
 			'#include <logdepthbuf_vertex>',
 			'#include <fog_vertex>',
 			'#include <shadowmap_vertex>',

+ 1 - 0
src/lights/LightShadow.d.ts

@@ -11,6 +11,7 @@ export class LightShadow {
 
 	camera: Camera;
 	bias: number;
+	normalOffset: number;
 	radius: number;
 	mapSize: Vector2;
 	map: RenderTarget;

+ 1 - 0
src/lights/LightShadow.js

@@ -13,6 +13,7 @@ function LightShadow( camera ) {
 	this.camera = camera;
 
 	this.bias = 0;
+	this.normalOffset = 0;
 	this.radius = 1;
 
 	this.mapSize = new Vector2( 512, 512 );

+ 0 - 39
src/renderers/shaders/ShaderChunk/lights_pars_begin.glsl.js

@@ -63,19 +63,6 @@ vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {
 
 	uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];
 
-	#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0
-
-		struct DirectionalLightShadow {
-			float shadowBias;
-			float shadowRadius;
-			vec2 shadowMapSize;
-		};
-
-		uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];
-
-	#endif
-
-
 	void getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {
 
 		directLight.color = directionalLight.color;
@@ -98,20 +85,6 @@ vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {
 
 	uniform PointLight pointLights[ NUM_POINT_LIGHTS ];
 
-	#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0
-
-		struct PointLightShadow {
-			float shadowBias;
-			float shadowRadius;
-			vec2 shadowMapSize;
-			float shadowCameraNear;
-			float shadowCameraFar;
-		};
-
-		uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];
-
-	#endif
-
 	// directLight is an out parameter as having it as a return value caused compiler errors on some devices
 	void getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {
 
@@ -143,18 +116,6 @@ vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {
 
 	uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];
 
-	#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0
-
-		struct SpotLightShadow {
-			float shadowBias;
-			float shadowRadius;
-			vec2 shadowMapSize;
-		};
-
-		uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];
-
-	#endif
-
 	// directLight is an out parameter as having it as a return value caused compiler errors on some devices
 	void getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight  ) {
 

+ 29 - 0
src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js

@@ -6,6 +6,15 @@ export default /* glsl */`
 		uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];
 		varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];
 
+		struct DirectionalLightShadow {
+			float shadowBias;
+			float shadowNormalOffset;
+			float shadowRadius;
+			vec2 shadowMapSize;
+		};
+
+		uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];
+
 	#endif
 
 	#if NUM_SPOT_LIGHT_SHADOWS > 0
@@ -13,6 +22,15 @@ export default /* glsl */`
 		uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];
 		varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];
 
+		struct SpotLightShadow {
+			float shadowBias;
+			float shadowNormalOffset;
+			float shadowRadius;
+			vec2 shadowMapSize;
+		};
+
+		uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];
+
 	#endif
 
 	#if NUM_POINT_LIGHT_SHADOWS > 0
@@ -20,6 +38,17 @@ export default /* glsl */`
 		uniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];
 		varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];
 
+		struct PointLightShadow {
+			float shadowBias;
+			float shadowNormalOffset;
+			float shadowRadius;
+			vec2 shadowMapSize;
+			float shadowCameraNear;
+			float shadowCameraFar;
+		};
+
+		uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];
+
 	#endif
 
 	/*

+ 29 - 0
src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl.js

@@ -6,6 +6,15 @@ export default /* glsl */`
 		uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ];
 		varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];
 
+		struct DirectionalLightShadow {
+			float shadowBias;
+			float shadowNormalOffset;
+			float shadowRadius;
+			vec2 shadowMapSize;
+		};
+
+		uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];
+
 	#endif
 
 	#if NUM_SPOT_LIGHT_SHADOWS > 0
@@ -13,6 +22,15 @@ export default /* glsl */`
 		uniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHT_SHADOWS ];
 		varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];
 
+		struct SpotLightShadow {
+			float shadowBias;
+			float shadowNormalOffset;
+			float shadowRadius;
+			vec2 shadowMapSize;
+		};
+
+		uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];
+
 	#endif
 
 	#if NUM_POINT_LIGHT_SHADOWS > 0
@@ -20,6 +38,17 @@ export default /* glsl */`
 		uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ];
 		varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];
 
+		struct PointLightShadow {
+			float shadowBias;
+			float shadowNormalOffset;
+			float shadowRadius;
+			vec2 shadowMapSize;
+			float shadowCameraNear;
+			float shadowCameraFar;
+		};
+
+		uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];
+
 	#endif
 
 	/*

+ 14 - 3
src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl.js

@@ -1,12 +1,21 @@
 export default /* glsl */`
 #ifdef USE_SHADOWMAP
 
+	#if NUM_DIR_LIGHT_SHADOWS > 0 || NUM_SPOT_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0
+
+		// Offsetting the position used for querying occlusion along the world normal can be used to reduce shadow acne.
+		vec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix );
+		vec4 shadowWorldPosition;
+
+	#endif
+
 	#if NUM_DIR_LIGHT_SHADOWS > 0
 
 	#pragma unroll_loop_start
 	for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {
 
-		vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;
+		shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalOffset, 0 );
+		vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;
 
 	}
 	#pragma unroll_loop_end
@@ -18,7 +27,8 @@ export default /* glsl */`
 	#pragma unroll_loop_start
 	for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {
 
-		vSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;
+		shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * spotLightShadows[ i ].shadowNormalOffset, 0 );
+		vSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * shadowWorldPosition;
 
 	}
 	#pragma unroll_loop_end
@@ -30,7 +40,8 @@ export default /* glsl */`
 	#pragma unroll_loop_start
 	for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {
 
-		vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;
+		shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalOffset, 0 );
+		vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition;
 
 	}
 	#pragma unroll_loop_end

+ 7 - 0
src/renderers/shaders/ShaderLib/shadow_vert.glsl.js

@@ -7,6 +7,13 @@ void main() {
 	#include <begin_vertex>
 	#include <project_vertex>
 	#include <worldpos_vertex>
+
+	#include <beginnormal_vertex>
+	#include <morphnormal_vertex>
+	#include <skinbase_vertex>
+	#include <skinnormal_vertex>
+	#include <defaultnormal_vertex>
+
 	#include <shadowmap_vertex>
 	#include <fog_vertex>
 

+ 3 - 0
src/renderers/shaders/UniformsLib.d.ts

@@ -73,6 +73,7 @@ export let UniformsLib: {
 			value: any[];
 			properties: {
 				shadowBias: {};
+				shadowNormalOffset: {};
 				shadowRadius: {};
 				shadowMapSize: {};
 			};
@@ -95,6 +96,7 @@ export let UniformsLib: {
 			value: any[];
 			properties: {
 				shadowBias: {};
+				shadowNormalOffset: {};
 				shadowRadius: {};
 				shadowMapSize: {};
 			};
@@ -114,6 +116,7 @@ export let UniformsLib: {
 			value: any[];
 			properties: {
 				shadowBias: {};
+				shadowNormalOffset: {};
 				shadowRadius: {};
 				shadowMapSize: {};
 			};

+ 3 - 0
src/renderers/shaders/UniformsLib.js

@@ -119,6 +119,7 @@ var UniformsLib = {
 
 		directionalLightShadows: { value: [], properties: {
 			shadowBias: {},
+			shadowNormalOffset: {},
 			shadowRadius: {},
 			shadowMapSize: {}
 		} },
@@ -138,6 +139,7 @@ var UniformsLib = {
 
 		spotLightShadows: { value: [], properties: {
 			shadowBias: {},
+			shadowNormalOffset: {},
 			shadowRadius: {},
 			shadowMapSize: {}
 		} },
@@ -154,6 +156,7 @@ var UniformsLib = {
 
 		pointLightShadows: { value: [], properties: {
 			shadowBias: {},
+			shadowNormalOffset: {},
 			shadowRadius: {},
 			shadowMapSize: {},
 			shadowCameraNear: {},

+ 6 - 0
src/renderers/webgl/WebGLLights.js

@@ -103,6 +103,7 @@ function ShadowUniformsCache() {
 				case 'DirectionalLight':
 					uniforms = {
 						shadowBias: 0,
+						shadowNormalOffset: 0,
 						shadowRadius: 1,
 						shadowMapSize: new Vector2()
 					};
@@ -111,6 +112,7 @@ function ShadowUniformsCache() {
 				case 'SpotLight':
 					uniforms = {
 						shadowBias: 0,
+						shadowNormalOffset: 0,
 						shadowRadius: 1,
 						shadowMapSize: new Vector2()
 					};
@@ -119,6 +121,7 @@ function ShadowUniformsCache() {
 				case 'PointLight':
 					uniforms = {
 						shadowBias: 0,
+						shadowNormalOffset: 0,
 						shadowRadius: 1,
 						shadowMapSize: new Vector2(),
 						shadowCameraNear: 1,
@@ -258,6 +261,7 @@ function WebGLLights() {
 					var shadowUniforms = shadowCache.get( light );
 
 					shadowUniforms.shadowBias = shadow.bias;
+					shadowUniforms.shadowNormalOffset = shadow.normalOffset;
 					shadowUniforms.shadowRadius = shadow.radius;
 					shadowUniforms.shadowMapSize = shadow.mapSize;
 
@@ -299,6 +303,7 @@ function WebGLLights() {
 					var shadowUniforms = shadowCache.get( light );
 
 					shadowUniforms.shadowBias = shadow.bias;
+					shadowUniforms.shadowNormalOffset = shadow.normalOffset;
 					shadowUniforms.shadowRadius = shadow.radius;
 					shadowUniforms.shadowMapSize = shadow.mapSize;
 
@@ -364,6 +369,7 @@ function WebGLLights() {
 					var shadowUniforms = shadowCache.get( light );
 
 					shadowUniforms.shadowBias = shadow.bias;
+					shadowUniforms.shadowNormalOffset = shadow.normalOffset;
 					shadowUniforms.shadowRadius = shadow.radius;
 					shadowUniforms.shadowMapSize = shadow.mapSize;
 					shadowUniforms.shadowCameraNear = shadow.camera.near;