浏览代码

Point light shadow mapping now utilizes cube-to-2D projection, avoiding the need for a samplerCube in the shader.

mkkellogg 10 年之前
父节点
当前提交
f476908380

+ 15 - 5
examples/webgl_shadowmap_omnidirectional.html

@@ -75,12 +75,12 @@
 				pointLight.shadowCameraFar = 30;
 				pointLight.shadowDarkness = 0.5;
 				pointLight.shadowCameraVisible = true;
-				pointLight.shadowMapWidth = 1024;
+				pointLight.shadowMapWidth = 2048;
 				pointLight.shadowMapHeight = 1024;
 				pointLight.name = 'Point Light';
 				scene.add( pointLight );
 
-				dirLight = new THREE.DirectionalLight( 0xffffff, 1 );
+				/*dirLight = new THREE.DirectionalLight( 0xffffff, 1 );
 				dirLight.position.set( 0, 50, 0 );
 				dirLight.castShadow = true;
 				dirLight.shadowCameraNear = 0.01;
@@ -94,7 +94,7 @@
 				dirLight.shadowMapWidth = 1024;
 				dirLight.shadowMapHeight = 1024;
 				dirLight.name = 'Dir. Light';
-				//scene.add( dirLight );			
+				scene.add( dirLight );*/			
 
 				cubeMaterial = new THREE.MeshPhongMaterial( {
 					color: 0xff0000,
@@ -128,7 +128,7 @@
 				var torusGeometry =  new THREE.TorusKnotGeometry( 25, 8, 75, 20 );
 				torusKnot = new THREE.Mesh( torusGeometry, cubeMaterial );
 				torusKnot.scale.multiplyScalar( 1 / 18 );
-				torusKnot.position.y = 3;
+				torusKnot.position.set( -1, 3, -4 );
 				torusKnot.castShadow = true;
 				torusKnot.receiveShadow = true;
 				scene.add( torusKnot );
@@ -185,6 +185,16 @@
 				wall.rotation.y = Math.PI / 2;
 				wall.rotation.z = Math.PI / 2;
 
+				/*wall = new THREE.Mesh( wallGeometry, wallMaterial );
+				wall.name = "front wall";
+				wall.scale.multiplyScalar( 3 );
+				wall.castShadow = false;
+				wall.receiveShadow = true;				
+				scene.add( wall );
+				wall.position.set( 0, 10, 14 );
+				wall.rotation.y = Math.PI / 2;
+				wall.rotation.z = Math.PI / 2;*/
+
 				var sphereGeometry = new THREE.SphereGeometry( 1, 32, 32 );
 				var material = new THREE.MeshBasicMaterial( { color: 0xffffff } );
 				var sphere = new THREE.Mesh( sphereGeometry, material );
@@ -206,7 +216,7 @@
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setClearColor( 0x000000 );				
 				renderer.shadowMap.enabled = true;
-				renderer.shadowMap.type = THREE.BasicShadowMap;
+				renderer.shadowMap.type = THREE.PCFShadowMap;
 
 				// Mouse control
 				controls = new THREE.OrbitControls( camera, renderer.domElement );

+ 22 - 14
src/renderers/WebGLRenderer.js

@@ -374,6 +374,16 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	};
 
+	this.getViewport = function ( dimensions ) {
+
+		dimensions.x = _viewportX;
+		dimensions.y = _viewportY;
+
+		dimensions.z = _viewportWidth;
+		dimensions.w = _viewportHeight;
+
+	};
+
 	this.setScissor = function ( x, y, width, height ) {
 
 		_gl.scissor(
@@ -1965,30 +1975,26 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				if ( ! light.castShadow ) continue;
 
-				if ( light instanceof THREE.PointLight || light instanceof THREE.SpotLight || light instanceof THREE.DirectionalLight  ) {
+				if ( light instanceof THREE.PointLight || light instanceof THREE.SpotLight || light instanceof THREE.DirectionalLight ) {
 
-					if( light instanceof THREE.PointLight ){
-
-						uniforms.shadowCube.value[ j ] = light.shadowMap;
-						uniforms.shadowMap.value[ j ] = null;
+					if ( light instanceof THREE.PointLight ) {
 
 						// for point lights we set the sign of the shadowDarkness uniform to be negative
-						uniforms.shadowDarkness.value[ j ] = -light.shadowDarkness;
+						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 
+						// 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.shadowCube.value[ j ] = null;
 						uniforms.shadowMatrix.value[ j ] = light.shadowMatrix;
 						uniforms.shadowDarkness.value[ j ] = light.shadowDarkness;
 
-					}					
+					}
 
+					uniforms.shadowMap.value[ j ] =  light.shadowMap;
 					uniforms.shadowMapSize.value[ j ] = light.shadowMapSize;
 					uniforms.shadowBias.value[ j ] = light.shadowBias;
 
@@ -2347,9 +2353,11 @@ THREE.WebGLRenderer = function ( parameters ) {
 						textureUnit = uniform._array[ i ];
 
 						if ( ! texture ) continue;
-						
+
 						if ( texture instanceof THREE.CubeTexture ||
-						   ( texture.image instanceof Array && texture.image.length === 6 ) ) { // CompressedTexture can have Array in image :/
+						   ( texture.image instanceof Array && texture.image.length === 6 ) ) {
+
+							// CompressedTexture can have Array in image :/
 
 							setCubeTexture( texture, textureUnit );
 
@@ -3153,10 +3161,10 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-		if( isCube ){
+		if ( isCube ) {
 
 			var renderTargetProperties = properties.get( renderTarget );
-			_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0,  _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, renderTargetProperties.__webglTexture , 0 );
+			_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0,  _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, renderTargetProperties.__webglTexture, 0 );
 
 		}
 

+ 43 - 35
src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl

@@ -24,7 +24,9 @@
 		// 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;
+
+		float texelSizeX =  1.0 / shadowMapSize[ i ].x;
+		float texelSizeY =  1.0 / shadowMapSize[ i ].y;
 
 		vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;
 		float shadow = 0.0;
@@ -49,16 +51,20 @@
 
 					if( isPointLight ) {
 
+						float cubeTexelSize = 1.0 / ( shadowMapSize[ i ].x * 0.25 );
+						vec3 baseDirection3D = normalize( lightToPosition );
+						vec2 baseDirection2D = cubeToUV( baseDirection3D, texelSizeX, texelSizeY );
+
 						initGridSamplingDisk();
 
-						float diskRadius = 1.5;
+						float diskRadius = 1.25;
 						float numSamples = 1.0;
 						shadow = 0.0;
 
 						vec3 baseDirection = normalize( lightToPosition );
 						float curDistance = length( lightToPosition );
 
-						float dist = unpack1K( textureCube( shadowCube[ i ],  baseDirection ) );
+						float dist = unpack1K( texture2D( shadowMap[ i ],  baseDirection2D ) ) + 0.1;
 						if ( curDistance >= dist )
 							shadow += 1.0;
 						
@@ -66,15 +72,17 @@
 						for( int s = 0; s < 20; s++ ) {
 						 
 							vec3 offset = gridSamplingDisk[ s ] * diskRadius * cubeTexelSize;
-							dist = unpack1K( textureCube( shadowCube[ i ],  vec3( baseDirection + offset ) ) );
+							vec3 adjustedBaseDirection3D = baseDirection3D + offset;
+							vec2 adjustedBaseDirection2D = cubeToUV( adjustedBaseDirection3D, texelSizeX, texelSizeY );
+							dist = unpack1K( texture2D( shadowMap[ i ],  adjustedBaseDirection2D ) ) + 0.1;
 							if ( curDistance >= dist )
 								shadow += 1.0;
 							numSamples += 1.0;
-							
+
 						}
 
 						shadow /= numSamples;
-						
+
 					} else {
 
 				#endif
@@ -86,30 +94,22 @@
 						/*
 								// nested loops breaks shader compiler / validator on some ATI cards when using OpenGL
 								// must enroll loop manually
-
 							for ( float y = -1.25; y <= 1.25; y += 1.25 )
 								for ( float x = -1.25; x <= 1.25; x += 1.25 ) {
-
 									vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );
-
 											// doesn't seem to produce any noticeable visual difference compared to simple texture2D lookup
 											//vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );
-
 									float fDepth = unpackDepth( rgbaDepth );
-
 									if ( fDepth < shadowCoord.z )
 										shadow += 1.0;
-
 							}
-
 							shadow /= 9.0;
-
 						*/
 
 						const float shadowDelta = 1.0 / 9.0;
 
-						float xPixelOffset = 1.0 / shadowMapSize[ i ].x;
-						float yPixelOffset = 1.0 / shadowMapSize[ i ].y;
+						float xPixelOffset = texelSizeX;
+						float yPixelOffset = texelSizeY;
 
 						float dx0 = -1.25 * xPixelOffset;
 						float dy0 = -1.25 * yPixelOffset;
@@ -150,40 +150,46 @@
 				#endif
 
 				shadowColor = shadowColor * vec3( ( 1.0 - realShadowDarkness * shadow ) );
-				
+
 			#elif defined( SHADOWMAP_TYPE_PCF_SOFT )
-	
-				#if defined(POINT_LIGHT_SHADOWS)	
+
+				#if defined(POINT_LIGHT_SHADOWS)
 
 					if( isPointLight ) {
 
+						float cubeTexelSize = 1.0 / ( shadowMapSize[ i ].x * 0.25 );
+						vec3 baseDirection3D = normalize( lightToPosition );
+						vec2 baseDirection2D = cubeToUV( baseDirection3D, texelSizeX, texelSizeY );
+
 						initGridSamplingDisk();
 
-						float diskRadius = 2.5;
+						float diskRadius = 2.25;
 						float numSamples = 1.0;
 						shadow = 0.0;
 
 						vec3 baseDirection = normalize( lightToPosition );
 						float curDistance = length( lightToPosition );
 
-						float dist = unpack1K( textureCube( shadowCube[ i ],  baseDirection ) );
+						float dist = unpack1K( texture2D( shadowMap[ i ],  baseDirection2D ) ) + 0.1;
 						if ( curDistance >= dist )
 							shadow += 1.0;
-						
+
 						// evaluate each sampling direction
 						for( int s = 0; s < 20; s++ ) {
-						 
+
 							vec3 offset = gridSamplingDisk[ s ] * diskRadius * cubeTexelSize;
-							dist = unpack1K( textureCube( shadowCube[ i ],  vec3( baseDirection + offset ) ) );
+							vec3 adjustedBaseDirection3D = baseDirection3D + offset;
+							vec2 adjustedBaseDirection2D = cubeToUV( adjustedBaseDirection3D, texelSizeX, texelSizeY );
+							dist = unpack1K( texture2D( shadowMap[ i ],  adjustedBaseDirection2D ) ) + 0.1;
 							if ( curDistance >= dist )
 								shadow += 1.0;
 							numSamples += 1.0;
-							
+
 						}
 
 						shadow /= numSamples;
 
-					} else { 
+					} else {
 
 				#endif
 
@@ -191,8 +197,8 @@
 						// (9 pixel kernel)
 						// http://fabiensanglard.net/shadowmappingPCF/
 
-						float xPixelOffset = 1.0 / shadowMapSize[ i ].x;
-						float yPixelOffset = 1.0 / shadowMapSize[ i ].y;
+						float xPixelOffset = texelSizeX;
+						float yPixelOffset = texelSizeY;
 
 						float dx0 = -1.0 * xPixelOffset;
 						float dy0 = -1.0 * yPixelOffset;
@@ -247,17 +253,19 @@
 
 				#if defined(POINT_LIGHT_SHADOWS)
 
-					if( isPointLight ) {		
+					if( isPointLight ) {
+
+						vec3 baseDirection3D = normalize( lightToPosition );
+						vec2 baseDirection2D = cubeToUV( baseDirection3D, texelSizeX, texelSizeY );
+						vec4 data = texture2D( shadowMap[ i ],  baseDirection2D );
+						float dist = unpack1K( data ) + 0.1;
+						if ( length( lightToPosition ) >= dist)
+							shadowColor = shadowColor * vec3( 1.0 - realShadowDarkness );
 
-							vec4 data = textureCube( shadowCube[ i ],  normalize( lightToPosition ) );
-							float dist = unpack1K( data );
-							if ( length( lightToPosition ) >= dist)
-								shadowColor = shadowColor * vec3( 1.0 - realShadowDarkness );
-							
 					} else {
 
 				#endif
-				
+
 						vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );
 						float fDepth = unpackDepth( rgbaDepth );
 

+ 67 - 14
src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl

@@ -18,19 +18,6 @@
 
 	#if defined(POINT_LIGHT_SHADOWS)
 
-		uniform samplerCube shadowCube[ MAX_SHADOWS ];
-
-		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 );
-	   		res -= res.xxyz * bitMsk;
-			return res;
-			
-		}
-
 		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 );
@@ -38,6 +25,72 @@
 			
 		}
 
+		vec2 cubeToUV( vec3 v, float texelSizeX, float texelSizeY ) {
+
+			// Horizontal cross layout:
+			const vec2 Squares = vec2( 4.0, 2.0 );
+			const vec2 Center = vec2( 1.0, 0.0 );
+
+			// Size of a square in UV space:
+			const vec2 TexSquareSize = 1.0 / Squares;
+
+			// UV space offset of the center of the center square of the cross:
+			const vec2 TexCoordOffs = TexSquareSize * ( 0.5 + Center );
+
+			// Factors to scale square space (-1..+1 per square) to UV space:
+			const vec2 TexSquareScale = TexSquareSize * 0.5;
+
+			// Just less than a texel in square space when divided by resolution:
+			const float TexEps = 1.5; // = min(Squares.x, Squares.y) - 0.5;
+
+			vec3 absV = abs( v );
+			vec3 sgnV = sign( v );
+
+			// Intersect unit cube
+
+			float scale = 1.0 / max( absV.x, max( absV.y, absV.z ) );
+
+			v *= scale; 
+			absV *= scale;
+
+			// Determine gate factors
+
+			// gate.? is one when on left / right, bottom / top, back
+			float eps = TexEps * texelSizeY;
+			vec3 gate = step( 1.0 - eps, vec3( absV.xy, v.z ) );
+
+			// prefer any square over bottom / top
+			float notX = 1. - gate.x;
+			float notZ = 1. - gate.z;
+			gate.y *= notX * notZ;
+			// prefer back over side
+			gate.x *= notZ;
+
+			// Unwrap
+
+			// start with xy coordinates
+			vec2 planar = v.xy;
+
+			// stop at the last texel (can use a factor of 1.0 for NEAREST)
+			float yTexelSize = 2.0 * Squares.y * texelSizeY;
+			float yAdjusted = planar.y * ( 1.0 - yTexelSize );
+			planar.y = yAdjusted;
+			planar.y -= gate.y * yAdjusted;
+
+			// unwrap left / right, top / bottom
+			planar.x += gate.x * ( sgnV.x + v.z * sgnV.x );
+
+			planar.x += gate.y * ( -sgnV.y * 2.0 );
+			planar.y += gate.y * ( 2.0  + ( v.z * sgnV.y ) );
+
+			// unwrap back
+			planar.x += gate.z * ( 4.0 - 2.0 * planar.x );
+
+			// adjust to UV space
+			return TexCoordOffs + planar * TexSquareScale; 
+			
+		}
+
 		vec3 gridSamplingDisk[ 20 ];
 		bool gridSamplingInitialized = false;
 
@@ -46,7 +99,7 @@
 			if( gridSamplingInitialized ){
 
 				return;
-				
+
 			}
 
 			gridSamplingDisk[0] = vec3(1, 1, 1);

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

@@ -1 +1,7 @@
-#ifdef USE_SHADOWMAP

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

#endif
+#ifdef USE_SHADOWMAP
+
+	uniform float shadowDarkness[ MAX_SHADOWS ];
+	uniform mat4 shadowMatrix[ MAX_SHADOWS ];
+	varying vec4 vShadowCoord[ MAX_SHADOWS ];
+
+#endif

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

@@ -1 +1,38 @@
-#ifdef USE_SHADOWMAP

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

		#if defined(POINT_LIGHT_SHADOWS)

			// 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
+#ifdef USE_SHADOWMAP
+
+	for( int i = 0; i < MAX_SHADOWS; i ++ ) {
+
+		#if defined(POINT_LIGHT_SHADOWS)
+
+			// 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

+ 6 - 5
src/renderers/shaders/ShaderLib.js

@@ -842,7 +842,7 @@ THREE.ShaderLib = {
 
 		uniforms: {
 
-			"lightPos": { type: "v3", value: new THREE.Vector3(0,0,0) }
+			"lightPos": { type: "v3", value: new THREE.Vector3( 0, 0, 0 ) }
 
 		},
 
@@ -867,7 +867,7 @@ THREE.ShaderLib = {
 
 			"}"
 
-		].join("\n"),
+		].join( "\n" ),
 
 		fragmentShader: [
 
@@ -885,8 +885,8 @@ THREE.ShaderLib = {
    			"	res -= res.xxyz * bitMsk;",
    			"	return res; ",
 
-			"}",			
-			
+			"}",
+
 			"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 );",
@@ -900,8 +900,9 @@ THREE.ShaderLib = {
 
 			"}"
 
-		].join("\n")
+		].join( "\n" )
 
 	}
 
 };
+

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

@@ -117,15 +117,13 @@ THREE.UniformsLib = {
 
 	shadowmap: {
 
-		"shadowCube": { type: "tv", value: [] },
 		"shadowMap": { type: "tv", value: [] },
-
 		"shadowMapSize": { type: "v2v", value: [] },
 
 		"shadowBias" : { type: "fv1", value: [] },
 		"shadowDarkness": { type: "fv1", value: [] },
 
-		"shadowMatrix" : { type: "m4v", value: [] },
+		"shadowMatrix" : { type: "m4v", value: [] }
 
 	}
 

+ 68 - 47
src/renderers/webgl/WebGLShadowMap.js

@@ -21,13 +21,16 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 	var _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin,
 	_distanceMaterial, _distanceMaterialMorph, _distanceMaterialSkin, _distanceMaterialMorphSkin;
 
-	var cubeDirections = [new THREE.Vector3(1,0,0), new THREE.Vector3(-1,0,0), new THREE.Vector3(0,1,0),
-						  new THREE.Vector3(0,-1,0), new THREE.Vector3(0,0,1), new THREE.Vector3(0,0,-1)];
+	var cubeDirections = [ new THREE.Vector3( 1, 0, 0 ), new THREE.Vector3( - 1, 0, 0 ), new THREE.Vector3( 0, 0, 1 ), 
+						   new THREE.Vector3( 0, 0, - 1 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, - 1, 0 ) ];
 
-	var cubeUps = [new THREE.Vector3(0,-1,0), new THREE.Vector3(0,-1,0), new THREE.Vector3(0,0,1),
-				   new THREE.Vector3(0,0,-1), new THREE.Vector3(0,-1,0), new THREE.Vector3(0,-1,0)];
+	var cubeUps = [ new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ), 
+					new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 0, 1 ),	new THREE.Vector3( 0, 0, - 1 ) ];
 
-    var _vector4 = new THREE.Vector4();
+	var cube2DViewPorts = [ new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4(),
+				   			new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4() ];
+
+	var _vector4 = new THREE.Vector4();
 
 	// init
 
@@ -118,7 +121,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 
 	this.render = function ( scene ) {
 
-		var faceCount, isCube;
+		var faceCount, isPointLight;
 
 		if ( scope.enabled === false ) return;
 		if ( scope.autoUpdate === false && scope.needsUpdate === false ) return;
@@ -152,12 +155,28 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 			if ( light instanceof THREE.PointLight ) {
 
 				faceCount = 6;
-				isCube = true;
+				isPointLight = true;
+
+				var vpWidth = light.shadowMapWidth / 4.0;
+				var vpHeight = light.shadowMapHeight / 2.0;
+
+				// positive X
+				cube2DViewPorts[ 0 ].set( vpWidth * 2, 0, vpWidth, vpHeight );
+				// negative X
+				cube2DViewPorts[ 1 ].set( 0, 0, vpWidth, vpHeight );
+				// positive Z
+				cube2DViewPorts[ 2 ].set( vpWidth * 3, 0, vpWidth, vpHeight );
+				// negative Z
+				cube2DViewPorts[ 3 ].set( vpWidth, 0, vpWidth, vpHeight );
+				// positive Y
+				cube2DViewPorts[ 4 ].set( 0, vpHeight, vpWidth, vpHeight );
+				// negative Y
+				cube2DViewPorts[ 5 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight );
 
 			} else {
 
 				faceCount = 1;
-				isCube = false;
+				isPointLight = false;
 
 			}
 
@@ -167,7 +186,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 
 				var shadowFilter = THREE.LinearFilter;
 
-				if ( scope.type === THREE.PCFSoftShadowMap ) {
+				if ( scope.type === THREE.PCFSoftShadowMap || light instanceof THREE.PointLight ) {
 
 					shadowFilter = THREE.NearestFilter;
 
@@ -175,17 +194,8 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 
 				var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat };
 
-				if ( isCube ) {
-
-					light.shadowMap = new THREE.WebGLRenderTargetCube( light.shadowMapWidth, light.shadowMapWidth, pars );
-					light.shadowMapSize = new  THREE.Vector2( light.shadowMapWidth, light.shadowMapWidth );
-
-				} else {
-
-					light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars );
-					light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight );
-
-				}	
+				light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars );
+				light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight );
 
 				light.shadowMatrix = new THREE.Matrix4();
 
@@ -203,7 +213,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 
 				} else {
 
-					light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar );
+					light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, 1.0, light.shadowCameraNear, light.shadowCameraFar );
 
 				}
 
@@ -225,27 +235,34 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 			var shadowCamera = light.shadowCamera;
 
 			_lightPositionWorld.setFromMatrixPosition( light.matrixWorld );
-			shadowCamera.position.copy( _lightPositionWorld);
+			shadowCamera.position.copy( _lightPositionWorld );
+
+			// save the existing viewport so it can be restored later
+			_renderer.getViewport( _vector4 );
+
+			_renderer.setRenderTarget( shadowMap );
+			_renderer.clear();		
 
 			// render shadow map for each cube face (if omni-directional) or
 			// run a single pass if not
 
-			for ( var face = 0; face < faceCount; face++ ){				
-		
-				if( isCube ){
+			for ( var face = 0; face < faceCount; face ++ ) {
+
+				if ( isPointLight ) {
 
 					_lookTarget.copy( shadowCamera.position );
-					_lookTarget.add( cubeDirections[face] );
-					shadowCamera.up.copy( cubeUps[face] );
-					shadowCamera.lookAt( _lookTarget );	
-					shadowMap.activeCubeFace = face;
+					_lookTarget.add( cubeDirections[ face ] );
+					shadowCamera.up.copy( cubeUps[ face ] );
+					shadowCamera.lookAt( _lookTarget );
+					var vpDimensions = cube2DViewPorts[ face ];
+					_renderer.setViewport( vpDimensions.x, vpDimensions.y, vpDimensions.z, vpDimensions.w );
 
 				} else {
 
 					_lookTarget.setFromMatrixPosition( light.target.matrixWorld );
 					shadowCamera.lookAt( _lookTarget );
 
-				}	
+				}
 
 				shadowCamera.updateMatrixWorld();
 				shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld );
@@ -270,17 +287,13 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 				_projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
 				_frustum.setFromMatrix( _projScreenMatrix );
 
-				// render shadow map	
-
-				_renderer.setRenderTarget( shadowMap );
-				_renderer.clear();
-
 				// set object matrices & frustum culling
 
 				_renderList.length = 0;
 
 				projectObject( scene, shadowCamera );
 
+				// render shadow map
 				// render regular objects
 
 				for ( var j = 0, jl = _renderList.length; j < jl; j ++ ) {
@@ -301,8 +314,8 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 
 							if ( groupMaterial.visible === true ) {
 
-								var depthMaterial = getDepthMaterial( object, groupMaterial, isCube, _lightPositionWorld );
-								_renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial , object, group );
+								var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld );
+								_renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial, object, group );
 
 							}
 
@@ -310,7 +323,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 
 					} else {
 
-						var depthMaterial = getDepthMaterial( object, material, isCube, _lightPosition);						
+						var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld );
 						_renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial, object, null );
 
 					}
@@ -318,6 +331,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 				}
 
 			}
+
 		}
 
 		// restore GL state
@@ -334,13 +348,15 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 
 		}
 
+		_renderer.setViewport( _vector4.x, _vector4.y, _vector4.z, _vector4.w );
+
 		_renderer.resetGLState();
 
 		scope.needsUpdate = false;
 
 	};
 
-	function getDepthMaterial( object, material, isCube, lightPositionWorld) {
+	function getDepthMaterial( object, material, isPointLight, lightPositionWorld ) {
 
 		var geometry = object.geometry;
 
@@ -350,27 +366,29 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 		var newMaterial;
 
 		var depthMaterial = _depthMaterial;
-		var depthMaterialMorph = _depthMaterialMorph; 
-		var depthMaterialSkin = _depthMaterialSkin; 
+		var depthMaterialMorph = _depthMaterialMorph;
+		var depthMaterialSkin = _depthMaterialSkin;
 		var depthMaterialMorphSkin = _depthMaterialMorphSkin;
 
-		if ( isCube ){
+		if ( isPointLight ) {
 
 			depthMaterial = _distanceMaterial;
-			depthMaterialMorph = _distanceMaterialMorph; 
-			depthMaterialSkin = _distanceMaterialSkin; 
+			depthMaterialMorph = _distanceMaterialMorph;
+			depthMaterialSkin = _distanceMaterialSkin;
 			depthMaterialMorphSkin = _distanceMaterialMorphSkin;
+
 		}
 
 		if ( object.customDepthMaterial || object.customDistanceMaterial ) {
 
-			if ( isCube ){
+			if ( isPointLight ) {
 
 				newMaterial = object.customDistanceMaterial;
 
 			} else {
 
 				newMaterial = object.customDepthMaterial;
+
 			}
 
 		} else if ( useSkinning ) {
@@ -391,12 +409,14 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 		newMaterial.wireframe = material.wireframe;
 		newMaterial.wireframeLinewidth = material.wireframeLinewidth;
 
-		if ( isCube ){
+		if ( isPointLight ) {
 
-			if( newMaterial.uniforms.lightPos ){
+			if ( newMaterial.uniforms.lightPos ) {
 
 				newMaterial.uniforms.lightPos.value.copy( lightPositionWorld );
+
 			}
+
 		}
 
 		return newMaterial;
@@ -414,6 +434,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 				var material = object.material;
 
 				if ( material.visible === true ) {
+
 					object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
 					_renderList.push( object );