Browse Source

Merge pull request #17228 from supereggbert/shadowcleanup

Shadow cleanup and refactor
Mr.doob 6 years ago
parent
commit
757ec5dfd6

+ 29 - 47
docs/api/en/lights/shadows/LightShadow.html

@@ -12,60 +12,18 @@
 		<h1>[name]</h1>
 
 		<p class="desc">
-			This is used internally by [page:PointLight PointLights] for calculating shadows, and also serves as
-			a base class for the other shadow classes.
+			Serves as a base class for the other shadow classes.
 		</p>
 
 
-		<h2>Example</h2>
-		<p>
-			<code>
-//Create a WebGLRenderer and turn on shadows in the renderer
-var renderer = new THREE.WebGLRenderer();
-renderer.shadowMap.enabled = true;
-renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
-
-//Create a PointLight and turn on shadows for the light
-var light = new THREE.PointLight( 0xffffff, 1, 100 );
-light.position.set( 0, 10, 0 );
-light.castShadow = true;            // default false
-scene.add( light );
-
-//Set up shadow properties for the light
-light.shadow.mapSize.width = 512;  // default
-light.shadow.mapSize.height = 512; // default
-light.shadow.camera.near = 0.5;       // default
-light.shadow.camera.far = 500      // default
-
-//Create a sphere that cast shadows (but does not receive them)
-var sphereGeometry = new THREE.SphereBufferGeometry( 5, 32, 32 );
-var sphereMaterial = new THREE.MeshStandardMaterial( { color: 0xff0000 } );
-var sphere = new THREE.Mesh( sphereGeometry, sphereMaterial );
-sphere.castShadow = true; //default is false
-sphere.receiveShadow = false; //default
-scene.add( sphere );
-
-//Create a plane that receives shadows (but does not cast them)
-var planeGeometry = new THREE.PlaneBufferGeometry( 20, 20, 32, 32 );
-var planeMaterial = new THREE.MeshStandardMaterial( { color: 0x00ff00 } )
-var plane = new THREE.Mesh( planeGeometry, planeMaterial );
-plane.receiveShadow = true;
-scene.add( plane );
-
-//Create a helper for the shadow camera (optional)
-var helper = new THREE.CameraHelper( light.shadow.camera );
-scene.add( helper );
-			</code>
-		</p>
-
 		<h2>Constructor</h2>
 
 		<h3>[name]( [param:Camera camera] )</h3>
 		<p>
 		[page:Camera camera] - the light's view of the world.<br /><br />
 
-		Create a new [name]. This is not intended to be called directly - it is called
-		internally by [page:PointLight] or used as a base class by other light shadows.
+		Create a new [name]. This is not intended to be called directly - it is used as a base class by 
+		other light shadows.
 		</p>
 
 		<h2>Properties</h2>
@@ -88,7 +46,6 @@ scene.add( helper );
 			in shadow. Computed internally during rendering.
 		</p>
 
-
 		<h3>[property:Vector2 mapSize]</h3>
 		<p>
 			A [Page:Vector2] defining the width and height of the shadow map.<br /><br />
@@ -118,10 +75,35 @@ scene.add( helper );
 
 
 		<h2>Methods</h2>
+
+		<h3>[method:Vector2 getFrameExtents]()</h3>
+		<p>
+		Used internally by the renderer to extend the shadow map to contain all viewports
+		</p>
+
+		<h3>[method:null updateMatrices]( [param:Light light], [param:Camera viewCamera], [param:number viewportIndex])</h3>
+		<p>
+		Update the matrices for the camera and shadow, used internally by the renderer.<br /><br />
+
+		light -- the light for which the shadow is being rendered.<br />
+		viewCamera -- the camera view for which the shadow is being rendered.<br />
+		viewportIndex -- calculates the matrix for this viewport
+		</p>
+
+		<h3>[method:Frustum getFrustum]()</h3>
+		<p>
+		Gets the shadow cameras frustum. Used internally by the renderer to cull objects.
+		</p>
+
+		<h3>[method:number getViewportCount]()</h3>
+		<p>
+		Used internally by the renderer to get the number of viewports that need to be rendered for this shadow.
+		</p>
+
 		<h3>[method:LightShadow copy]( [param:LightShadow source] )</h3>
 		<p>
 		Copies value of all the properties from the [page:LightShadow source] to this
-		SpotLight.
+		Light.
 		</p>
 
 		<h3>[method:LightShadow clone]()</h3>

+ 81 - 0
docs/api/en/lights/shadows/PointLightShadow.html

@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+
+		<h1>[name]</h1>
+
+		<p class="desc">
+			This is used internally by [page:PointLight PointLights] for calculating shadows
+		</p>
+
+
+		<h2>Example</h2>
+		<p>
+			<code>
+//Create a WebGLRenderer and turn on shadows in the renderer
+var renderer = new THREE.WebGLRenderer();
+renderer.shadowMap.enabled = true;
+renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
+
+//Create a PointLight and turn on shadows for the light
+var light = new THREE.PointLight( 0xffffff, 1, 100 );
+light.position.set( 0, 10, 0 );
+light.castShadow = true;            // default false
+scene.add( light );
+
+//Set up shadow properties for the light
+light.shadow.mapSize.width = 512;  // default
+light.shadow.mapSize.height = 512; // default
+light.shadow.camera.near = 0.5;       // default
+light.shadow.camera.far = 500      // default
+
+//Create a sphere that cast shadows (but does not receive them)
+var sphereGeometry = new THREE.SphereBufferGeometry( 5, 32, 32 );
+var sphereMaterial = new THREE.MeshStandardMaterial( { color: 0xff0000 } );
+var sphere = new THREE.Mesh( sphereGeometry, sphereMaterial );
+sphere.castShadow = true; //default is false
+sphere.receiveShadow = false; //default
+scene.add( sphere );
+
+//Create a plane that receives shadows (but does not cast them)
+var planeGeometry = new THREE.PlaneBufferGeometry( 20, 20, 32, 32 );
+var planeMaterial = new THREE.MeshStandardMaterial( { color: 0x00ff00 } )
+var plane = new THREE.Mesh( planeGeometry, planeMaterial );
+plane.receiveShadow = true;
+scene.add( plane );
+
+//Create a helper for the shadow camera (optional)
+var helper = new THREE.CameraHelper( light.shadow.camera );
+scene.add( helper );
+			</code>
+		</p>
+
+		<h2>Constructor</h2>
+		<h3>[name]( )</h3>
+		<p>
+			Creates a new [name]. This is not intended to be called directly - it is called
+			internally by [page:PointLight].
+		</p>
+
+		<h2>Properties</h2>
+		<p>
+			See the base [page:LightShadow LightShadow] class for common properties.
+		</p>
+
+		<h2>Methods</h2>
+		<p>
+			See the base [page:LightShadow LightShadow] class for common methods.
+		</p>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/lights/[name].js src/lights/[name].js]
+	</body>
+</html>

+ 2 - 1
docs/list.js

@@ -217,8 +217,9 @@ var list = {
 			},
 
 			"Lights / Shadows": {
-				"DirectionalLightShadow": "api/en/lights/shadows/DirectionalLightShadow",
 				"LightShadow": "api/en/lights/shadows/LightShadow",
+				"PointLightShadow": "api/en/lights/shadows/PointLightShadow",
+				"DirectionalLightShadow": "api/en/lights/shadows/DirectionalLightShadow",
 				"SpotLightShadow": "api/en/lights/shadows/SpotLightShadow"
 			},
 

+ 3 - 3
examples/webgl_shadowmap.html

@@ -79,13 +79,13 @@
 				var ambient = new THREE.AmbientLight( 0x444444 );
 				scene.add( ambient );
 
-				light = new THREE.SpotLight( 0xffffff, 1, 0, Math.PI / 2 );
+				light = new THREE.SpotLight( 0xffffff, 1, 0, Math.PI / 5, 0.3 );
 				light.position.set( 0, 1500, 1000 );
 				light.target.position.set( 0, 0, 0 );
 
 				light.castShadow = true;
-
-				light.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 50, 1, 1200, 2500 ) );
+				light.shadow.camera.near = 1200;
+				light.shadow.camera.far = 2500;
 				light.shadow.bias = 0.0001;
 
 				light.shadow.mapSize.width = SHADOW_MAP_WIDTH;

+ 3 - 4
examples/webgl_shadowmap_performance.html

@@ -76,14 +76,13 @@
 				var ambient = new THREE.AmbientLight( 0x444444 );
 				scene.add( ambient );
 
-				light = new THREE.SpotLight( 0xffffff, 1, 0, Math.PI / 2 );
+				light = new THREE.SpotLight( 0xffffff, 1, 0, Math.PI / 5, 0.3 );
 				light.position.set( 0, 1500, 1000 );
 				light.target.position.set( 0, 0, 0 );
 
 				light.castShadow = true;
-
-				light.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 50, 1, 700, FAR ) );
-
+				light.shadow.camera.near = 1200;
+				light.shadow.camera.far = 2500;
 				light.shadow.bias = 0.0001;
 
 				light.shadow.mapSize.width = SHADOW_MAP_WIDTH;

+ 21 - 2
src/lights/DirectionalLightShadow.js

@@ -5,7 +5,7 @@ import { OrthographicCamera } from '../cameras/OrthographicCamera.js';
  * @author mrdoob / http://mrdoob.com/
  */
 
-function DirectionalLightShadow( ) {
+function DirectionalLightShadow() {
 
 	LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) );
 
@@ -13,7 +13,26 @@ function DirectionalLightShadow( ) {
 
 DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), {
 
-	constructor: DirectionalLightShadow
+	constructor: DirectionalLightShadow,
+
+	isDirectionalLightShadow: true,
+
+	updateMatrices: function ( light, viewCamera, viewportIndex ) {
+
+		var camera = this.camera,
+			lightPositionWorld = this._lightPositionWorld,
+			lookTarget = this._lookTarget;
+
+		lightPositionWorld.setFromMatrixPosition( light.matrixWorld );
+		camera.position.copy( lightPositionWorld );
+
+		lookTarget.setFromMatrixPosition( light.target.matrixWorld );
+		camera.lookAt( lookTarget );
+		camera.updateMatrixWorld();
+
+		LightShadow.prototype.updateMatrices.call( this, light, viewCamera, viewportIndex );
+
+	}
 
 } );
 

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

@@ -1,5 +1,7 @@
 import { Camera } from './../cameras/Camera';
+import { Light } from './../lights/Light';
 import { Vector2 } from './../math/Vector2';
+import { Vector4 } from './../math/Vector4';
 import { Matrix4 } from './../math/Matrix4';
 import { RenderTarget } from '../renderers/webgl/WebGLRenderLists';
 
@@ -17,5 +19,9 @@ export class LightShadow {
 	copy( source: LightShadow ): this;
 	clone( recursive?: boolean ): this;
 	toJSON(): any;
+	getFrustum(): number;
+	updateMatrices( light: Light, viewCamera: Camera, viewportIndex: number ): void;
+	getViewport( viewportIndex: number ): Vector4;
+	getFrameExtents(): Vector2;
 
 }

+ 65 - 0
src/lights/LightShadow.js

@@ -1,5 +1,8 @@
 import { Matrix4 } from '../math/Matrix4.js';
 import { Vector2 } from '../math/Vector2.js';
+import { Vector3 } from '../math/Vector3.js';
+import { Vector4 } from '../math/Vector4.js';
+import { Frustum } from '../math/Frustum.js';
 
 /**
  * @author mrdoob / http://mrdoob.com/
@@ -17,10 +20,72 @@ function LightShadow( camera ) {
 	this.map = null;
 	this.matrix = new Matrix4();
 
+	this._frustum = new Frustum();
+	this._frameExtents = new Vector2( 1, 1 );
+
+	this._viewportCount = 1;
+
+	this._viewports = [
+
+		new Vector4( 0, 0, 1, 1 )
+
+	];
+
 }
 
 Object.assign( LightShadow.prototype, {
 
+	_projScreenMatrix: new Matrix4(),
+
+	_lightPositionWorld: new Vector3(),
+
+	_lookTarget: new Vector3(),
+
+	getViewportCount: function () {
+
+		return this._viewportCount;
+
+	},
+
+	getFrustum: function () {
+
+		return this._frustum;
+
+	},
+
+	updateMatrices: function () {
+
+		var shadowCamera = this.camera,
+			shadowMatrix = this.matrix,
+			projScreenMatrix = this._projScreenMatrix;
+
+		projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
+		this._frustum.setFromMatrix( projScreenMatrix );
+
+		shadowMatrix.set(
+			0.5, 0.0, 0.0, 0.5,
+			0.0, 0.5, 0.0, 0.5,
+			0.0, 0.0, 0.5, 0.5,
+			0.0, 0.0, 0.0, 1.0
+		);
+
+		shadowMatrix.multiply( shadowCamera.projectionMatrix );
+		shadowMatrix.multiply( shadowCamera.matrixWorldInverse );
+
+	},
+
+	getViewport: function ( viewportIndex ) {
+
+		return this._viewports[ viewportIndex ];
+
+	},
+
+	getFrameExtents: function () {
+
+		return this._frameExtents;
+
+	},
+
 	copy: function ( source ) {
 
 		this.camera = source.camera.clone();

+ 2 - 3
src/lights/PointLight.js

@@ -1,6 +1,5 @@
 import { Light } from './Light.js';
-import { PerspectiveCamera } from '../cameras/PerspectiveCamera.js';
-import { LightShadow } from './LightShadow.js';
+import { PointLightShadow } from './PointLightShadow.js';
 
 /**
  * @author mrdoob / http://mrdoob.com/
@@ -33,7 +32,7 @@ function PointLight( color, intensity, distance, decay ) {
 	this.distance = ( distance !== undefined ) ? distance : 0;
 	this.decay = ( decay !== undefined ) ? decay : 1;	// for physically correct lights, should be 2.
 
-	this.shadow = new LightShadow( new PerspectiveCamera( 90, 1, 0.5, 500 ) );
+	this.shadow = new PointLightShadow();
 
 }
 

+ 8 - 0
src/lights/PointLightShadow.d.ts

@@ -0,0 +1,8 @@
+import { PerspectiveCamera } from './../cameras/PerspectiveCamera';
+import { LightShadow } from './LightShadow';
+
+export class PointLightShadow extends LightShadow {
+
+	camera: PerspectiveCamera;
+
+}

+ 89 - 0
src/lights/PointLightShadow.js

@@ -0,0 +1,89 @@
+import { LightShadow } from './LightShadow.js';
+import { PerspectiveCamera } from '../cameras/PerspectiveCamera.js';
+import { Vector2 } from '../math/Vector2.js';
+import { Vector3 } from '../math/Vector3.js';
+import { Vector4 } from '../math/Vector4.js';
+
+function PointLightShadow() {
+
+	LightShadow.call( this, new PerspectiveCamera( 90, 1, 0.5, 500 ) );
+
+	this._frameExtents = new Vector2( 4, 2 );
+
+	this._viewportCount = 6;
+
+	this._viewports = [
+		// These viewports map a cube-map onto a 2D texture with the
+		// following orientation:
+		//
+		//  xzXZ
+		//   y Y
+		//
+		// X - Positive x direction
+		// x - Negative x direction
+		// Y - Positive y direction
+		// y - Negative y direction
+		// Z - Positive z direction
+		// z - Negative z direction
+
+		// positive X
+		new Vector4( 2, 1, 1, 1 ),
+		// negative X
+		new Vector4( 0, 1, 1, 1 ),
+		// positive Z
+		new Vector4( 3, 1, 1, 1 ),
+		// negative Z
+		new Vector4( 1, 1, 1, 1 ),
+		// positive Y
+		new Vector4( 3, 0, 1, 1 ),
+		// negative Y
+		new Vector4( 1, 0, 1, 1 )
+	];
+
+	this._cubeDirections = [
+		new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ),
+		new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 )
+	];
+
+	this._cubeUps = [
+		new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ),
+		new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ),	new Vector3( 0, 0, - 1 )
+	];
+
+}
+
+PointLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), {
+
+	constructor: PointLightShadow,
+
+	isPointLightShadow: true,
+
+	updateMatrices: function ( light, viewCamera, viewportIndex ) {
+
+		var camera = this.camera,
+			shadowMatrix = this.matrix,
+			lightPositionWorld = this._lightPositionWorld,
+			lookTarget = this._lookTarget,
+			shadowMatrix = this.matrix,
+			projScreenMatrix = this._projScreenMatrix;
+
+		lightPositionWorld.setFromMatrixPosition( light.matrixWorld );
+		camera.position.copy( lightPositionWorld );
+
+		lookTarget.copy( camera.position );
+		lookTarget.add( this._cubeDirections[ viewportIndex ] );
+		camera.up.copy( this._cubeUps[ viewportIndex ] );
+		camera.lookAt( lookTarget );
+		camera.updateMatrixWorld();
+
+		shadowMatrix.makeTranslation( - lightPositionWorld.x, - lightPositionWorld.y, - lightPositionWorld.z );
+
+		projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
+		this._frustum.setFromMatrix( projScreenMatrix );
+
+	}
+
+} );
+
+
+export { PointLightShadow };

+ 0 - 2
src/lights/SpotLightShadow.d.ts

@@ -1,10 +1,8 @@
 import { PerspectiveCamera } from './../cameras/PerspectiveCamera';
-import { Light } from './Light';
 import { LightShadow } from './LightShadow';
 
 export class SpotLightShadow extends LightShadow {
 
 	camera: PerspectiveCamera;
-	update( light: Light ): void;
 
 }

+ 13 - 2
src/lights/SpotLightShadow.js

@@ -18,9 +18,11 @@ SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype
 
 	isSpotLightShadow: true,
 
-	update: function ( light ) {
+	updateMatrices: function ( light, viewCamera, viewportIndex ) {
 
-		var camera = this.camera;
+		var camera = this.camera,
+			lookTarget = this._lookTarget,
+			lightPositionWorld = this._lightPositionWorld;
 
 		var fov = _Math.RAD2DEG * 2 * light.angle;
 		var aspect = this.mapSize.width / this.mapSize.height;
@@ -35,6 +37,15 @@ SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype
 
 		}
 
+		lightPositionWorld.setFromMatrixPosition( light.matrixWorld );
+		camera.position.copy( lightPositionWorld );
+
+		lookTarget.setFromMatrixPosition( light.target.matrixWorld );
+		camera.lookAt( lookTarget );
+		camera.updateMatrixWorld();
+
+		LightShadow.prototype.updateMatrices.call( this, light, viewCamera, viewportIndex );
+
 	}
 
 } );

+ 44 - 132
src/renderers/webgl/WebGLShadowMap.js

@@ -8,21 +8,17 @@ import { WebGLRenderTarget } from '../WebGLRenderTarget.js';
 import { MeshDepthMaterial } from '../../materials/MeshDepthMaterial.js';
 import { MeshDistanceMaterial } from '../../materials/MeshDistanceMaterial.js';
 import { Vector4 } from '../../math/Vector4.js';
-import { Vector3 } from '../../math/Vector3.js';
 import { Vector2 } from '../../math/Vector2.js';
-import { Matrix4 } from '../../math/Matrix4.js';
 import { Frustum } from '../../math/Frustum.js';
 
 function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 
 	var _frustum = new Frustum(),
-		_projScreenMatrix = new Matrix4(),
 
 		_shadowMapSize = new Vector2(),
-		_maxShadowMapSize = new Vector2( maxTextureSize, maxTextureSize ),
+		_viewportSize = new Vector2(),
 
-		_lookTarget = new Vector3(),
-		_lightPositionWorld = new Vector3(),
+		_viewport = new Vector4(),
 
 		_MorphingFlag = 1,
 		_SkinningFlag = 2,
@@ -36,21 +32,6 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 
 	var shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide };
 
-	var cubeDirections = [
-		new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ),
-		new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 )
-	];
-
-	var cubeUps = [
-		new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ),
-		new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ),	new Vector3( 0, 0, - 1 )
-	];
-
-	var cube2DViewPorts = [
-		new Vector4(), new Vector4(), new Vector4(),
-		new Vector4(), new Vector4(), new Vector4()
-	];
-
 	// init
 
 	for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) {
@@ -69,8 +50,6 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 
 		_depthMaterials[ i ] = depthMaterial;
 
-		//
-
 		var distanceMaterial = new MeshDistanceMaterial( {
 
 			morphTargets: useMorphing,
@@ -82,8 +61,6 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 
 	}
 
-	//
-
 	var scope = this;
 
 	this.enabled = false;
@@ -114,13 +91,10 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 
 		// render depth map
 
-		var faceCount;
-
 		for ( var i = 0, il = lights.length; i < il; i ++ ) {
 
 			var light = lights[ i ];
 			var shadow = light.shadow;
-			var isPointLight = light && light.isPointLight;
 
 			if ( shadow === undefined ) {
 
@@ -129,130 +103,68 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 
 			}
 
-			var shadowCamera = shadow.camera;
-
 			_shadowMapSize.copy( shadow.mapSize );
-			_shadowMapSize.min( _maxShadowMapSize );
-
-			if ( isPointLight ) {
-
-				var vpWidth = _shadowMapSize.x;
-				var vpHeight = _shadowMapSize.y;
-
-				// These viewports map a cube-map onto a 2D texture with the
-				// following orientation:
-				//
-				//  xzXZ
-				//   y Y
-				//
-				// X - Positive x direction
-				// x - Negative x direction
-				// Y - Positive y direction
-				// y - Negative y direction
-				// Z - Positive z direction
-				// z - Negative z direction
-
-				// positive X
-				cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight );
-				// negative X
-				cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight );
-				// positive Z
-				cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight );
-				// negative Z
-				cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight );
-				// positive Y
-				cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight );
-				// negative Y
-				cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight );
-
-				_shadowMapSize.x *= 4.0;
-				_shadowMapSize.y *= 2.0;
-
-			}
-
-			if ( shadow.map === null ) {
-
-				var pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat };
-
-				shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );
-				shadow.map.texture.name = light.name + ".shadowMap";
 
-				shadowCamera.updateProjectionMatrix();
+			var shadowFrameExtents = shadow.getFrameExtents();
 
-			}
-
-			if ( shadow.isSpotLightShadow ) {
+			_shadowMapSize.multiply( shadowFrameExtents );
 
-				shadow.update( light );
-
-			}
+			_viewportSize.copy( shadow.mapSize );
 
-			var shadowMap = shadow.map;
-			var shadowMatrix = shadow.matrix;
+			if ( _shadowMapSize.x > maxTextureSize || _shadowMapSize.y > maxTextureSize ) {
 
-			_lightPositionWorld.setFromMatrixPosition( light.matrixWorld );
-			shadowCamera.position.copy( _lightPositionWorld );
+				console.warn( 'THREE.WebGLShadowMap:', light, 'has shadow exceeding max texture size, reducing' );
 
-			if ( isPointLight ) {
+				if ( _shadowMapSize.x > maxTextureSize ) {
 
-				faceCount = 6;
+					_viewportSize.x = Math.floor( maxTextureSize / shadowFrameExtents.x );
+					_shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x;
+					shadow.mapSize.x = _viewportSize.x;
 
-				// for point lights we set the shadow matrix to be a translation-only matrix
-				// equal to inverse of the light's position
+				}
 
-				shadowMatrix.makeTranslation( - _lightPositionWorld.x, - _lightPositionWorld.y, - _lightPositionWorld.z );
+				if ( _shadowMapSize.y > maxTextureSize ) {
 
-			} else {
+					_viewportSize.y = Math.floor( maxTextureSize / shadowFrameExtents.y );
+					_shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y;
+					shadow.mapSize.y = _viewportSize.y;
 
-				faceCount = 1;
+				}
 
-				_lookTarget.setFromMatrixPosition( light.target.matrixWorld );
-				shadowCamera.lookAt( _lookTarget );
-				shadowCamera.updateMatrixWorld();
+			}
 
-				// compute shadow matrix
+			if ( shadow.map === null ) {
 
-				shadowMatrix.set(
-					0.5, 0.0, 0.0, 0.5,
-					0.0, 0.5, 0.0, 0.5,
-					0.0, 0.0, 0.5, 0.5,
-					0.0, 0.0, 0.0, 1.0
-				);
+				var pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat };
 
-				shadowMatrix.multiply( shadowCamera.projectionMatrix );
-				shadowMatrix.multiply( shadowCamera.matrixWorldInverse );
+				shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );
+				shadow.map.texture.name = light.name + ".shadowMap";
 
 			}
 
-			_renderer.setRenderTarget( shadowMap );
+			_renderer.setRenderTarget( shadow.map );
 			_renderer.clear();
 
-			// render shadow map for each cube face (if omni-directional) or
-			// run a single pass if not
+			var viewportCount = shadow.getViewportCount();
 
-			for ( var face = 0; face < faceCount; face ++ ) {
+			for ( var vp = 0; vp < viewportCount; vp ++ ) {
 
-				if ( isPointLight ) {
+				var viewport = shadow.getViewport( vp );
 
-					_lookTarget.copy( shadowCamera.position );
-					_lookTarget.add( cubeDirections[ face ] );
-					shadowCamera.up.copy( cubeUps[ face ] );
-					shadowCamera.lookAt( _lookTarget );
-					shadowCamera.updateMatrixWorld();
-
-					var vpDimensions = cube2DViewPorts[ face ];
-					_state.viewport( vpDimensions );
-
-				}
+				_viewport.set(
+					_viewportSize.x * viewport.x,
+					_viewportSize.y * viewport.y,
+					_viewportSize.x * viewport.z,
+					_viewportSize.y * viewport.w
+				);
 
-				// update camera matrices and frustum
+				_state.viewport( _viewport );
 
-				_projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
-				_frustum.setFromMatrix( _projScreenMatrix );
+				shadow.updateMatrices( light, camera, vp );
 
-				// set object matrices & frustum culling
+				_frustum = shadow.getFrustum();
 
-				renderObject( scene, camera, shadowCamera, isPointLight );
+				renderObject( scene, camera, shadow.camera, light );
 
 			}
 
@@ -264,7 +176,7 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 
 	};
 
-	function getDepthMaterial( object, material, isPointLight, lightPositionWorld, shadowCameraNear, shadowCameraFar ) {
+	function getDepthMaterial( object, material, light, shadowCameraNear, shadowCameraFar ) {
 
 		var geometry = object.geometry;
 
@@ -273,7 +185,7 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 		var materialVariants = _depthMaterials;
 		var customMaterial = object.customDepthMaterial;
 
-		if ( isPointLight ) {
+		if ( light.isPointLight ) {
 
 			materialVariants = _distanceMaterials;
 			customMaterial = object.customDistanceMaterial;
@@ -362,9 +274,9 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 		result.wireframeLinewidth = material.wireframeLinewidth;
 		result.linewidth = material.linewidth;
 
-		if ( isPointLight && result.isMeshDistanceMaterial ) {
+		if ( light.isPointLight && result.isMeshDistanceMaterial ) {
 
-			result.referencePosition.copy( lightPositionWorld );
+			result.referencePosition.setFromMatrixPosition( light.matrixWorld );
 			result.nearDistance = shadowCameraNear;
 			result.farDistance = shadowCameraFar;
 
@@ -374,7 +286,7 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 
 	}
 
-	function renderObject( object, camera, shadowCamera, isPointLight ) {
+	function renderObject( object, camera, shadowCamera, light ) {
 
 		if ( object.visible === false ) return;
 
@@ -400,7 +312,7 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 
 						if ( groupMaterial && groupMaterial.visible ) {
 
-							var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld, shadowCamera.near, shadowCamera.far );
+							var depthMaterial = getDepthMaterial( object, groupMaterial, light, shadowCamera.near, shadowCamera.far );
 							_renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group );
 
 						}
@@ -409,7 +321,7 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 
 				} else if ( material.visible ) {
 
-					var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld, shadowCamera.near, shadowCamera.far );
+					var depthMaterial = getDepthMaterial( object, material, light, shadowCamera.near, shadowCamera.far );
 					_renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null );
 
 				}
@@ -422,7 +334,7 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 
 		for ( var i = 0, l = children.length; i < l; i ++ ) {
 
-			renderObject( children[ i ], camera, shadowCamera, isPointLight );
+			renderObject( children[ i ], camera, shadowCamera, light );
 
 		}