Browse Source

Updated point-light shadow mapping code to be more efficient with uniforms and varyings.

Mark Kellogg 10 years ago
parent
commit
5964cb9b00

+ 12 - 13
src/renderers/WebGLRenderer.js

@@ -1701,7 +1701,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			if ( object.receiveShadow && ! material._shadowPass ) {
 
-				refreshUniformsShadow( m_uniforms, lights );
+				refreshUniformsShadow( m_uniforms, lights, camera );
 
 			}
 
@@ -1953,7 +1953,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	}
 
-	function refreshUniformsShadow ( uniforms, lights ) {
+	function refreshUniformsShadow ( uniforms, lights, camera ) {
 
 		if ( uniforms.shadowMatrix ) {
 
@@ -1971,28 +1971,27 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 						uniforms.shadowCube.value[ j ] = light.shadowMap;
 						uniforms.shadowMap.value[ j ] = null;
-						uniforms.isShadowCube.value[ j ] = 1;
+
+						// for point lights we set the sign of the shadowDarkness uniform to be negative
+						uniforms.shadowDarkness.value[ j ] = -light.shadowDarkness;
+
+						// when we have a point light, the 'shadowMatrix' uniform is used to store
+						// the inverse of the view matrix (camera.matrixWorld), so that we can get the 
+						// world-space position of the light in the shader.
+						uniforms.shadowMatrix.value[ j ] = camera.matrixWorld;
 
 					} else {
 
 						uniforms.shadowMap.value[ j ] = light.shadowMap;
-						uniforms.isShadowCube.value[ j ] = 0;
 						uniforms.shadowCube.value[ j ] = null;
+						uniforms.shadowMatrix.value[ j ] = light.shadowMatrix;
+						uniforms.shadowDarkness.value[ j ] = light.shadowDarkness;
 
 					}					
 
 					uniforms.shadowMapSize.value[ j ] = light.shadowMapSize;
-
-					uniforms.shadowMatrix.value[ j ] = light.shadowMatrix;
-
-					uniforms.shadowDarkness.value[ j ] = light.shadowDarkness;
 					uniforms.shadowBias.value[ j ] = light.shadowBias;
 
-					_vector3.setFromMatrixPosition( light.matrixWorld );
-					uniforms.shadowLightPosition.value[ j * 3 ] = _vector3.x;
-					uniforms.shadowLightPosition.value[ j * 3 + 1 ] = _vector3.y;
-					uniforms.shadowLightPosition.value[ j * 3 + 2 ] = _vector3.z;
-
 					j ++;
 
 				}

+ 6 - 0
src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl

@@ -3,3 +3,9 @@
 	varying vec3 vWorldPosition;
 
 #endif
+
+#if MAX_POINT_LIGHTS > 0
+
+	uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];
+
+#endif

+ 27 - 19
src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl

@@ -13,16 +13,24 @@
 	vec3 shadowColor = vec3( 1.0 );
 
 	for( int i = 0; i < MAX_SHADOWS; i ++ ) {
-		vec3 lightToPosition = vWPosition[ i ].xyz - shadowLightPosition[ i ];
-		vec3 lightToFragment = normalize( lightToPosition );
-		float distanceToLight = length( lightToPosition );
-		int currentIsCube = isShadowCube[ i ];
+
+		// to save on uniform space, we use the sign of @shadowDarkness[ i ] to determine
+		// whether or not this light is a point light ( shadowDarkness[ i ] < 0 == point light)
+		bool isPointLight = shadowDarkness[ i ] < 0.0;
+
+		// get the real shadow darkness
+		float realShadowDarkness = abs( shadowDarkness[ i ] );
+
+		// for point lights, the uniform @vShadowCoord is re-purposed to hold
+		// the distance from the light to the world-space position of the fragment.
+		vec3 lightToPosition = vShadowCoord[ i ].xyz;
 		float cubeTexelSize = 1.0 / shadowMapSize[ i ].x;
+
 		vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;
 		float shadow = 0.0;
 
-				// if ( something && something ) breaks ATI OpenGL shader compiler
-				// if ( all( something, something ) ) using this instead
+		// if ( something && something ) breaks ATI OpenGL shader compiler
+		// if ( all( something, something ) ) using this instead
 
 		bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );
 		bool inFrustum = all( inFrustumVec );
@@ -31,14 +39,14 @@
 
 		bool frustumTest = all( frustumTestVec );
 
-		if ( frustumTest || currentIsCube == 1) {
+		if ( frustumTest || isPointLight ) {
 
 			shadowCoord.z += shadowBias[ i ];
 
 			#if defined( SHADOWMAP_TYPE_PCF )
-				if( currentIsCube == 1 ){
+				if( isPointLight ){
 				
-					shadow = sampleCubeShadowMapPCF( shadowCube[ i ], lightToFragment, distanceToLight, cubeTexelSize, 1.5);
+					shadow = sampleCubeShadowMapPCF( i,  normalize( lightToPosition ), length( lightToPosition ), cubeTexelSize, 1.5);
 					
 				} else {
 					// Percentage-close filtering
@@ -106,13 +114,13 @@
 					if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
 
 				}
-				shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );
+				shadowColor = shadowColor * vec3( ( 1.0 - realShadowDarkness * shadow ) );
 				
 			#elif defined( SHADOWMAP_TYPE_PCF_SOFT )
 			
-				if( currentIsCube == 1 ){
+				if( isPointLight ){
 
-					shadow = sampleCubeShadowMapPCF( shadowCube[ i ], lightToFragment, distanceToLight, cubeTexelSize, 2.5 );
+					shadow = sampleCubeShadowMapPCF( i,  normalize( lightToPosition ), length( lightToPosition ), cubeTexelSize, 2.5 );
 
 				} else { 
 
@@ -165,15 +173,15 @@
 					shadow = dot( shadowValues, vec4( 1.0 ) );
 				}
 
-				shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );
+				shadowColor = shadowColor * vec3( ( 1.0 - realShadowDarkness * shadow ) );
 
 			#else
 
-				if( currentIsCube == 1 ){		
+				if( isPointLight ){		
 				
-					float dist = getCubeMapFloat( shadowCube[ i ], lightToFragment );
-					if ( distanceToLight >= dist)
-						shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );
+					float dist = getCubeShadowMapFloat( i,  normalize( lightToPosition ) );
+					if ( length( lightToPosition ) >= dist)
+						shadowColor = shadowColor * vec3( 1.0 - realShadowDarkness );
 						
 				} else {
 				
@@ -184,11 +192,11 @@
 
 					// spot with multiple shadows is darker
 
-					shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );
+					shadowColor = shadowColor * vec3( 1.0 - realShadowDarkness );
 
 					// spot with multiple shadows has the same color as single shadow spot
 
-					// 	shadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );
+					// 	shadowColor = min( shadowColor, vec3( realShadowDarkness ) );
 				}
 
 			#endif

+ 39 - 24
src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl

@@ -1,6 +1,5 @@
 #ifdef USE_SHADOWMAP
 
-	uniform int isShadowCube[ MAX_SHADOWS ];
 	uniform samplerCube shadowCube[ MAX_SHADOWS ];
 	uniform sampler2D shadowMap[ MAX_SHADOWS ];
 	uniform vec2 shadowMapSize[ MAX_SHADOWS ];
@@ -8,10 +7,7 @@
 	uniform float shadowDarkness[ MAX_SHADOWS ];
 	uniform float shadowBias[ MAX_SHADOWS ];
 
-	uniform vec3 shadowLightPosition[ MAX_SHADOWS ];
-
 	varying vec4 vShadowCoord[ MAX_SHADOWS ];
-	varying vec4 vWPosition[ MAX_SHADOWS ];
 
 	float unpackDepth( const in vec4 rgba_depth ) {
 
@@ -21,25 +17,25 @@
 
 	}
 
-	vec4 pack1K (float depth) {
+	vec4 pack1K ( float depth ) {
 	
 		depth /= 1000.0;
-		const vec4 bitSh = vec4(256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0);
-  		const vec4 bitMsk = vec4(0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0);
-   		vec4 res = fract(depth * bitSh);
+		const vec4 bitSh = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );
+  		const vec4 bitMsk = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );
+   		vec4 res = fract( depth * bitSh );
    		res -= res.xxyz * bitMsk;
 		return res;
 		
 	}
 
-	float unpack1K (vec4 color) {
+	float unpack1K ( vec4 color ) {
 	
-		const vec4 bitSh = vec4(1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0);
-		return dot(color, bitSh) * 1000.0;
+		const vec4 bitSh = vec4( 1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0 );
+		return dot( color, bitSh ) * 1000.0;
 		
 	}
 
-	vec3 gridSamplingDisk[20];
+	vec3 gridSamplingDisk[ 20 ];
 	int gridSamplingInitialized = 0;
 
 	void initGridSamplingDisk(){
@@ -67,17 +63,35 @@
 		
 	}
 
-	float getCubeMapFloat(in samplerCube cube, in vec3 baseDirection){
-	
-		vec4 data =  textureCube(cube,  baseDirection);
+	float getCubeShadowMapFloat( const in int cubeIndex, in vec3 baseDirection ){
+
+		vec4 data = vec4( 0, 0, 0, 0 );
+
+		// This loop may seem silly and unnecessary, but we can't use @cubeIndex to access @shadowCube
+		// directly, since its bounds are unknown to the compiler. The compiler
+		// knows the bounds of the loop variable 'i' so we CAN use that to index
+		// into @shadowCube. The alternative to this is to send the samplerCube directly
+		// to this function as a parameter, but come drivers don't allow that.
+
+		for( int i = 0; i < MAX_SHADOWS; i++ ) {
+
+			if( i == cubeIndex ){
+
+				data =  textureCube(shadowCube[ i ],  baseDirection);
+				break;
+
+			}
+
+		}
+
 		float dist = unpack1K( data );
 		return dist;	
 		
 	}	
 
-	float sampleCubeShadowMapPCF(in samplerCube cube, in vec3 baseDirection, in float curDistance, in float texSize, float softness){
+	float sampleCubeShadowMapPCF( const in int cubeIndex, in vec3 baseDirection, in float curDistance, in float texSize, float softness ){
 	
-		if( gridSamplingInitialized == 0){
+		if( gridSamplingInitialized == 0 ){
 		
 			initGridSamplingDisk();
 			gridSamplingInitialized = 1;
@@ -89,17 +103,17 @@
 		float numSamples = 0.0;
 		float shadowFactor = 0.0;
 
-		float dist = getCubeMapFloat(cube, baseDirection);
-		if ( curDistance >= dist)
+		float dist = getCubeShadowMapFloat( cubeIndex, baseDirection );
+		if ( curDistance >= dist )
 			shadowFactor += 1.0;
 		numSamples += 1.0;
 		
 		// evaluate each sampling direction
-		for(int i=0; i<20; i++){
-		
-			vec3 offset = gridSamplingDisk[i] * diskRadius * texSize;
-			dist = getCubeMapFloat(cube, vec3(baseDirection + offset));
-			if ( curDistance >= dist)
+		for( int i = 0; i < 20; i++ ){
+		 
+			vec3 offset = gridSamplingDisk[ i ] * diskRadius * texSize;
+			dist = getCubeShadowMapFloat( cubeIndex, vec3( baseDirection + offset ) );
+			if ( curDistance >= dist )
 				shadowFactor += 1.0;
 			numSamples += 1.0;
 			
@@ -107,6 +121,7 @@
 
 		shadowFactor /= numSamples;
 		return shadowFactor;
+
 	}
 
 #endif

+ 1 - 1
src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl

@@ -1 +1 @@
-#ifdef USE_SHADOWMAP

	varying vec4 vShadowCoord[ MAX_SHADOWS ];
	varying vec4 vWPosition[ MAX_SHADOWS ];
	uniform mat4 shadowMatrix[ MAX_SHADOWS ];
	uniform vec3 shadowLightPosition[ MAX_SHADOWS ];

#endif
+#ifdef USE_SHADOWMAP

	uniform float shadowDarkness[ MAX_SHADOWS ];
	uniform mat4 shadowMatrix[ MAX_SHADOWS ];
	varying vec4 vShadowCoord[ MAX_SHADOWS ];

#endif

+ 1 - 1
src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl

@@ -1 +1 @@
-#ifdef USE_SHADOWMAP

	for( int i = 0; i < MAX_SHADOWS; i ++ ) {

		vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;
		vWPosition[ i ] = worldPosition;

	}

#endif
+#ifdef USE_SHADOWMAP

	for( int i = 0; i < MAX_SHADOWS; i ++ ) {

		#if MAX_POINT_LIGHTS > 0

			// if shadowDarkness[ i ] < 0.0, that means we have a point light with a cube
			// shadow map
			if( shadowDarkness[ i ] < 0.0 ){

				// When we have a point light, the @shadowMatrix uniform is used to store
				// the inverse of the view matrix, so that we can get the world-space 
				// position of the light.

				vec4 lightPositionWorld = ( shadowMatrix[ i ] * vec4( pointLightPosition[ i ], 1.0 ));
				vec4 distanceToLight = worldPosition - lightPositionWorld;
				distanceToLight.w = 1.0;
				 
				// We also repurpose vShadowCoord to hold the distance in world space from the
				// light to the vertex. This value will be interpolated correctly in the fragment shader.

				vShadowCoord[ i ] = distanceToLight;

			} else {

				vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;

			}

		#else

			vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;

		#endif

	}

#endif

+ 3 - 10
src/renderers/shaders/ShaderLib.js

@@ -856,21 +856,14 @@ THREE.ShaderLib = {
 
 			"void main() {",
 
-			"#ifdef USE_SKINNING",
-
-			"	vWorldPosition = modelMatrix * skinned;",
-
-			"#else",
-
-			"	vWorldPosition = modelMatrix * vec4( position, 1.0 );",
-
-			"#endif",
-
 				THREE.ShaderChunk[ "skinbase_vertex" ],
 				THREE.ShaderChunk[ "begin_vertex" ],
 				THREE.ShaderChunk[ "morphtarget_vertex" ],
 				THREE.ShaderChunk[ "skinning_vertex" ],
 				THREE.ShaderChunk[ "project_vertex" ],
+				THREE.ShaderChunk[ "worldpos_vertex" ],
+
+				"vWorldPosition = worldPosition;",
 
 			"}"
 

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

@@ -117,7 +117,6 @@ THREE.UniformsLib = {
 
 	shadowmap: {
 
-		"isShadowCube": { type: "iv1", value: [] },
 		"shadowCube": { type: "tv", value: [] },
 		"shadowMap": { type: "tv", value: [] },
 
@@ -127,7 +126,6 @@ THREE.UniformsLib = {
 		"shadowDarkness": { type: "fv1", value: [] },
 
 		"shadowMatrix" : { type: "m4v", value: [] },
-		"shadowLightPosition" : { type: "fv", value: [] }
 
 	}
 

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

@@ -14,7 +14,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 	_max = new THREE.Vector3(),
 
 	_lookTarget = new THREE.Vector3(),
-	_lightPosition = new THREE.Vector3(),
+	_lightPositionWorld = new THREE.Vector3(),
 
 	_renderList = [];
 
@@ -224,8 +224,8 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 			var shadowMatrix = light.shadowMatrix;
 			var shadowCamera = light.shadowCamera;
 
-			_lightPosition.setFromMatrixPosition( light.matrixWorld );
-			shadowCamera.position.copy( _lightPosition);
+			_lightPositionWorld.setFromMatrixPosition( light.matrixWorld );
+			shadowCamera.position.copy( _lightPositionWorld);
 
 			// render shadow map for each cube face (if omni-directional) or
 			// run a single pass if not
@@ -306,7 +306,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 
 							if ( groupMaterial.visible === true ) {
 
-								var depthMaterial = getDepthMaterial( object, groupMaterial, isCube, _lightPosition );
+								var depthMaterial = getDepthMaterial( object, groupMaterial, isCube, _lightPositionWorld );
 								_renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial , object, group );
 
 							}
@@ -345,7 +345,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 
 	};
 
-	function getDepthMaterial( object, material, isCube, lightPosition) {
+	function getDepthMaterial( object, material, isCube, lightPositionWorld) {
 
 		var geometry = object.geometry;
 
@@ -400,7 +400,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 
 			if( newMaterial.uniforms.lightPos ){
 
-				newMaterial.uniforms.lightPos.value.copy( lightPosition );
+				newMaterial.uniforms.lightPos.value.copy( lightPositionWorld );
 			}
 		}