浏览代码

Intersection option for WebGLRenderer clipping method (#9470)

* Add material option

* Local clipIntersection

* Updated Docs

* improving global/local clipping interaction

* intersection parameter update

* intersection shader fix

* removed commented code

* remove comment
Peter Whidden 8 年之前
父节点
当前提交
449d18aeaf

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

@@ -119,6 +119,10 @@
 		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 null.
 		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 null.
 		</div>
 		</div>
 
 
+		<h3>[property:Boolean clipIntersection]</h3>
+
+		<div>Changes the behavior of clipping planes so that only their intersection is clipped, rather than their union. Default is false. See <a href="http://threejs.org/examples/#webgl_clipping_intersection">example</a> </div>
+
 		<h3>[property:Boolean clipShadows]</h3>
 		<h3>[property:Boolean clipShadows]</h3>
 		<div>
 		<div>
 		Defines whether to clip shadows according to the clipping planes specified on this material. Default is false.
 		Defines whether to clip shadows according to the clipping planes specified on this material. Default is false.

+ 147 - 0
examples/webgl_clipping_intersection.html

@@ -0,0 +1,147 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - clipIntersection</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/stats.min.js"></script>
+		<script src="js/libs/dat.gui.min.js"></script>
+
+		<script>
+
+			
+
+			function init() {
+
+				camera = new THREE.PerspectiveCamera(
+						40, window.innerWidth / window.innerHeight, 1, 800 );
+
+				camera.position.set( -20, 10, 50 );
+				camera.lookAt( new THREE.Vector3( 0, 0, 0) );
+
+				scene = new THREE.Scene();
+
+				// Lights
+
+				var light = new THREE.HemisphereLight( 0xffffbb, 0x080820, 1 );
+				scene.add( light );
+
+				var clipPlanes = [ new THREE.Plane( new THREE.Vector3( 1,  0,  0 ), 0 ), 
+								   new THREE.Plane( new THREE.Vector3( 0, -1,  0 ), 0 ), 
+								   new THREE.Plane( new THREE.Vector3( 0,  0, -1 ), 0 ) ]
+
+				scene.add( new THREE.AmbientLight( 0x505050 ) );
+
+				balls = new THREE.Object3D();
+
+				for ( var i = 1; i < 25; i++ ) {
+					balls.add( new THREE.Mesh( 
+						new THREE.SphereBufferGeometry( i / 2, 48, 48), 
+						new THREE.MeshStandardMaterial( { color: new THREE.Color( Math.sin( i * 0.5 ) * 0.5 + 0.5, 
+																				  Math.cos( i * 1.5 ) * 0.5 + 0.5, 
+																				  Math.sin( i * 4.5 + 0 ) * 0.5 + 0.5 ),
+														  			roughness: 0.95, metalness: 0.0, side: THREE.DoubleSide, clippingPlanes: clipPlanes, clipIntersection: true } )
+						) );
+				}  
+
+				scene.add( balls );
+					
+				// Renderer
+
+				var container = document.body;
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.antialias = true;
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.setClearColor( 0x222222 );
+				renderer.localClippingEnabled = true;
+
+				window.addEventListener( 'resize', onWindowResize, false );
+				container.appendChild( renderer.domElement );
+
+
+				// Stats
+
+				stats = new Stats();
+				container.appendChild( stats.dom );
+
+				// GUI
+
+				mode = {};
+				mode.clipIntersection = true;
+				mode.clipPosition = 0;
+				var gui = new dat.GUI();
+				gui.width = 800;
+				gui.add( mode, 'clipIntersection' ).onChange( function() {
+					for (var i = 0; i < balls.children.length; i++) {
+						balls.children[i].material.clipIntersection = !balls.children[i].material.clipIntersection;
+					}
+				} );
+
+				gui.add( mode, 'clipPosition', -16, 16 ).onChange( function( value ) {
+
+				} );
+
+				// Controls
+
+				var controls = new THREE.OrbitControls( camera, renderer.domElement );
+				controls.target.set( 0, 1, 0 );
+				controls.update();
+
+				// Start
+
+				startTime = Date.now();
+				time = 0;
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+
+
+				requestAnimationFrame( animate );
+
+
+				for ( var obj = 0; obj < balls.children.length; obj++ ) {
+					var current = balls.children[ obj ].material;
+					for ( var i = 0; i < current.clippingPlanes.length; i++ ) {
+						var plane = current.clippingPlanes[ i ];
+						plane.constant = ( 49 * plane.constant + mode.clipPosition ) / 50;
+					}
+				}
+
+
+				stats.begin();
+				renderer.render( scene, camera );
+				stats.end();
+
+			}
+
+			init();
+			animate();
+
+		</script>
+
+	</body>
+</html>

+ 2 - 0
src/materials/Material.js

@@ -39,6 +39,7 @@ function Material() {
 	this.depthWrite = true;
 	this.depthWrite = true;
 
 
 	this.clippingPlanes = null;
 	this.clippingPlanes = null;
+	this.clipIntersection = false;
 	this.clipShadows = false;
 	this.clipShadows = false;
 
 
 	this.colorWrite = true;
 	this.colorWrite = true;
@@ -302,6 +303,7 @@ Material.prototype = {
 
 
 		this.visible = source.visible;
 		this.visible = source.visible;
 		this.clipShadows = source.clipShadows;
 		this.clipShadows = source.clipShadows;
+		this.clipIntersection = source.clipIntersection;
 
 
 		var srcPlanes = source.clippingPlanes,
 		var srcPlanes = source.clippingPlanes,
 			dstPlanes = null;
 			dstPlanes = null;

+ 5 - 3
src/renderers/WebGLRenderer.js

@@ -1541,7 +1541,7 @@ function WebGLRenderer( parameters ) {
 		var materialProperties = properties.get( material );
 		var materialProperties = properties.get( material );
 
 
 		var parameters = programCache.getParameters(
 		var parameters = programCache.getParameters(
-				material, _lights, fog, _clipping.numPlanes, object );
+				material, _lights, fog, _clipping.numPlanes, _clipping.numIntersection, object );
 
 
 		var code = programCache.getProgramCode( material, parameters );
 		var code = programCache.getProgramCode( material, parameters );
 
 
@@ -1644,6 +1644,7 @@ function WebGLRenderer( parameters ) {
 		       material.clipping === true ) {
 		       material.clipping === true ) {
 
 
 			materialProperties.numClippingPlanes = _clipping.numPlanes;
 			materialProperties.numClippingPlanes = _clipping.numPlanes;
+			materialProperties.numIntersection = _clipping.numIntersection;
 			uniforms.clippingPlanes = _clipping.uniform;
 			uniforms.clippingPlanes = _clipping.uniform;
 
 
 		}
 		}
@@ -1719,7 +1720,7 @@ function WebGLRenderer( parameters ) {
 				// object instead of the material, once it becomes feasible
 				// object instead of the material, once it becomes feasible
 				// (#8465, #8379)
 				// (#8465, #8379)
 				_clipping.setState(
 				_clipping.setState(
-						material.clippingPlanes, material.clipShadows,
+						material.clippingPlanes, material.clipIntersection, material.clipShadows,
 						camera, materialProperties, useCache );
 						camera, materialProperties, useCache );
 
 
 			}
 			}
@@ -1741,7 +1742,8 @@ function WebGLRenderer( parameters ) {
 				material.needsUpdate = true;
 				material.needsUpdate = true;
 
 
 			} else if ( materialProperties.numClippingPlanes !== undefined &&
 			} else if ( materialProperties.numClippingPlanes !== undefined &&
-				materialProperties.numClippingPlanes !== _clipping.numPlanes ) {
+				( materialProperties.numClippingPlanes !== _clipping.numPlanes || 
+ 				  materialProperties.numIntersection  !== _clipping.numIntersection ) ) {
 
 
 				material.needsUpdate = true;
 				material.needsUpdate = true;
 
 

+ 13 - 1
src/renderers/shaders/ShaderChunk/clipping_planes_fragment.glsl

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

+ 5 - 1
src/renderers/webgl/WebGLClipping.js

@@ -21,6 +21,7 @@ function WebGLClipping() {
 
 
 	this.uniform = uniform;
 	this.uniform = uniform;
 	this.numPlanes = 0;
 	this.numPlanes = 0;
+	this.numIntersection = 0;
 
 
 	this.init = function( planes, enableLocalClipping, camera ) {
 	this.init = function( planes, enableLocalClipping, camera ) {
 
 
@@ -55,7 +56,7 @@ function WebGLClipping() {
 
 
 	};
 	};
 
 
-	this.setState = function( planes, clipShadows, camera, cache, fromCache ) {
+	this.setState = function( planes, clipIntersection, clipShadows, camera, cache, fromCache ) {
 
 
 		if ( ! localClippingEnabled ||
 		if ( ! localClippingEnabled ||
 				planes === null || planes.length === 0 ||
 				planes === null || planes.length === 0 ||
@@ -90,6 +91,7 @@ function WebGLClipping() {
 			}
 			}
 
 
 			cache.clippingState = dstArray;
 			cache.clippingState = dstArray;
+			this.numIntersection = clipIntersection ? this.numPlanes : 0;
 			this.numPlanes += nGlobal;
 			this.numPlanes += nGlobal;
 
 
 		}
 		}
@@ -107,6 +109,7 @@ function WebGLClipping() {
 		}
 		}
 
 
 		scope.numPlanes = numGlobalPlanes;
 		scope.numPlanes = numGlobalPlanes;
+		scope.numIntersection = 0;
 
 
 	}
 	}
 
 
@@ -151,6 +154,7 @@ function WebGLClipping() {
 		}
 		}
 
 
 		scope.numPlanes = nPlanes;
 		scope.numPlanes = nPlanes;
+		
 		return dstArray;
 		return dstArray;
 
 
 	}
 	}

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

@@ -451,6 +451,7 @@ function WebGLProgram( renderer, code, material, parameters ) {
 			parameters.flipSided ? '#define FLIP_SIDED' : '',
 			parameters.flipSided ? '#define FLIP_SIDED' : '',
 
 
 			'#define NUM_CLIPPING_PLANES ' + parameters.numClippingPlanes,
 			'#define NUM_CLIPPING_PLANES ' + parameters.numClippingPlanes,
+			'#define UNION_CLIPPING_PLANES ' + (parameters.numClippingPlanes - parameters.numClipIntersection),
 
 
 			parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
 			parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
 			parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
 			parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',

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

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