Explorar el Código

Clipping planes

* Implemented user-defined clipping planes.

* Renderer: Minor documentation update.

* Implements object-level clipping.

* Renderer: Minor cosmetic changes.

* Fixed lethal typo in normal shader.

* Let shadows respect .side of object's material.

* Fixed Plane.applyMatrix4.

- Scale denormalized but did not scale the position
- Projection is not supported (solved by documentation)
- Removed unnecessary copying to make up for the normalization
- Still arguably too heavy

* Added custom clipping example.

* Clipping: Fixed transition to disabled state.

* Improved custom clipping example.

- GUI dependency of volume visualization on Enabled state
- Larger radius for cylindrical global clipping area

* No .matrixAutoUpdate = false in clipping example.

* Let ShadowMap check renderer.localClippingEnabled.

* Added a simplified clipping example.
tschw hace 9 años
padre
commit
c46846127e
Se han modificado 43 ficheros con 1040 adiciones y 40 borrados
  1. 11 0
      docs/api/materials/Material.html
  2. 6 1
      docs/api/materials/ShaderMaterial.html
  3. 2 1
      docs/api/math/Plane.html
  4. 15 4
      docs/api/renderers/WebGLRenderer.html
  5. 2 0
      examples/files.js
  6. 192 0
      examples/webgl_clipping.html
  7. 401 0
      examples/webgl_clipping_advanced.html
  8. 19 0
      src/materials/Material.js
  9. 2 0
      src/materials/ShaderMaterial.js
  10. 6 7
      src/math/Plane.js
  11. 234 7
      src/renderers/WebGLRenderer.js
  12. 10 0
      src/renderers/shaders/ShaderChunk/clipping_planes_fragment.glsl
  13. 9 0
      src/renderers/shaders/ShaderChunk/clipping_planes_pars_fragment.glsl
  14. 3 0
      src/renderers/shaders/ShaderChunk/clipping_planes_pars_vertex.glsl
  15. 4 0
      src/renderers/shaders/ShaderChunk/clipping_planes_vertex.glsl
  16. 3 0
      src/renderers/shaders/ShaderLib/cube_frag.glsl
  17. 4 2
      src/renderers/shaders/ShaderLib/cube_vert.glsl
  18. 3 1
      src/renderers/shaders/ShaderLib/depthRGBA_frag.glsl
  19. 2 0
      src/renderers/shaders/ShaderLib/depthRGBA_vert.glsl
  20. 2 0
      src/renderers/shaders/ShaderLib/depth_frag.glsl
  21. 2 0
      src/renderers/shaders/ShaderLib/depth_vert.glsl
  22. 3 1
      src/renderers/shaders/ShaderLib/distanceRGBA_frag.glsl
  23. 2 0
      src/renderers/shaders/ShaderLib/distanceRGBA_vert.glsl
  24. 3 0
      src/renderers/shaders/ShaderLib/equirect_frag.glsl
  25. 4 2
      src/renderers/shaders/ShaderLib/equirect_vert.glsl
  26. 3 0
      src/renderers/shaders/ShaderLib/linedashed_frag.glsl
  27. 2 0
      src/renderers/shaders/ShaderLib/linedashed_vert.glsl
  28. 3 0
      src/renderers/shaders/ShaderLib/meshbasic_frag.glsl
  29. 2 0
      src/renderers/shaders/ShaderLib/meshbasic_vert.glsl
  30. 3 0
      src/renderers/shaders/ShaderLib/meshlambert_frag.glsl
  31. 2 0
      src/renderers/shaders/ShaderLib/meshlambert_vert.glsl
  32. 3 0
      src/renderers/shaders/ShaderLib/meshphong_frag.glsl
  33. 2 0
      src/renderers/shaders/ShaderLib/meshphong_vert.glsl
  34. 3 0
      src/renderers/shaders/ShaderLib/meshstandard_frag.glsl
  35. 2 0
      src/renderers/shaders/ShaderLib/meshstandard_vert.glsl
  36. 2 0
      src/renderers/shaders/ShaderLib/normal_frag.glsl
  37. 2 0
      src/renderers/shaders/ShaderLib/normal_vert.glsl
  38. 3 0
      src/renderers/shaders/ShaderLib/points_frag.glsl
  39. 2 0
      src/renderers/shaders/ShaderLib/points_vert.glsl
  40. 4 0
      src/renderers/webgl/WebGLProgram.js
  41. 4 2
      src/renderers/webgl/WebGLPrograms.js
  42. 50 12
      src/renderers/webgl/WebGLShadowMap.js
  43. 4 0
      utils/build/includes/common.json

+ 11 - 0
docs/api/materials/Material.html

@@ -108,6 +108,17 @@
 		Sets the alpha value to be used when running an alpha test. Default is *0*.
 		</div>
 
+		<h3>[property:Array clippingPlanes]</h3>
+
+		<div>
+		User-defined clipping planes specified as THREE.Plane objects in world space. These planes apply to the objects this material is attached to. Points in space whose dot product with the plane is negative are cut away. Default is [].
+		</div>
+
+		<h3>[property:Boolean clipShadows]</h3>
+		<div>
+		Defines whether to clip shadows according to the clipping planes specified on this material. Default is false.
+		</div>
+
 		<h3>[property:Float overdraw]</h3>
 		<div>
 		Amount of triangle expansion at draw time. This is a workaround for cases when gaps appear between triangles when using [page:CanvasRenderer]. *0.5* tends to give good results across browsers. Default is *0*.

+ 6 - 1
docs/api/materials/ShaderMaterial.html

@@ -272,7 +272,12 @@
 
 		<h3>[property:Boolean lights]</h3>
 		<div>
-		Defines whether this material uses lighting; true to pass uniform data related to lighting to this shader
+		Defines whether this material uses lighting; true to pass uniform data related to lighting to this shader. Default is false.
+		</div>
+
+		<h3>[property:Boolean clipping]</h3>
+		<div>
+		Defines whether this material supports clipping; true to let the renderer pass the clippingPlanes uniform. Default is false.
 		</div>
 
 		<h3>[property:Number vertexColors]</h3>

+ 2 - 1
docs/api/math/Plane.html

@@ -61,7 +61,8 @@
 		optionalNormalMatrix -- (optional) pre-computed normal [Page:Matrix3] of the Matrix4 to apply
 		</div>
 		<div>
-		Apply a Matrix4 to the plane. The second parameter is optional.
+		Apply a Matrix4 to the plane. The matrix must be an affine, homogeneous transform.
+		The second parameter is optional.
 
 		<code>
 		var optionalNormalMatrix = new THREE.Matrix3().getNormalMatrix( matrix )

+ 15 - 4
docs/api/renderers/WebGLRenderer.html

@@ -72,6 +72,16 @@
 		<div>Note: Sorting is used to attempt to properly render objects that have some degree of transparency.  By definition, sorting objects may not work in all cases.  Depending on the needs of application, it may be neccessary to turn off sorting and use other methods to deal with transparency rendering e.g. manually determining the object rendering order.</div>
 
 
+		<h3>[property:Array clippingPlanes]</h3>
+
+		<div>User-defined clipping planes specified as THREE.Plane objects in world space. These planes apply globally. Points in space whose dot product with the plane is negative are cut away. Default is [].</div>
+
+		<h3>[property:Boolean localClippingEnabled]</h3>
+
+		<div>Defines whether the renderer respects object-level clipping planes. Default is false.</div>
+
+
+
 		<h3>[property:Boolean gammaInput]</h3>
 
 		<div>Default is false. If set, then it expects that all textures and colors are premultiplied gamma.</div>
@@ -125,7 +135,6 @@
 		<ul>
 			<li>memory:
 				<ul>
-					<li>programs</li>
 					<li>geometries</li>
 					<li>textures</li>
 				</ul>
@@ -138,12 +147,14 @@
 					<li>points</li>
 				</ul>
 			</li>
+			<li>programs
+			</li>
 		</ul>
 		</div>
 
-		<h3>[property:ShadowMapPlugin shadowMapPlugin]</h3>
+		<h3>[property:WebGLShadowMap shadowMap]</h3>
 		<div>
-		This contains the reference to the shadowMapPlugin.
+		This contains the reference to the component implementing shadow mapping.
 		</div>
 
 
@@ -163,7 +174,7 @@
 		<div>
 		Return a [page:Boolean] true if the context supports vertex textures.
 		</div>
-		
+
 		<h3>[method:Object getSize]()</h3>
 		<div>Returns an object containing the width and height of the renderer's output canvas, in pixels.</div>
 

+ 2 - 0
examples/files.js

@@ -7,6 +7,8 @@ var files = {
 		"webgl_camera",
 		"webgl_camera_cinematic",
 		"webgl_camera_logarithmicdepthbuffer",
+		"webgl_clipping",
+		"webgl_clipping_advanced",
 		"webgl_decals",
 		"webgl_effects_anaglyph",
 		"webgl_effects_parallaxbarrier",

+ 192 - 0
examples/webgl_clipping.html

@@ -0,0 +1,192 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - clipping planes</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				margin: 0px;
+				background-color: #000000;
+				overflow: hidden;
+			}
+		</style>
+	</head>
+	<body>
+
+		<script src="../build/three.js"></script>
+		<script src="js/controls/OrbitControls.js"></script>
+
+		<script src="js/libs/dat.gui.min.js"></script>
+
+		<script>
+
+			var camera, scene, renderer, startTime, object;
+
+			function init() {
+
+				camera = new THREE.PerspectiveCamera(
+						36, window.innerWidth / window.innerHeight, 0.25, 16 );
+
+				camera.position.set( 0, 1.3, 3 );
+
+				scene = new THREE.Scene();
+
+				// Lights
+
+				scene.add( new THREE.AmbientLight( 0x505050 ) );
+
+				var spotLight = new THREE.SpotLight( 0xffffff );
+				spotLight.angle = Math.PI / 5;
+				spotLight.penumbra = 0.2;
+				spotLight.position.set( 2, 3, 3 );
+				spotLight.castShadow = true;
+				spotLight.shadow.camera.near = 3;
+				spotLight.shadow.camera.far = 10;
+				spotLight.shadow.mapSize.width = 1024;
+				spotLight.shadow.mapSize.height = 1024;
+				scene.add( spotLight );
+
+				var dirLight = new THREE.DirectionalLight( 0x55505a, 1 );
+				dirLight.position.set( 0, 3, 0 );
+				dirLight.castShadow = true;
+				dirLight.shadow.camera.near = 1;
+				dirLight.shadow.camera.far = 10;
+
+				dirLight.shadow.camera.right = 1;
+				dirLight.shadow.camera.left = - 1;
+				dirLight.shadow.camera.top	= 1;
+				dirLight.shadow.camera.bottom = - 1;
+
+				dirLight.shadow.mapSize.width = 1024;
+				dirLight.shadow.mapSize.height = 1024;
+				scene.add( dirLight );
+
+				// ***** Clipping planes: *****
+
+				var localPlane = new THREE.Plane( new THREE.Vector3( 0, - 1, 0 ), 0.8 ),
+					globalPlane = new THREE.Plane( new THREE.Vector3( - 1, 0, 0 ), 0.1 );
+
+				// Geometry
+
+				var material = new THREE.MeshPhongMaterial( {
+						color: 0x80ee10,
+						shininess: 100,
+						side: THREE.DoubleSide,
+
+						// ***** Clipping setup (material): *****
+						clippingPlanes: [ localPlane ],
+						clipShadows: true
+
+					} ),
+
+					geometry = new THREE.TorusKnotGeometry( 0.4, 0.08, 95, 20 );
+
+				object = new THREE.Mesh( geometry, material );
+				object.castShadow = true;
+				scene.add( object );
+
+				var ground = new THREE.Mesh(
+						new THREE.PlaneBufferGeometry( 9, 9, 1, 1 ),
+						new THREE.MeshPhongMaterial( {
+							color: 0xa0adaf, shininess: 150 } ) );
+
+				ground.rotation.x = - Math.PI / 2; // rotates X/Y to X/Z
+				ground.receiveShadow = true;
+				scene.add( ground );
+
+				// Renderer
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.shadowMap.enabled = true;
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				window.addEventListener( 'resize', onWindowResize, false );
+				document.body.appendChild( renderer.domElement );
+
+				// ***** Clipping setup (renderer): *****
+				var globalPlanes = [ globalPlane ],
+					Empty = Object.freeze( [] );
+				renderer.clippingPlanes = Empty; // GUI sets it to globalPlanes
+				renderer.localClippingEnabled = true;
+
+				// Controls
+
+				var controls = new THREE.OrbitControls( camera, renderer.domElement );
+				controls.target.set( 0, 1, 0 );
+				controls.update();
+
+				// GUI
+
+				var gui = new dat.GUI(),
+					folderLocal = gui.addFolder( "Local Clipping" ),
+					propsLocal = {
+
+						get 'Enabled'() { return renderer.localClippingEnabled; },
+						set 'Enabled'( v ) { renderer.localClippingEnabled = v; },
+
+						get 'Shadows'() { return material.clipShadows; },
+						set 'Shadows'( v ) { material.clipShadows = v; },
+
+						get 'Plane'() { return localPlane.constant; },
+						set 'Plane'( v ) { localPlane.constant = v }
+
+					},
+
+					folderGlobal = gui.addFolder( "Global Clipping" ),
+					propsGlobal = {
+
+						get 'Enabled'() { return renderer.clippingPlanes !== Empty; },
+						set 'Enabled'( v  ) {
+								renderer.clippingPlanes = v ? globalPlanes : Empty; },
+
+						get 'Plane'() { return globalPlane.constant; },
+						set 'Plane'( v ) { globalPlane.constant = v; }
+
+					};
+
+				folderLocal.add( propsLocal, 'Enabled' );
+				folderLocal.add( propsLocal, 'Shadows' );
+				folderLocal.add( propsLocal, 'Plane', 0.3, 1.25 );
+
+				folderGlobal.add( propsGlobal, 'Enabled' );
+				folderGlobal.add( propsGlobal, 'Plane', -0.4, 3 );
+
+				// Start
+
+				startTime = Date.now();
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+
+				var currentTime = Date.now(),
+					time = ( currentTime - startTime ) / 1000;
+
+				requestAnimationFrame( animate );
+
+				object.position.y = 0.8;
+				object.rotation.x = time * 0.5;
+				object.rotation.y = time * 0.2;
+				object.scale.setScalar( Math.cos( time ) * 0.125 + 0.875 );
+
+				renderer.render( scene, camera );
+
+			}
+
+			init();
+			animate();
+
+		</script>
+
+	</body>
+</html>

+ 401 - 0
examples/webgl_clipping_advanced.html

@@ -0,0 +1,401 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - clipping planes</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				margin: 0px;
+				background-color: #000000;
+				overflow: hidden;
+			}
+
+			#stats { position: absolute; top:0; left: 0 }
+			#stats #fps { background: transparent !important }
+			#stats #fps #fpsText { color: #777 !important }
+			#stats #fps #fpsGraph { display: none }
+		</style>
+	</head>
+	<body>
+
+		<script src="../build/three.js"></script>
+		<script src="js/controls/OrbitControls.js"></script>
+
+		<script src="js/libs/stats.min.js"></script>
+		<script src="js/libs/dat.gui.min.js"></script>
+
+		<script>
+
+			function planesFromMesh( vertices, indices ) {
+				// creates a clipping volume from a convex triangular mesh
+				// specified by the arrays 'vertices' and 'indices'
+
+				var n = indices.length / 3,
+					result = new Array( n );
+
+				for ( var i = 0, j = 0; i < n; ++ i, j += 3 ) {
+
+					var a = vertices[ indices[   j   ] ],
+						b = vertices[ indices[ j + 1 ] ],
+						c = vertices[ indices[ j + 2 ] ];
+
+					result[ i ] = new THREE.Plane().
+							setFromCoplanarPoints( a, b, c );
+
+				}
+
+				return result;
+
+			}
+
+			function createPlanes( n ) {
+				// creates an array of n uninitialized plane objects
+
+				var result = new Array( n );
+
+				for ( var i = 0; i !== n; ++ i )
+					result[ i ] = new THREE.Plane();
+
+				return result;
+
+			}
+
+			function assignTransformedPlanes( planesOut, planesIn, matrix ) {
+				// sets an array of existing planes to transformed 'planesIn'
+
+				for ( var i = 0, n = planesIn.length; i !== n; ++ i )
+					planesOut[ i ].copy( planesIn[ i ] ).applyMatrix4( matrix );
+
+			}
+
+			function cylindricalPlanes( n, innerRadius ) {
+
+				var result = createPlanes( n );
+
+				for ( var i = 0; i !== n; ++ i ) {
+
+					var plane = result[ i ],
+						angle = i * Math.PI * 2 / n;
+
+					plane.normal.set(
+							Math.cos( angle ), 0, Math.sin( angle ) );
+
+					plane.constant = innerRadius;
+
+				}
+
+				return result;
+
+			}
+
+			var planeToMatrix = ( function() {
+				// creates a matrix that aligns X/Y to a given plane
+
+				// temporaries:
+				var xAxis = new THREE.Vector3(),
+					yAxis = new THREE.Vector3(),
+					trans = new THREE.Vector3();
+
+				return function planeToMatrix( plane ) {
+
+					var zAxis = plane.normal,
+						matrix = new THREE.Matrix4();
+
+					// Hughes & Moeller '99
+					// "Building an Orthonormal Basis from a Unit Vector."
+
+					if ( Math.abs( zAxis.x ) > Math.abs( zAxis.z ) ) {
+
+						yAxis.set( -zAxis.y, zAxis.x, 0 );
+
+					} else {
+
+						yAxis.set( 0, -zAxis.z, zAxis.y );
+
+					}
+
+					xAxis.crossVectors( yAxis.normalize(), zAxis );
+
+					plane.coplanarPoint( trans );
+					return matrix.set(
+						xAxis.x, yAxis.x, zAxis.x, trans.x,
+						xAxis.y, yAxis.y, zAxis.y, trans.y,
+						xAxis.z, yAxis.z, zAxis.z, trans.z,
+							0,		0,		0,			1 );
+
+				};
+
+			} )();
+
+
+			// A regular tetrahedron for the clipping volume:
+
+			var Vertices = [
+					new THREE.Vector3( + 1,   0, + Math.SQRT1_2 ),
+					new THREE.Vector3( - 1,   0, + Math.SQRT1_2 ),
+					new THREE.Vector3(   0, + 1, - Math.SQRT1_2 ),
+					new THREE.Vector3(   0, - 1, - Math.SQRT1_2 )
+				],
+
+				Indices = [
+					0, 1, 2,	0, 2, 3,	0, 3, 1,	1, 3, 2 ],
+
+				Planes = planesFromMesh( Vertices, Indices ),
+				PlaneMatrices = Planes.map( planeToMatrix );
+
+				GlobalClippingPlanes = cylindricalPlanes( 5, 3.5 ),
+
+				Empty = Object.freeze( [] );
+
+
+			var camera, scene, renderer, startTime, stats,
+
+				object, clipMaterial,
+				volumeVisualization,
+				globalClippingPlanes;
+
+			function init() {
+
+				camera = new THREE.PerspectiveCamera(
+						36, window.innerWidth / window.innerHeight, 0.25, 16 );
+
+				camera.position.set( 0, 1.5, 3 );
+
+				scene = new THREE.Scene();
+
+				// Lights
+
+				scene.add( new THREE.AmbientLight( 0x505050 ) );
+
+				var spotLight = new THREE.SpotLight( 0xffffff );
+				spotLight.angle = Math.PI / 5;
+				spotLight.penumbra = 0.2;
+				spotLight.position.set( 2, 3, 3 );
+				spotLight.castShadow = true;
+				spotLight.shadow.camera.near = 3;
+				spotLight.shadow.camera.far = 10;
+				spotLight.shadow.mapSize.width = 1024;
+				spotLight.shadow.mapSize.height = 1024;
+				scene.add( spotLight );
+
+				var dirLight = new THREE.DirectionalLight( 0x55505a, 1 );
+				dirLight.position.set( 0, 2, 0 );
+				dirLight.castShadow = true;
+				dirLight.shadow.camera.near = 1;
+				dirLight.shadow.camera.far = 10;
+
+				dirLight.shadow.camera.right = 1;
+				dirLight.shadow.camera.left = - 1;
+				dirLight.shadow.camera.top	= 1;
+				dirLight.shadow.camera.bottom = - 1;
+
+				dirLight.shadow.mapSize.width = 1024;
+				dirLight.shadow.mapSize.height = 1024;
+				scene.add( dirLight );
+
+				// Geometry
+
+				clipMaterial = new THREE.MeshPhongMaterial( {
+					color: 0xee0a10,
+					shininess: 100,
+					side: THREE.DoubleSide,
+					// Clipping setup:
+					clippingPlanes: createPlanes( Planes.length ),
+					clipShadows: true
+				} );
+
+				object = new THREE.Group();
+
+				var geometry = new THREE.BoxBufferGeometry( 0.18, 0.18, 0.18 );
+
+				for ( var z = -2; z <= 2; ++ z )
+				for ( var y = -2; y <= 2; ++ y )
+				for ( var x = -2; x <= 2; ++ x ) {
+
+					var mesh = new THREE.Mesh( geometry, clipMaterial );
+					mesh.position.set( x / 5, y / 5, z / 5 );
+					mesh.castShadow = true;
+					object.add( mesh );
+
+				}
+
+				scene.add( object );
+
+
+				var planeGeometry =
+						new THREE.PlaneBufferGeometry( 3, 3, 1, 1 ),
+
+					color = new THREE.Color();
+
+				volumeVisualization = new THREE.Group();
+				volumeVisualization.visible = false;
+
+				for ( var i = 0, n = Planes.length; i !== n; ++ i ) {
+
+					var material = new THREE.MeshBasicMaterial( {
+						color:  color.setHSL( i / n, 0.5, 0.5 ).getHex(),
+						side: THREE.DoubleSide,
+
+						opacity: 0.2,
+						transparent: true,
+
+						// clip to the others to show the volume (wildly
+						// intersecting transparent planes look bad)
+						clippingPlanes: clipMaterial.clippingPlanes.
+								filter( function( _, j ) { return j !== i; } )
+
+						// no need to enable shadow clipping - the plane
+						// visualization does not cast shadows
+
+					} );
+
+					volumeVisualization.add(
+							new THREE.Mesh( planeGeometry, material ) );
+
+				}
+
+				scene.add( volumeVisualization );
+
+
+				var ground = new THREE.Mesh( planeGeometry,
+						new THREE.MeshPhongMaterial( {
+							color: 0xa0adaf, shininess: 150 } ) );
+				ground.rotation.x = - Math.PI / 2;
+				ground.scale.multiplyScalar( 3 );
+				ground.receiveShadow = true;
+				scene.add( ground );
+
+				// Renderer
+
+				var container = document.body;
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.shadowMap.enabled = true;
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				window.addEventListener( 'resize', onWindowResize, false );
+				container.appendChild( renderer.domElement );
+				// Clipping setup:
+				globalClippingPlanes = createPlanes( GlobalClippingPlanes.length );
+				renderer.clippingPlanes = Empty;
+				renderer.localClippingEnabled = true;
+
+				// Stats
+
+				stats = new Stats();
+				container.appendChild( stats.domElement );
+
+				// Controls
+
+				var controls = new THREE.OrbitControls( camera, renderer.domElement );
+				controls.target.set( 0, 1, 0 );
+				controls.update();
+
+				// GUI
+
+				var gui = new dat.GUI(),
+					folder = gui.addFolder( "Local Clipping" ),
+					props = {
+						get 'Enabled'() { return renderer.localClippingEnabled; },
+						set 'Enabled'( v ) {
+								renderer.localClippingEnabled = v;
+								if ( ! v ) volumeVisualization.visible = false; },
+
+						get 'Shadows'() { return clipMaterial.clipShadows; },
+						set 'Shadows'( v ) { clipMaterial.clipShadows = v; },
+
+						get 'Visualize'() { return volumeVisualization.visible; },
+						set 'Visualize'( v ) {
+								if ( renderer.localClippingEnabled )
+									volumeVisualization.visible = v; }
+					};
+
+				folder.add( props, 'Enabled' );
+				folder.add( props, 'Shadows' );
+				folder.add( props, 'Visualize' ).listen();
+
+				gui.addFolder( "Global Clipping" ).
+						add( {
+							get 'Enabled'() { return renderer.clippingPlanes !== Empty; },
+							set 'Enabled'( v  ) { renderer.clippingPlanes = v ?
+									globalClippingPlanes : Empty; }
+						}, "Enabled" );
+
+
+				// Start
+
+				startTime = Date.now();
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function setObjectWorldMatrix( object, matrix ) {
+				// set the orientation of an object based on a world matrix
+
+				var parent = object.parent;
+				scene.updateMatrixWorld();
+				object.matrix.getInverse( parent.matrixWorld );
+				object.applyMatrix( matrix );
+
+			}
+
+			var transform = new THREE.Matrix4(),
+				tmpMatrix = new THREE.Matrix4();
+
+			function animate() {
+
+				var currentTime = Date.now(),
+					time = ( currentTime - startTime ) / 1000;
+
+				requestAnimationFrame( animate );
+
+				object.position.y = 1;
+				object.rotation.x = time * 0.5;
+				object.rotation.y = time * 0.2;
+
+				object.updateMatrix();
+				transform.copy( object.matrix );
+
+				var bouncy = Math.cos( time * .5 ) * 0.5 + 0.7;
+				transform.multiply(
+						tmpMatrix.makeScale( bouncy, bouncy, bouncy ) );
+
+				assignTransformedPlanes(
+						clipMaterial.clippingPlanes, Planes, transform );
+
+				var planeMeshes = volumeVisualization.children;
+
+				for ( var i = 0, n = planeMeshes.length; i !== n; ++ i ) {
+
+					tmpMatrix.multiplyMatrices( transform, PlaneMatrices[ i ] );
+					setObjectWorldMatrix( planeMeshes[ i ], tmpMatrix );
+
+				}
+
+				transform.makeRotationY( time * 0.1 );
+
+				assignTransformedPlanes(
+						globalClippingPlanes, GlobalClippingPlanes, transform );
+
+				stats.begin();
+				renderer.render( scene, camera );
+				stats.end();
+
+			}
+
+			init();
+			animate();
+
+		</script>
+
+	</body>
+</html>

+ 19 - 0
src/materials/Material.js

@@ -30,6 +30,9 @@ THREE.Material = function () {
 	this.depthTest = true;
 	this.depthWrite = true;
 
+	this.clippingPlanes = null;
+	this.clipShadows = false;
+
 	this.colorWrite = true;
 
 	this.precision = null; // override the renderer's default precision for this material
@@ -274,6 +277,22 @@ THREE.Material.prototype = {
 		this.overdraw = source.overdraw;
 
 		this.visible = source.visible;
+		this.clipShadows = source.clipShadows;
+
+		var srcPlanes = source.clippingPlanes,
+			dstPlanes = null;
+
+		if ( srcPlanes !== null ) {
+
+			var n = srcPlanes.length;
+			dstPlanes = new Array( n );
+
+			for ( var i = 0; i !== n; ++ i )
+				dstPlanes[ i ] = srcPlanes[ i ].clone();
+
+		}
+
+		this.clippingPlanes = dstPlanes;
 
 		return this;
 

+ 2 - 0
src/materials/ShaderMaterial.js

@@ -47,6 +47,7 @@ THREE.ShaderMaterial = function ( parameters ) {
 	this.fog = false; // set to use scene fog
 
 	this.lights = false; // set to use scene lights
+	this.clipping = false; // set to use user-defined clipping planes
 
 	this.vertexColors = THREE.NoColors; // set to use "color" attribute stream
 
@@ -108,6 +109,7 @@ THREE.ShaderMaterial.prototype.copy = function ( source ) {
 	this.fog = source.fog;
 
 	this.lights = source.lights;
+	this.clipping = source.clipping;
 
 	this.vertexColors = source.vertexColors;
 

+ 6 - 7
src/math/Plane.js

@@ -195,20 +195,19 @@ THREE.Plane.prototype = {
 	applyMatrix4: function () {
 
 		var v1 = new THREE.Vector3();
-		var v2 = new THREE.Vector3();
 		var m1 = new THREE.Matrix3();
 
 		return function ( matrix, optionalNormalMatrix ) {
 
-			// compute new normal based on theory here:
+			var referencePoint = this.coplanarPoint( v1 ).applyMatrix4( matrix );
+
+			// transform normal based on theory here:
 			// http://www.songho.ca/opengl/gl_normaltransform.html
 			var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix );
-			var newNormal = v1.copy( this.normal ).applyMatrix3( normalMatrix );
-
-			var newCoplanarPoint = this.coplanarPoint( v2 );
-			newCoplanarPoint.applyMatrix4( matrix );
+			var normal = this.normal.applyMatrix3( normalMatrix ).normalize();
 
-			this.setFromNormalAndCoplanarPoint( newNormal, newCoplanarPoint );
+			// recalculate constant (like in setFromNormalAndCoplanarPoint)
+			this.constant = - referencePoint.dot( normal );
 
 			return this;
 

+ 234 - 7
src/renderers/WebGLRenderer.js

@@ -3,6 +3,7 @@
  * @author mrdoob / http://mrdoob.com/
  * @author alteredq / http://alteredqualia.com/
  * @author szimek / https://github.com/szimek/
+ * @author tschw
  */
 
 THREE.WebGLRenderer = function ( parameters ) {
@@ -49,6 +50,11 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	this.sortObjects = true;
 
+	// user-defined clipping
+
+	this.clippingPlanes = [];
+	this.localClippingEnabled = false;
+
 	// physically based shading
 
 	this.gammaFactor = 2.0;	// for backwards compatibility
@@ -115,6 +121,24 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	_frustum = new THREE.Frustum(),
 
+	// clipping
+
+	_clippingEnabled = false,
+	_localClippingEnabled = false,
+	_clipRenderingShadows = false,
+
+	_numClippingPlanes = 0,
+	_clippingPlanesUniform = {
+			type: '4fv', value: null, needsUpdate: false },
+
+	_globalClippingState = null,
+	_numGlobalClippingPlanes = 0,
+
+	_matrix3 = new THREE.Matrix3(),
+	_sphere = new THREE.Sphere(),
+	_plane = new THREE.Plane(),
+
+
 	// camera matrices cache
 
 	_projScreenMatrix = new THREE.Matrix4(),
@@ -1156,8 +1180,11 @@ THREE.WebGLRenderer = function ( parameters ) {
 		sprites.length = 0;
 		lensFlares.length = 0;
 
+		setupGlobalClippingPlanes( this.clippingPlanes, camera );
+
 		projectObject( scene, camera );
 
+
 		opaqueObjects.length = opaqueObjectsLastIndex + 1;
 		transparentObjects.length = transparentObjectsLastIndex + 1;
 
@@ -1170,12 +1197,26 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		//
 
+		if ( _clippingEnabled ) {
+
+			_clipRenderingShadows = true;
+			setupClippingPlanes( null );
+
+		}
+
 		setupShadows( lights );
 
 		shadowMap.render( scene, camera );
 
 		setupLights( lights, camera );
 
+		if ( _clippingEnabled ) {
+
+			_clipRenderingShadows = false;
+			resetGlobalClippingState();
+
+		}
+
 		//
 
 		_infoRender.calls = 0;
@@ -1299,6 +1340,37 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	}
 
+	function isObjectViewable( object ) {
+
+		var geometry = object.geometry;
+
+		if ( geometry.boundingSphere === null )
+			geometry.computeBoundingSphere();
+
+		var sphere = _sphere.
+				copy( geometry.boundingSphere ).
+				applyMatrix4( object.matrixWorld );
+
+		if ( ! _frustum.intersectsSphere( sphere ) ) return false;
+		if ( _numClippingPlanes === 0 ) return true;
+
+		var planes = _this.clippingPlanes,
+
+			center = sphere.center,
+			negRad = - sphere.radius,
+			i = 0;
+
+		do {
+
+			// out when deeper than radius in the negative halfspace
+			if ( planes[ i ].distanceToPoint( center ) < negRad ) return false;
+
+		} while ( ++ i !== _numClippingPlanes );
+
+		return true;
+
+	}
+
 	function projectObject( object, camera ) {
 
 		if ( object.visible === false ) return;
@@ -1311,7 +1383,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			} else if ( object instanceof THREE.Sprite ) {
 
-				if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) {
+				if ( object.frustumCulled === false || isObjectViewable( object ) === true ) {
 
 					sprites.push( object );
 
@@ -1340,7 +1412,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				}
 
-				if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) {
+				if ( object.frustumCulled === false || isObjectViewable( object ) === true ) {
 
 					var material = object.material;
 
@@ -1439,7 +1511,9 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		var materialProperties = properties.get( material );
 
-		var parameters = programCache.getParameters( material, _lights, fog, object );
+		var parameters = programCache.getParameters(
+				material, _lights, fog, _numClippingPlanes, object );
+
 		var code = programCache.getProgramCode( material, parameters );
 
 		var program = materialProperties.program;
@@ -1534,10 +1608,20 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-		materialProperties.uniformsList = [];
+		var uniforms = materialProperties.__webglShader.uniforms;
+
+		if ( ! ( material instanceof THREE.ShaderMaterial ) &&
+				! ( material instanceof THREE.RawShaderMaterial ) ||
+				material.clipping === true ) {
+
+			materialProperties.numClippingPlanes = _numClippingPlanes;
+			uniforms.clippingPlanes = _clippingPlanesUniform;
+
+		}
+
+		var uniformLocations = materialProperties.program.getUniforms();
 
-		var uniforms = materialProperties.__webglShader.uniforms,
-			uniformLocations = materialProperties.program.getUniforms();
+		materialProperties.uniformsList = [];
 
 		for ( var u in uniforms ) {
 
@@ -1631,6 +1715,32 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		var materialProperties = properties.get( material );
 
+		if ( _clippingEnabled ) {
+
+			if ( _localClippingEnabled || camera !== _currentCamera ) {
+
+				var useCache =
+						camera === _currentCamera &&
+						material.id === _currentMaterialId;
+
+				// we might want to call this function with some ClippingGroup
+				// object instead of the material, once it becomes feasible
+				// (#8465, #8379)
+				setClippingState(
+						material.clippingPlanes, material.clipShadows,
+						camera, materialProperties, useCache );
+
+			}
+
+			if ( materialProperties.numClippingPlanes !== undefined &&
+				materialProperties.numClippingPlanes !== _numClippingPlanes ) {
+
+				material.needsUpdate = true;
+
+			}
+
+		}
+
 		if ( materialProperties.program === undefined ) {
 
 			material.needsUpdate = true;
@@ -2044,7 +2154,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 		uniforms.diffuse.value = material.color;
 		uniforms.opacity.value = material.opacity;
 		uniforms.size.value = material.size * _pixelRatio;
-		uniforms.scale.value = _canvas.clientHeight / 2.0; // TODO: Cache this.
+		uniforms.scale.value = _canvas.clientHeight * 0.5;
 
 		uniforms.map.value = material.map;
 
@@ -2783,6 +2893,123 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	}
 
+
+	function setupGlobalClippingPlanes( planes, camera ) {
+
+		_clippingEnabled =
+				_this.clippingPlanes.length !== 0 ||
+				_this.localClippingEnabled ||
+				// enable state of previous frame - the clipping code has to
+				// run another frame in order to reset the state:
+				_numGlobalClippingPlanes !== 0 ||
+				_localClippingEnabled;
+
+		_localClippingEnabled = _this.localClippingEnabled;
+
+		_globalClippingState = setupClippingPlanes( planes, camera, 0 );
+		_numGlobalClippingPlanes = planes !== null ? planes.length : 0;
+
+	}
+
+	function setupClippingPlanes( planes, camera, dstOffset, skipTransform ) {
+
+		var nPlanes = planes !== null ? planes.length : 0,
+			dstArray = null;
+
+		if ( nPlanes !== 0 ) {
+
+			dstArray = _clippingPlanesUniform.value;
+
+			if ( skipTransform !== true || dstArray === null ) {
+
+				var flatSize = dstOffset + nPlanes * 4,
+					viewMatrix = camera.matrixWorldInverse,
+					viewNormalMatrix = _matrix3.getNormalMatrix( viewMatrix );
+
+				if ( dstArray === null || dstArray.length < flatSize ) {
+
+					dstArray = new Float32Array( flatSize );
+
+				}
+
+				for ( var i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) {
+
+					var plane = _plane.copy( planes[ i ] ).
+							applyMatrix4( viewMatrix, viewNormalMatrix );
+
+					plane.normal.toArray( dstArray, i4 );
+					dstArray[ i4 + 3 ] = plane.constant;
+
+				}
+
+			}
+
+			_clippingPlanesUniform.value = dstArray;
+			_clippingPlanesUniform.needsUpdate = true;
+
+		}
+
+		_numClippingPlanes = nPlanes;
+		return dstArray;
+
+	}
+
+	function resetGlobalClippingState() {
+
+		if ( _clippingPlanesUniform.value !== _globalClippingState ) {
+
+			_clippingPlanesUniform.value = _globalClippingState;
+			_clippingPlanesUniform.needsUpdate = _numGlobalClippingPlanes > 0;
+
+		}
+
+		_numClippingPlanes = _numGlobalClippingPlanes;
+
+	}
+
+	function setClippingState( planes, clipShadows, camera, cache, fromCache ) {
+
+		if ( ! _localClippingEnabled ||
+				planes === null || planes.length === 0 ||
+				_clipRenderingShadows && ! clipShadows ) {
+			// there's no local clipping
+
+			if ( _clipRenderingShadows ) {
+				// there's no global clipping
+
+				setupClippingPlanes( null );
+
+			} else {
+
+				resetGlobalClippingState();
+			}
+
+		} else {
+
+			var nGlobal = _clipRenderingShadows ? 0 : _numGlobalClippingPlanes,
+				lGlobal = nGlobal * 4,
+
+				dstArray = cache.clippingState || null;
+
+			_clippingPlanesUniform.value = dstArray; // ensure unique state
+
+			dstArray = setupClippingPlanes(
+					planes, camera, lGlobal, fromCache );
+
+			for ( var i = 0; i !== lGlobal; ++ i ) {
+
+				dstArray[ i ] = _globalClippingState[ i ];
+
+			}
+
+			cache.clippingState = dstArray;
+			_numClippingPlanes += nGlobal;
+
+		}
+
+	}
+
+
 	// GL state setting
 
 	this.setFaceCulling = function ( cullFace, frontFaceDirection ) {

+ 10 - 0
src/renderers/shaders/ShaderChunk/clipping_planes_fragment.glsl

@@ -0,0 +1,10 @@
+#if NUM_CLIPPING_PLANES > 0
+
+	for ( int i = 0; i < NUM_CLIPPING_PLANES; ++ i ) {
+
+		vec4 plane = clippingPlanes[ i ];
+		if ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;
+
+	}
+
+#endif

+ 9 - 0
src/renderers/shaders/ShaderChunk/clipping_planes_pars_fragment.glsl

@@ -0,0 +1,9 @@
+#if NUM_CLIPPING_PLANES > 0
+
+	#if ! defined( STANDARD ) && ! defined( PHONG )
+		varying vec3 vViewPosition;
+	#endif
+
+	uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];
+
+#endif

+ 3 - 0
src/renderers/shaders/ShaderChunk/clipping_planes_pars_vertex.glsl

@@ -0,0 +1,3 @@
+#if NUM_CLIPPING_PLANES > 0 && ! defined( STANDARD ) && ! defined( PHONG )
+	varying vec3 vViewPosition;
+#endif

+ 4 - 0
src/renderers/shaders/ShaderChunk/clipping_planes_vertex.glsl

@@ -0,0 +1,4 @@
+#if NUM_CLIPPING_PLANES > 0 && ! defined( STANDARD ) && ! defined( PHONG )
+	vViewPosition = - mvPosition.xyz;
+#endif
+

+ 3 - 0
src/renderers/shaders/ShaderLib/cube_frag.glsl

@@ -5,9 +5,12 @@ varying vec3 vWorldPosition;
 
 #include <common>
 #include <logdepthbuf_pars_fragment>
+#include <clipping_planes_pars_fragment>
 
 void main() {
 
+	#include <clipping_planes_fragment>
+
 	gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );
 
 	#include <logdepthbuf_fragment>

+ 4 - 2
src/renderers/shaders/ShaderLib/cube_vert.glsl

@@ -2,13 +2,15 @@ varying vec3 vWorldPosition;
 
 #include <common>
 #include <logdepthbuf_pars_vertex>
+#include <clipping_planes_pars_vertex>
 
 void main() {
 
 	vWorldPosition = transformDirection( position, modelMatrix );
 
-	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
-
+	#include <begin_vertex>
+	#include <project_vertex>
 	#include <logdepthbuf_vertex>
+	#include <clipping_planes_vertex>
 
 }

+ 3 - 1
src/renderers/shaders/ShaderLib/depthRGBA_frag.glsl

@@ -1,9 +1,11 @@
 #include <common>
-#include <logdepthbuf_pars_fragment>
 #include <packing>
+#include <logdepthbuf_pars_fragment>
+#include <clipping_planes_pars_fragment>
 
 void main() {
 
+	#include <clipping_planes_fragment>
 	#include <logdepthbuf_fragment>
 
 	#ifdef USE_LOGDEPTHBUF_EXT

+ 2 - 0
src/renderers/shaders/ShaderLib/depthRGBA_vert.glsl

@@ -2,6 +2,7 @@
 #include <morphtarget_pars_vertex>
 #include <skinning_pars_vertex>
 #include <logdepthbuf_pars_vertex>
+#include <clipping_planes_pars_vertex>
 
 void main() {
 
@@ -12,5 +13,6 @@ void main() {
 	#include <skinning_vertex>
 	#include <project_vertex>
 	#include <logdepthbuf_vertex>
+	#include <clipping_planes_vertex>
 
 }

+ 2 - 0
src/renderers/shaders/ShaderLib/depth_frag.glsl

@@ -5,9 +5,11 @@ uniform float opacity;
 #include <common>
 #include <packing>
 #include <logdepthbuf_pars_fragment>
+#include <clipping_planes_pars_fragment>
 
 void main() {
 
+	#include <clipping_planes_fragment>
 	#include <logdepthbuf_fragment>
 
 	#ifdef USE_LOGDEPTHBUF_EXT

+ 2 - 0
src/renderers/shaders/ShaderLib/depth_vert.glsl

@@ -1,6 +1,7 @@
 #include <common>
 #include <morphtarget_pars_vertex>
 #include <logdepthbuf_pars_vertex>
+#include <clipping_planes_pars_vertex>
 
 void main() {
 
@@ -8,5 +9,6 @@ void main() {
 	#include <morphtarget_vertex>
 	#include <project_vertex>
 	#include <logdepthbuf_vertex>
+	#include <clipping_planes_vertex>
 
 }

+ 3 - 1
src/renderers/shaders/ShaderLib/distanceRGBA_frag.glsl

@@ -3,10 +3,12 @@ varying vec4 vWorldPosition;
 
 #include <common>
 #include <packing>
-
+#include <clipping_planes_pars_fragment>
 
 void main () {
 
+	#include <clipping_planes_fragment>
+
 	gl_FragColor = packLinearUnitToRGBA( length( vWorldPosition.xyz - lightPos.xyz ) / 1000.0 );
 
 }

+ 2 - 0
src/renderers/shaders/ShaderLib/distanceRGBA_vert.glsl

@@ -3,6 +3,7 @@ varying vec4 vWorldPosition;
 #include <common>
 #include <morphtarget_pars_vertex>
 #include <skinning_pars_vertex>
+#include <clipping_planes_pars_vertex>
 
 void main() {
 
@@ -12,6 +13,7 @@ void main() {
 	#include <skinning_vertex>
 	#include <project_vertex>
 	#include <worldpos_vertex>
+	#include <clipping_planes_vertex>
 
 	vWorldPosition = worldPosition;
 

+ 3 - 0
src/renderers/shaders/ShaderLib/equirect_frag.glsl

@@ -5,9 +5,12 @@ varying vec3 vWorldPosition;
 
 #include <common>
 #include <logdepthbuf_pars_fragment>
+#include <clipping_planes_pars_fragment>
 
 void main() {
 
+	#include <clipping_planes_fragment>
+
 	// 	gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );
 	vec3 direction = normalize( vWorldPosition );
 	vec2 sampleUV;

+ 4 - 2
src/renderers/shaders/ShaderLib/equirect_vert.glsl

@@ -2,13 +2,15 @@ varying vec3 vWorldPosition;
 
 #include <common>
 #include <logdepthbuf_pars_vertex>
+#include <clipping_planes_pars_vertex>
 
 void main() {
 
 	vWorldPosition = transformDirection( position, modelMatrix );
 
-	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
-
+	#include <begin_vertex>
+	#include <project_vertex>
 	#include <logdepthbuf_vertex>
+	#include <clipping_planes_vertex>
 
 }

+ 3 - 0
src/renderers/shaders/ShaderLib/linedashed_frag.glsl

@@ -10,9 +10,12 @@ varying float vLineDistance;
 #include <color_pars_fragment>
 #include <fog_pars_fragment>
 #include <logdepthbuf_pars_fragment>
+#include <clipping_planes_pars_fragment>
 
 void main() {
 
+	#include <clipping_planes_fragment>
+
 	if ( mod( vLineDistance, totalSize ) > dashSize ) {
 
 		discard;

+ 2 - 0
src/renderers/shaders/ShaderLib/linedashed_vert.glsl

@@ -6,6 +6,7 @@ varying float vLineDistance;
 #include <common>
 #include <color_pars_vertex>
 #include <logdepthbuf_pars_vertex>
+#include <clipping_planes_pars_vertex>
 
 void main() {
 
@@ -17,5 +18,6 @@ void main() {
 	gl_Position = projectionMatrix * mvPosition;
 
 	#include <logdepthbuf_vertex>
+	#include <clipping_planes_vertex>
 
 }

+ 3 - 0
src/renderers/shaders/ShaderLib/meshbasic_frag.glsl

@@ -18,9 +18,12 @@ uniform float opacity;
 #include <fog_pars_fragment>
 #include <specularmap_pars_fragment>
 #include <logdepthbuf_pars_fragment>
+#include <clipping_planes_pars_fragment>
 
 void main() {
 
+	#include <clipping_planes_fragment>
+
 	vec4 diffuseColor = vec4( diffuse, opacity );
 
 	#include <logdepthbuf_fragment>

+ 2 - 0
src/renderers/shaders/ShaderLib/meshbasic_vert.glsl

@@ -6,6 +6,7 @@
 #include <morphtarget_pars_vertex>
 #include <skinning_pars_vertex>
 #include <logdepthbuf_pars_vertex>
+#include <clipping_planes_pars_vertex>
 
 void main() {
 
@@ -30,6 +31,7 @@ void main() {
 	#include <logdepthbuf_vertex>
 
 	#include <worldpos_vertex>
+	#include <clipping_planes_vertex>
 	#include <envmap_vertex>
 
 }

+ 3 - 0
src/renderers/shaders/ShaderLib/meshlambert_frag.glsl

@@ -28,9 +28,12 @@ varying vec3 vLightFront;
 #include <shadowmask_pars_fragment>
 #include <specularmap_pars_fragment>
 #include <logdepthbuf_pars_fragment>
+#include <clipping_planes_pars_fragment>
 
 void main() {
 
+	#include <clipping_planes_fragment>
+
 	vec4 diffuseColor = vec4( diffuse, opacity );
 	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
 	vec3 totalEmissiveRadiance = emissive;

+ 2 - 0
src/renderers/shaders/ShaderLib/meshlambert_vert.glsl

@@ -19,6 +19,7 @@ varying vec3 vLightFront;
 #include <skinning_pars_vertex>
 #include <shadowmap_pars_vertex>
 #include <logdepthbuf_pars_vertex>
+#include <clipping_planes_pars_vertex>
 
 void main() {
 
@@ -37,6 +38,7 @@ void main() {
 	#include <skinning_vertex>
 	#include <project_vertex>
 	#include <logdepthbuf_vertex>
+	#include <clipping_planes_vertex>
 
 	#include <worldpos_vertex>
 	#include <envmap_vertex>

+ 3 - 0
src/renderers/shaders/ShaderLib/meshphong_frag.glsl

@@ -26,9 +26,12 @@ uniform float opacity;
 #include <normalmap_pars_fragment>
 #include <specularmap_pars_fragment>
 #include <logdepthbuf_pars_fragment>
+#include <clipping_planes_pars_fragment>
 
 void main() {
 
+	#include <clipping_planes_fragment>
+
 	vec4 diffuseColor = vec4( diffuse, opacity );
 	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
 	vec3 totalEmissiveRadiance = emissive;

+ 2 - 0
src/renderers/shaders/ShaderLib/meshphong_vert.glsl

@@ -18,6 +18,7 @@ varying vec3 vViewPosition;
 #include <skinning_pars_vertex>
 #include <shadowmap_pars_vertex>
 #include <logdepthbuf_pars_vertex>
+#include <clipping_planes_pars_vertex>
 
 void main() {
 
@@ -43,6 +44,7 @@ void main() {
 	#include <skinning_vertex>
 	#include <project_vertex>
 	#include <logdepthbuf_vertex>
+	#include <clipping_planes_vertex>
 
 	vViewPosition = - mvPosition.xyz;
 

+ 3 - 0
src/renderers/shaders/ShaderLib/meshstandard_frag.glsl

@@ -38,9 +38,12 @@ varying vec3 vViewPosition;
 #include <roughnessmap_pars_fragment>
 #include <metalnessmap_pars_fragment>
 #include <logdepthbuf_pars_fragment>
+#include <clipping_planes_pars_fragment>
 
 void main() {
 
+	#include <clipping_planes_fragment>
+
 	vec4 diffuseColor = vec4( diffuse, opacity );
 	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
 	vec3 totalEmissiveRadiance = emissive;

+ 2 - 0
src/renderers/shaders/ShaderLib/meshstandard_vert.glsl

@@ -18,6 +18,7 @@ varying vec3 vViewPosition;
 #include <shadowmap_pars_vertex>
 #include <specularmap_pars_fragment>
 #include <logdepthbuf_pars_vertex>
+#include <clipping_planes_pars_vertex>
 
 void main() { // STANDARD
 
@@ -43,6 +44,7 @@ void main() { // STANDARD
 	#include <skinning_vertex>
 	#include <project_vertex>
 	#include <logdepthbuf_vertex>
+	#include <clipping_planes_vertex>
 
 	vViewPosition = - mvPosition.xyz;
 

+ 2 - 0
src/renderers/shaders/ShaderLib/normal_frag.glsl

@@ -4,9 +4,11 @@ varying vec3 vNormal;
 #include <common>
 #include <packing>
 #include <logdepthbuf_pars_fragment>
+#include <clipping_planes_pars_fragment>
 
 void main() {
 
+	#include <clipping_planes_fragment>
 	gl_FragColor = vec4( packNormalToRGB( vNormal ), opacity );
 
 	#include <logdepthbuf_fragment>

+ 2 - 0
src/renderers/shaders/ShaderLib/normal_vert.glsl

@@ -3,6 +3,7 @@ varying vec3 vNormal;
 #include <common>
 #include <morphtarget_pars_vertex>
 #include <logdepthbuf_pars_vertex>
+#include <clipping_planes_pars_vertex>
 
 void main() {
 
@@ -12,5 +13,6 @@ void main() {
 	#include <morphtarget_vertex>
 	#include <project_vertex>
 	#include <logdepthbuf_vertex>
+	#include <clipping_planes_vertex>
 
 }

+ 3 - 0
src/renderers/shaders/ShaderLib/points_frag.glsl

@@ -7,9 +7,12 @@ uniform float opacity;
 #include <fog_pars_fragment>
 #include <shadowmap_pars_fragment>
 #include <logdepthbuf_pars_fragment>
+#include <clipping_planes_pars_fragment>
 
 void main() {
 
+	#include <clipping_planes_fragment>
+
 	vec3 outgoingLight = vec3( 0.0 );
 	vec4 diffuseColor = vec4( diffuse, opacity );
 

+ 2 - 0
src/renderers/shaders/ShaderLib/points_vert.glsl

@@ -5,6 +5,7 @@ uniform float scale;
 #include <color_pars_vertex>
 #include <shadowmap_pars_vertex>
 #include <logdepthbuf_pars_vertex>
+#include <clipping_planes_pars_vertex>
 
 void main() {
 
@@ -19,6 +20,7 @@ void main() {
 	#endif
 
 	#include <logdepthbuf_vertex>
+	#include <clipping_planes_vertex>
 	#include <worldpos_vertex>
 	#include <shadowmap_vertex>
 

+ 4 - 0
src/renderers/webgl/WebGLProgram.js

@@ -416,6 +416,8 @@ THREE.WebGLProgram = ( function () {
 				parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
 				parameters.flipSided ? '#define FLIP_SIDED' : '',
 
+				'#define NUM_CLIPPING_PLANES ' + parameters.numClippingPlanes,
+
 				parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
 				parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
 
@@ -517,6 +519,8 @@ THREE.WebGLProgram = ( function () {
 				parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
 				parameters.flipSided ? '#define FLIP_SIDED' : '',
 
+				'#define NUM_CLIPPING_PLANES ' + parameters.numClippingPlanes,
+
 				parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
 				parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
 

+ 4 - 2
src/renderers/webgl/WebGLPrograms.js

@@ -25,7 +25,7 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) {
 		"maxMorphTargets", "maxMorphNormals", "premultipliedAlpha",
 		"numDirLights", "numPointLights", "numSpotLights", "numHemiLights",
 		"shadowMapEnabled", "shadowMapType", "toneMapping", 'physicallyCorrectLights',
-		"alphaTest", "doubleSided", "flipSided"
+		"alphaTest", "doubleSided", "flipSided", "numClippingPlanes"
 	];
 
 
@@ -96,7 +96,7 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) {
 
 	}
 
-	this.getParameters = function ( material, lights, fog, object ) {
+	this.getParameters = function ( material, lights, fog, nClipPlanes, object ) {
 
 		var shaderID = shaderIDs[ material.type ];
 
@@ -170,6 +170,8 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) {
 			numSpotLights: lights.spot.length,
 			numHemiLights: lights.hemi.length,
 
+			numClippingPlanes: nClipPlanes,
+
 			shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && lights.shadows.length > 0,
 			shadowMapType: renderer.shadowMap.type,
 

+ 50 - 12
src/renderers/webgl/WebGLShadowMap.js

@@ -25,7 +25,9 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 	_NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1,
 
 	_depthMaterials = new Array( _NumberOfMaterialVariants ),
-	_distanceMaterials = new Array( _NumberOfMaterialVariants );
+	_distanceMaterials = new Array( _NumberOfMaterialVariants ),
+
+	_materialCache = {};
 
 	var cubeDirections = [
 		new THREE.Vector3( 1, 0, 0 ), new THREE.Vector3( - 1, 0, 0 ), new THREE.Vector3( 0, 0, 1 ),
@@ -60,7 +62,8 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 			vertexShader: depthShader.vertexShader,
 			fragmentShader: depthShader.fragmentShader,
 			morphTargets: useMorphing,
-			skinning: useSkinning
+			skinning: useSkinning,
+			clipping: true
 		} );
 
 		_depthMaterials[ i ] = depthMaterial;
@@ -73,7 +76,8 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 			vertexShader: distanceShader.vertexShader,
 			fragmentShader: distanceShader.fragmentShader,
 			morphTargets: useMorphing,
-			skinning: useSkinning
+			skinning: useSkinning,
+			clipping: true
 		} );
 
 		_distanceMaterials[ i ] = distanceMaterial;
@@ -300,7 +304,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 
 		var geometry = object.geometry;
 
-		var newMaterial = null;
+		var result = null;
 
 		var materialVariants = _depthMaterials;
 		var customMaterial = object.customDepthMaterial;
@@ -324,25 +328,59 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
 			if ( useMorphing ) variantIndex |= _MorphingFlag;
 			if ( useSkinning ) variantIndex |= _SkinningFlag;
 
-			newMaterial = materialVariants[ variantIndex ];
+			result = materialVariants[ variantIndex ];
 
 		} else {
 
-			newMaterial = customMaterial;
+			result = customMaterial;
+
+		}
+
+		if ( _renderer.localClippingEnabled &&
+			 material.clipShadows === true &&
+				material.clippingPlanes.length !== 0 ) {
+
+			// in this case we need a unique material instance reflecting the
+			// appropriate state
+
+			var keyA = result.uuid, keyB = material.uuid;
+
+			var materialsForVariant = _materialCache[ keyA ];
+
+			if ( materialsForVariant === undefined ) {
+
+				materialsForVariant = {};
+				_materialCache[ keyA ] = materialsForVariant;
+
+			}
+
+			var cachedMaterial = materialsForVariant[ keyB ];
+
+			if ( cachedMaterial === undefined ) {
+
+				cachedMaterial = result.clone();
+				materialsForVariant[ keyB ] = cachedMaterial;
+
+			}
+
+			result = cachedMaterial;
 
 		}
 
-		newMaterial.visible = material.visible;
-		newMaterial.wireframe = material.wireframe;
-		newMaterial.wireframeLinewidth = material.wireframeLinewidth;
+		result.visible = material.visible;
+		result.wireframe = material.wireframe;
+		result.side = material.side;
+		result.clipShadows = material.clipShadows;
+		result.clippingPlanes = material.clippingPlanes;
+		result.wireframeLinewidth = material.wireframeLinewidth;
 
-		if ( isPointLight && newMaterial.uniforms.lightPos !== undefined ) {
+		if ( isPointLight && result.uniforms.lightPos !== undefined ) {
 
-			newMaterial.uniforms.lightPos.value.copy( lightPositionWorld );
+			result.uniforms.lightPos.value.copy( lightPositionWorld );
 
 		}
 
-		return newMaterial;
+		return result;
 
 	}
 

+ 4 - 0
utils/build/includes/common.json

@@ -132,6 +132,10 @@
 	"src/renderers/shaders/ShaderChunk/beginnormal_vertex.glsl",
 	"src/renderers/shaders/ShaderChunk/bsdfs.glsl",
 	"src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl",
+	"src/renderers/shaders/ShaderChunk/clipping_planes_fragment.glsl",
+	"src/renderers/shaders/ShaderChunk/clipping_planes_pars_fragment.glsl",
+	"src/renderers/shaders/ShaderChunk/clipping_planes_pars_vertex.glsl",
+	"src/renderers/shaders/ShaderChunk/clipping_planes_vertex.glsl",
 	"src/renderers/shaders/ShaderChunk/color_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl",