瀏覽代碼

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.
 		</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>
 		<div>
 		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.clippingPlanes = null;
+	this.clipIntersection = false;
 	this.clipShadows = false;
 
 	this.colorWrite = true;
@@ -302,6 +303,7 @@ Material.prototype = {
 
 		this.visible = source.visible;
 		this.clipShadows = source.clipShadows;
+		this.clipIntersection = source.clipIntersection;
 
 		var srcPlanes = source.clippingPlanes,
 			dstPlanes = null;

+ 5 - 3
src/renderers/WebGLRenderer.js

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

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

@@ -1,10 +1,22 @@
 #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 ];
 		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

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

@@ -21,6 +21,7 @@ function WebGLClipping() {
 
 	this.uniform = uniform;
 	this.numPlanes = 0;
+	this.numIntersection = 0;
 
 	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 ||
 				planes === null || planes.length === 0 ||
@@ -90,6 +91,7 @@ function WebGLClipping() {
 			}
 
 			cache.clippingState = dstArray;
+			this.numIntersection = clipIntersection ? this.numPlanes : 0;
 			this.numPlanes += nGlobal;
 
 		}
@@ -107,6 +109,7 @@ function WebGLClipping() {
 		}
 
 		scope.numPlanes = numGlobalPlanes;
+		scope.numIntersection = 0;
 
 	}
 
@@ -151,6 +154,7 @@ function WebGLClipping() {
 		}
 
 		scope.numPlanes = nPlanes;
+		
 		return dstArray;
 
 	}

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

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

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

@@ -32,7 +32,7 @@ function WebGLPrograms( renderer, capabilities ) {
 		"maxMorphTargets", "maxMorphNormals", "premultipliedAlpha",
 		"numDirLights", "numPointLights", "numSpotLights", "numHemiLights",
 		"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 ];
 
@@ -181,6 +181,7 @@ function WebGLPrograms( renderer, capabilities ) {
 			numHemiLights: lights.hemi.length,
 
 			numClippingPlanes: nClipPlanes,
+			numClipIntersection: nClipIntersection,
 
 			shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && lights.shadows.length > 0,
 			shadowMapType: renderer.shadowMap.type,