Procházet zdrojové kódy

Merge pull request #18761 from gkjohnson/csm-cascade-blend

CSM: Add fade
Mr.doob před 5 roky
rodič
revize
6b8c695a18

+ 46 - 7
examples/jsm/csm/CSM.js

@@ -47,11 +47,13 @@ export default class CSM {
 		this.lightFar = data.lightFar || 2000;
 		this.lightMargin = data.lightMargin || 200;
 		this.customSplitsCallback = data.customSplitsCallback;
+		this.fade = false;
 		this.mainFrustum = new Frustum();
 		this.frustums = [];
 		this.breaks = [];
 
 		this.lights = [];
+		this.shaders = [];
 		this.materials = [];
 		this.createLights();
 
@@ -161,12 +163,13 @@ export default class CSM {
 
 	update( cameraMatrix ) {
 
-		for ( let i = 0; i < this.frustums.length; i ++ ) {
+		const frustums = this.frustums;
+		for ( let i = 0; i < frustums.length; i ++ ) {
 
 			const light = this.lights[ i ];
 			light.shadow.camera.updateMatrixWorld( true );
 			_cameraToLightMatrix.multiplyMatrices( light.shadow.camera.matrixWorldInverse, cameraMatrix );
-			this.frustums[ i ].toSpace( _cameraToLightMatrix, _lightSpaceFrustum );
+			frustums[ i ].toSpace( _cameraToLightMatrix, _lightSpaceFrustum );
 
 			_bbox.makeEmpty();
 			for ( let j = 0; j < 4; j ++ ) {
@@ -179,10 +182,21 @@ export default class CSM {
 			_bbox.getSize( _size );
 			_bbox.getCenter( _center );
 			_center.z = _bbox.max.z + this.lightMargin;
-
 			_center.applyMatrix4( light.shadow.camera.matrixWorld );
 
-			const squaredBBWidth = Math.max( _size.x, _size.y );
+			let squaredBBWidth = Math.max( _size.x, _size.y );
+			if ( this.fade ) {
+
+				// expand the shadow extents by the fade margin if fade is enabled.
+				const camera = this.camera;
+				const far = Math.max( camera.far, this.maxFar );
+				const linearDepth = frustums[ i ].vertices.far[ 0 ].z / ( far - camera.near );
+				const margin = 0.25 * Math.pow( linearDepth, 2.0 ) * ( far - camera.near );
+
+				squaredBBWidth += margin;
+
+			}
+
 			light.shadow.camera.left = - squaredBBWidth / 2;
 			light.shadow.camera.right = squaredBBWidth / 2;
 			light.shadow.camera.top = squaredBBWidth / 2;
@@ -215,6 +229,12 @@ export default class CSM {
 		material.defines.USE_CSM = 1;
 		material.defines.CSM_CASCADES = this.cascades;
 
+		if ( this.fade ) {
+
+			material.defines.CSM_FADE = '';
+
+		}
+
 		const breaksVec2 = [];
 		this.getExtendedBreaks( breaksVec2 );
 
@@ -227,9 +247,10 @@ export default class CSM {
 			shader.uniforms.cameraNear = { value: self.camera.near };
 			shader.uniforms.shadowFar = { value: far };
 
-			self.materials.push( shader );
+			self.shaders.push( shader );
 
 		};
+		this.materials.push( material );
 
 	}
 
@@ -237,15 +258,33 @@ export default class CSM {
 
 		const far = Math.min( this.camera.far, this.maxFar );
 
-		for ( let i = 0; i < this.materials.length; i ++ ) {
+		for ( let i = 0; i < this.shaders.length; i ++ ) {
 
-			const uniforms = this.materials[ i ].uniforms;
+			const shader = this.shaders[ i ];
+			const uniforms = shader.uniforms;
 			this.getExtendedBreaks( uniforms.CSM_cascades.value );
 			uniforms.cameraNear.value = this.camera.near;
 			uniforms.shadowFar.value = far;
 
 		}
 
+		for ( let i = 0; i < this.materials.length; i ++ ) {
+
+			const material = this.materials[ i ];
+			if ( ! this.fade && 'CSM_FADE' in material.defines ) {
+
+				delete material.defines.CSM_FADE;
+				material.needsUpdate = true;
+
+			} else if ( this.fade && ! ( 'CSM_FADE' in material.defines ) ) {
+
+				material.defines.CSM_FADE = '';
+				material.needsUpdate = true;
+
+			}
+
+		}
+
 	}
 
 	getExtendedBreaks( target ) {

+ 44 - 3
examples/jsm/csm/Shader.js

@@ -78,11 +78,52 @@ IncidentLight directLight;
 	DirectionalLightShadow directionalLightShadow;
 	#endif
 
+	#if defined( CSM_FADE )
+	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
+
+		directionalLight = directionalLights[ i ];
+		getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );
+
+		float margin = 0.25 * pow( linearDepth, 2.0 );
+		float csmx = CSM_cascades[ i ].x - margin / 2.0;
+		float csmy = CSM_cascades[ i ].y + margin / 2.0;
+		if( i < NUM_DIR_LIGHT_SHADOWS && linearDepth >= csmx && ( linearDepth < csmy ||  i == CSM_CASCADES - 1 ) ) {
+
+			float dist = min( linearDepth - csmx, csmy - linearDepth );
+			float ratio = clamp( dist / margin, 0.0, 1.0 );
+			if( i < NUM_DIR_LIGHT_SHADOWS ) {
+
+				vec3 prevColor = directLight.color;
+				directionalLightShadow = directionalLightShadows[ i ];
+				directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
+
+				bool shouldFadeLastCascade = i == CSM_CASCADES - 1 && linearDepth > ( csmx + csmy ) / 2.0;
+				directLight.color = mix( prevColor, directLight.color, shouldFadeLastCascade ? ratio : 1.0 );
+
+			}
+
+			ReflectedLight prevLight = reflectedLight;
+			RE_Direct( directLight, geometry, material, reflectedLight );
+
+			bool shouldBlend = i != CSM_CASCADES - 1 || i == CSM_CASCADES - 1 && linearDepth < ( csmx + csmy ) / 2.0;
+			float blendRatio = shouldBlend ? ratio : 1.0;
+
+			reflectedLight.directDiffuse = mix( prevLight.directDiffuse, reflectedLight.directDiffuse, blendRatio );
+			reflectedLight.directSpecular = mix( prevLight.directSpecular, reflectedLight.directSpecular, blendRatio );
+			reflectedLight.indirectDiffuse = mix( prevLight.indirectDiffuse, reflectedLight.indirectDiffuse, blendRatio );
+			reflectedLight.indirectSpecular = mix( prevLight.indirectSpecular, reflectedLight.indirectSpecular, blendRatio );
+
+		}
+
+	}
+	#else
+
 	#pragma unroll_loop
 	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
 
 		directionalLight = directionalLights[ i ];
 		getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );
+
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
 
 		directionalLightShadow = directionalLightShadows[ i ];
@@ -90,12 +131,12 @@ IncidentLight directLight;
 
 		#endif
 
-        if(linearDepth >= CSM_cascades[UNROLLED_LOOP_INDEX].x && (linearDepth < CSM_cascades[UNROLLED_LOOP_INDEX].y || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1)) RE_Direct( directLight, geometry, material, reflectedLight );
-
-
+		if(linearDepth >= CSM_cascades[UNROLLED_LOOP_INDEX].x && (linearDepth < CSM_cascades[UNROLLED_LOOP_INDEX].y || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1)) RE_Direct( directLight, geometry, material, reflectedLight );
 
 	}
 
+	#endif
+
 #endif
 
 

+ 8 - 0
examples/webgl_shadowmap_csm.html

@@ -26,6 +26,7 @@
 
 			var params = {
 				orthographic: false,
+				fade: false,
 				far: 1000,
 				mode: 'practical',
 				lightX: - 1,
@@ -136,6 +137,13 @@
 
 				} );
 
+				gui.add( params, 'fade' ).onChange( function ( value ) { 
+
+					csm.fade = value;
+					csm.updateUniforms();
+
+				} );
+
 				gui.add( params, 'far', 1, 5000 ).step( 1 ).name( 'shadow far' ).onChange( function ( value ) {
 
 					csm.maxFar = value;