threejs实现多模型多颜色的实心剖切

144 阅读2分钟

前言

threejs实现一个多层模型的剖分,剖分出来的是实心的

效果

image.png

image.png

代码

var planes, planeObjects=[],planeObjects2=[], planeObjects3=[], planeHelpers;
var params = {planeY: {constant: 0,negated: false,displayHelper: false},};
init();
animate();
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() {
	scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera( 36, window.innerWidth / window.innerHeight, 1, 100000 );
    camera.position.set( 0, 0, 20 );
    let ambientLight = new THREE.AmbientLight(0xcccccc,0.6);
    scene.add(ambientLight);
    var dirLight = new THREE.DirectionalLight( 0xffffff, 1 );
    scene.add( dirLight );
    planes = [new THREE.Plane( new THREE.Vector3( - 1,0, 0 ), 0 ),];
    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_loader2.load( '/zeng/box.mtl', function (materials ) { 
        materials.preload();
        obj_loader2.setMaterials(materials);
        obj_loader2.load( '/zeng/box.obj', function (obj ) { 
			obj.scale.set(0.0001,0.0001,0.0001);
			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.MeshStandardMaterial({
						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;
                }
            }) 
			object2.add(obj); 
			var planeGeom = new THREE.PlaneGeometry( 4000, 4000 );
			var poGroup = new THREE.Group();
			var plane = planes[ 0 ]; 
			for (var w = 0; w < obj.children.length; w++) {
				var geometry0 = object2.children[0].children[w].geometry.clone();
				geometry0.translate(x/0.0001, y/0.0001, z/0.0001);
				var stencilGroup = createPlaneStencilGroup(geometry0, plane,  1);
				stencilGroup.scale.set(0.0001,0.0001,0.0001)
				object2.add(stencilGroup);
			}
			var planeMat =new THREE.MeshBasicMaterial( {
				color: 0x4B580C,
                metalness: 0.1, //金属度属性
                roughness: 0.75, //粗糙度
				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 = 1.1;
			poGroup.add( po );
			planeObjects2.push( po );
			scene.add( poGroup );
        });
    })
    mtl_loader.load( '/zeng/box.mtl', function (materials ) {
        console.log(materials,'materials')
        materials.preload();
        obj_loader.setMaterials(materials);
        obj_loader.load( '/zeng/box.obj', function (obj ) {
            console.log(obj,'obj')
            obj.position.y =40
			obj.scale.set(0.0001,0.0001,0.0001);
			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.MeshStandardMaterial({
						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 );
			var poGroup = new THREE.Group();
			var plane = planes[ 0 ]; 
			for (var w = 0; w < obj.children.length; w++) {
				var geometry0 = object.children[0].children[w].geometry.clone();
				geometry0.translate(x/0.0001, y/0.0001, z/0.0001);
				var stencilGroup = createPlaneStencilGroup(geometry0, plane,  2);
				stencilGroup.scale.set(0.0001,0.0001,0.0001)
				object.add(stencilGroup);
			}
			var planeMat =new THREE.MeshBasicMaterial( {
				color: 0x1C1C1C,
                metalness: 0.1, //金属度属性
                roughness: 0.75, //粗糙度
				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 = 2.1;
			poGroup.add( po );
			planeObjects.push( po );
			scene.add( poGroup );
        });
    })
    mtl_loader3.load( '/zeng/box.mtl', function (materials ) { 
        materials.preload();
        obj_loader3.setMaterials(materials);
        obj_loader3.load( '/zeng/box.obj', function (obj ) { 
            obj.position.y = 80
			obj.scale.set(0.0001,0.0001,0.0001);
			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.MeshStandardMaterial({
						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;
                }
            })
			object3.add(obj); 
			var planeGeom = new THREE.PlaneGeometry( 4000, 4000 );
			var poGroup = new THREE.Group();
			var plane = planes[ 0 ]; 
			for (var w = 0; w < obj.children.length; w++) {
				var geometry0 = object3.children[0].children[w].geometry.clone();
				geometry0.translate(x/0.0001, y/0.0001, z/0.0001);
				var stencilGroup = createPlaneStencilGroup(geometry0, plane,  3);
				stencilGroup.scale.set(0.0001,0.0001,0.0001)
				object3.add(stencilGroup);
			}
			var planeMat =new THREE.MeshBasicMaterial( {
				color: 0x00B000,
                metalness: 0.1, //金属度属性
                roughness: 0.75, //粗糙度
				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 = 3.1;
			poGroup.add( po );
			planeObjects3.push( po );
			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.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    renderer.setClearColor( 0x263238 );
    window.addEventListener( 'resize', onWindowResize, false );
    document.body.appendChild( renderer.domElement );
    renderer.localClippingEnabled = true;
    var controls = new OrbitControls( camera, renderer.domElement );controls.update();
    var gui = new GUI();
    var planeY = gui.addFolder( 'planeY' );
    planeY.add( params.planeY, 'displayHelper' ).onChange( v => planeHelpers[ 1 ].visible = v );
    planeY.add( params.planeY, 'constant' ).min( -300 ).max( 300 ).onChange( d => planes[ 0 ].constant = d );
    // planeY.add( params2.planeY, 'constant' ).min( -300 ).max( 300 ).onChange( d => planes[ 0 ].constant = d );
    // planeY.add( params3.planeY, 'constant' ).min( -300 ).max( 300 ).onChange( d => planes[ 0 ].constant = d );
    planeY.add( params.planeY, 'negated' ).onChange( () => {planes[ 0 ].negate();params.planeY.constant = planes[ 0 ].constant;} );planeY.open();}
	function onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize( window.innerWidth, window.innerHeight );
    } 
    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();
    }