threejs实现obj模型的多面实心剖切

194 阅读2分钟

前言

threejs实现obj模型的多面实心剖切

效果

image.png

image.png

代码

	let  DOM,width,height; 
	let  animationEvent;
	let  center = new THREE.Vector3();
	let group = new THREE.Group(); 
	// 暂停tween.js动画
	let tween; 
	let camera, scene, renderer, object,object2,object3, stats;
	let planes, planeObjects=[],planeObjects2=[],planeObjects3=[],  displayHelper;
	let params = {planeX: {constant: 0,negated: false,displayHelper: false},planeY: {constant: 0,negated: false,displayHelper: false},};
	


	function createPlaneStencilGroup( geometry, plane, renderOrder ) {
		var group = new THREE.Group();
		var baseMat = new THREE.MeshBasicMaterial();
		baseMat.depthWrite = false;
		baseMat.depthTest = false;
		baseMat.colorWrite = false;
		baseMat.stencilWrite = true;
		baseMat.stencilFunc = THREE.AlwaysStencilFunc;
		var mat0 = baseMat.clone();
		mat0.side = THREE.BackSide;
		mat0.clippingPlanes = [ plane ];
		mat0.stencilFail = THREE.IncrementWrapStencilOp;
		mat0.stencilZFail = THREE.IncrementWrapStencilOp;
		mat0.stencilZPass = THREE.IncrementWrapStencilOp;
		var mesh0 = new THREE.Mesh( geometry, mat0 );
		mesh0.renderOrder = renderOrder;
		group.add( mesh0 );
		var mat1 = baseMat.clone();
		mat1.side = THREE.FrontSide;
		mat1.clippingPlanes = [ plane ];
		mat1.stencilFail = THREE.DecrementWrapStencilOp;
		mat1.stencilZFail = THREE.DecrementWrapStencilOp;
		mat1.stencilZPass = THREE.DecrementWrapStencilOp;
		var mesh1 = new THREE.Mesh( geometry, mat1 );
		mesh1.renderOrder = renderOrder;
		group.add( mesh1 );
		return group;
	}
	function init() {
		DOM= document.getElementById('three1')  
		width = DOM.offsetWidth; //宽度  
		height = DOM.offsetHeight; //高度  
		scene = new THREE.Scene();
		camera = new THREE.PerspectiveCamera( 36, width / height, 1, 100000 );
		camera.position.set( 0, 0, 20 );
		planes = [new THREE.Plane( new THREE.Vector3( 0,- 1, 0 ), 0 ),new THREE.Plane( new THREE.Vector3( -1,0, 0 ), 0 )];
		displayHelper = planes.map( p => new THREE.PlaneHelper( p, 10, 0xffffff ) );
    	displayHelper.forEach( ph => {ph.visible = false;  scene.add( ph );} );
		scene.add(new THREE.AmbientLight(0xffffff,1));  
		object = new THREE.Group();
		object2 = new THREE.Group();
		object3 = new THREE.Group();
		scene.add( object );
		scene.add( object2 );
		scene.add( object3 ); 
		var obj_loader = new OBJLoader();
		var mtl_loader=new MTLLoader();
		var obj_loader2 = new OBJLoader();
		var mtl_loader2 =new MTLLoader();
		var obj_loader3 = new OBJLoader();
		var mtl_loader3 =new MTLLoader();
		mtl_loader.load( '/zeng/实体.mtl', function (materials ) { 
			materials.preload();
			obj_loader.setMaterials(materials);
			obj_loader.load( '/zeng/实体.obj', function (obj ) { 
				obj.scale.set(0.01,0.01,0.01);
				let bbox = new THREE.Box3().setFromObject(obj);
				var x=-(bbox.max.x+bbox.min.x)/2;
				var y=-(bbox.max.y+bbox.min.y)/2;
				var z= -(bbox.max.z+bbox.min.z)/2;
				obj.position.set(-(bbox.max.x+bbox.min.x)/2,-(bbox.max.y+bbox.min.y)/2,-(bbox.max.z+bbox.min.z)/2);
				obj.traverse(function(child){ 
					if (child instanceof THREE.Mesh) { 
						child.material = new THREE.MeshBasicMaterial({
							color: child.material.color,
							metalness: 0.1,
							roughness: 0.75,
							clippingPlanes: planes,
							clipShadows: true,
							shadowSide: THREE.DoubleSide, 
							// side: THREE.DoubleSide, 
						});
						child.castShadow = true;
						child.renderOrder = 6;
					}
				}) 
				object.add(obj); 
				var planeGeom = new THREE.PlaneGeometry( 4000, 4000 );
				for ( let i = 0; i < 2; i ++ ) {
					var poGroup = new THREE.Group();
					var plane = planes[ i ]; 
					for (var w = 0; w < obj.children.length; w++) {
						var geometry0 = object.children[0].children[w].geometry.clone();
						geometry0.translate(x/0.01, y/0.01, z/0.01);
						var stencilGroup = createPlaneStencilGroup(geometry0, plane, i+ 1);
						stencilGroup.scale.set(0.01,0.01,0.01)
						object.add(stencilGroup);
					}
					var planeMat =new THREE.MeshBasicMaterial( {
						color: 0x3B4600,
						clippingPlanes: planes.filter( p => p !== plane ),
						stencilWrite: true,
						stencilRef: 0,
						stencilFunc: THREE.NotEqualStencilFunc,
						stencilFail: THREE.ReplaceStencilOp,
						stencilZFail: THREE.ReplaceStencilOp,
						stencilZPass: THREE.ReplaceStencilOp,
					} );
					var po = new THREE.Mesh( planeGeom, planeMat );
					po.onAfterRender = function ( renderer ) {renderer.clearStencil();};
					po.renderOrder = i + 1.1;
					poGroup.add( po );
					planeObjects2.push( po );
					poGroup.scale.set(1,1,1)
					scene.add( poGroup );
				}
			});
		})

		var ground = new THREE.Mesh(new THREE.PlaneGeometry( 9, 9, 1, 1 ),new THREE.ShadowMaterial( { color: 0, opacity: 0.25, side: THREE.DoubleSide } ));
		ground.rotation.x = - Math.PI / 2;
		ground.position.y = - 1;ground.receiveShadow = true;
		scene.add( ground );
		stats = new Stats();
		document.body.appendChild( stats.dom );
		renderer = new THREE.WebGLRenderer( { antialias: true } );
		renderer.shadowMap.enabled = true;
		renderer.setClearColor(0xb9d3ff, 0.1); //设置背景颜色
		renderer.setPixelRatio( window.devicePixelRatio );
		renderer.setSize( width, height );
		window.addEventListener( 'resize', onWindowResize, false );
		 DOM.appendChild(renderer.domElement); 
		renderer.localClippingEnabled = true;
		var controls = new OrbitControls( camera, renderer.domElement );controls.update();
		// GUI
        const gui = new GUI();
        gui.add( params, 'animate' );

        const planeX = gui.addFolder( 'planeX' );
        planeX.add( params.planeX, 'displayHelper' ).onChange( v => displayHelper[ 0 ].visible = v );
        planeX.add( params.planeX, 'constant' ).min( - 3 ).max( 3 ).onChange( d => planes[ 0 ].constant = d );
        planeX.add( params.planeX, 'negated' ).onChange( () => { planes[ 0 ].negate(); params.planeX.constant = planes[ 0 ].constant; } );
        planeX.open();

        const planeY = gui.addFolder( 'planeY' );
        planeY.add( params.planeY, 'displayHelper' ).onChange( v => displayHelper[ 1 ].visible = v );
        planeY.add( params.planeY, 'constant' ).min( - 15 ).max( 15 ).onChange( d => planes[ 1 ].constant = d );
        planeY.add( params.planeY, 'negated' ).onChange( () => { planes[ 1 ].negate(); params.planeY.constant = planes[ 1 ].constant; } );
        planeY.open();
	}
	function onWindowResize() {
        camera.aspect = width / height;
        camera.updateProjectionMatrix();
        renderer.setSize( width, height );
    } 
    function animate() {
        requestAnimationFrame( animate );
        for ( var i = 0; i < planeObjects.length; i ++ ) { 
            var plane = planes[ i ];
            var po = planeObjects[ i ]; 
            plane.coplanarPoint( po.position );
            po.lookAt(po.position.x - plane.normal.x,po.position.y - plane.normal.y,po.position.z - plane.normal.z,);
        }
        for ( var i = 0; i < planeObjects2.length; i ++ ) { 
            var plane = planes[ i ];
            var po = planeObjects2[ i ]; 
            plane.coplanarPoint( po.position );
            po.lookAt(po.position.x - plane.normal.x,po.position.y - plane.normal.y,po.position.z - plane.normal.z,);
        }
        for ( var i = 0; i < planeObjects3.length; i ++ ) { 
            var plane = planes[ i ];
            var po = planeObjects3[ i ]; 
            plane.coplanarPoint( po.position );
            po.lookAt(po.position.x - plane.normal.x,po.position.y - plane.normal.y,po.position.z - plane.normal.z,);
        }
        stats.begin();
        renderer.render( scene, camera );
        stats.end();
    }