一、
由于项目需要,我进行了模型剖切功能方面的研究,如果单纯实现模型剖切效果,我相信你会从这篇文章中得到收获。


二、
我对模型剖切功能的研究主要基于three.js中的webgl_clipping_stencil.html进行,该示例中被剖切的网格模型是由Three.js自身创建的几何体和材质构成,当我把导入的obj模型添加进去之后,始终没有剖切成功,更别说绘制剖切面了,这花费了我很长时间,还查过着色器相关的知识,后来随着对Three.js学习的深入,我认识到了我的错误。。。

实际上,最终的代码十分简单,导入的obj模型 和Three.js自身创建的网格模型本质上是一样的,所以上只要用obj模型合理的替代原本的网格模型,便可以实现剖切及剖切面的绘制,不过这样并没有实际上学习到模型剖切的原理,可以在研究场景渲染效果时加深对着色器的认识,再回过头分析剖切的原理。


三、
首先对源代码中的一些内容进行介绍:

1.创建三个剖切平面,gui用于调节剖切平面的位置

 planes = [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 )];

2.创建 组object,将网格模型 clippedColorFront 和 剖切面stencilGroup 添加到object中,实际上分为两部分内容:将模型位于剖切一侧的部分剪切掉,添加到 组object中,此时只是实现剖切,但是没有剖切面;调用creatPlaneStencilGroup函数,设置几何体、平面、渲染顺序,生成剖切面的网格模型,同样添加到 组object中。

var camera, scene, renderer, object, stats;
//初始化组
object = new THREE.Group();
scene.add( object );
//添加网格模型
var clippedColorFront = new THREE.Mesh( geometry, material );
object.add( clippedColorFront );
//绘制剖切面
var stencilGroup = createPlaneStencilGroup( geometry, plane, i + 1 );
object.add( stencilGroup );

四、
接下来对代码进行修改,实现obj模型的剖切
1.在加载obj模型的函数中修改模型的材质,设置属性clippingPlanes,其参数对应先前创建的平面,实现模型的剪切。

obj.traverse(function(child){if (child instanceof THREE.Mesh) {child.material = new THREE.MeshStandardMaterial({color: child.material.color,clippingPlanes: planes,clipShadows: true,shadowSide: THREE.DoubleSide,});child.castShadow = true;child.renderOrder = 6;}
})

2.由于我使用的模型尺寸很大,设置模型缩放比例为0.001,同时为确保模型位于场景中心,模型位置为(x,y,z)。剖切面的绘制如代码所示,object为创建的组,object.children[0]代表加载的obj模型,其子对象为网格模型,具有几何体和材质。这里还需要设置geometry.translate和stencilGroup.scale,从而确保剖切面的比例和位置正确。

 for (var w = 0; w < obj.children.length; w++) {var geometry0 = object.children[0].children[w].geometry.clone();geometry0.translate(x/0.001, y/0.001, z/0.001);var stencilGroup = createPlaneStencilGroup(geometry0, plane,  1);stencilGroup.scale.set(0.001,0.001,0.001)object.add(stencilGroup);}

3.还有一点需要注意,只有位于planeGeom范围内的模型才会被成功剖切,所以要设定合适的参数。

var planeGeom = new THREE.PlaneBufferGeometry( 4000, 4000 );

五、
下面为obj模型剖切的完整代码,为避免代码过长,仅定义了一个剖切平面。

<!DOCTYPE html>
<html lang="en">
<head><title>three.js webgl - clipping stencil</title><meta charset="utf-8">
</head>
<style>body{overflow:hidden;background-color:#263238;}
</style>
<body>
<script type="module">import * as THREE from '../build/three.module.js';import { OrbitControls } from './jsm/controls/OrbitControls.js';import { GUI } from './jsm/libs/dat.gui.module.js';import Stats from './jsm/libs/stats.module.js';import { OBJLoader } from './jsm/loaders/OBJLoader.js';import { MTLLoader } from './jsm/loaders/MTLLoader.js';var camera, scene, renderer, object, stats;var planes, planeObjects=[], 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;// back facesvar 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 );// front facesvar 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, 100 );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( 0, - 1, 0 ), 0 ),];planeHelpers = planes.map( p => new THREE.PlaneHelper( p, 10, 0xffffff ) );planeHelpers.forEach( ph => {ph.visible = false;scene.add( ph );} );object = new THREE.Group();scene.add( object );var obj_loader = new OBJLoader();var mtl_loader=new MTLLoader();mtl_loader.load( 'models/obj/blue0.mtl', function (materials ) {materials.preload();obj_loader.setMaterials(materials);obj_loader.load( 'models/obj/blue0.obj', function (obj ) {obj.scale.set(0.001,0.001,0.001);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,clippingPlanes: planes,clipShadows: true,shadowSide: THREE.DoubleSide,});child.castShadow = true;child.renderOrder = 6;}})console.log(obj)object.add(obj);var planeGeom = new THREE.PlaneBufferGeometry( 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.001, y/0.001, z/0.001);var stencilGroup = createPlaneStencilGroup(geometry0, plane,  1);stencilGroup.scale.set(0.001,0.001,0.001)object.add(stencilGroup);}var planeMat =new THREE.MeshBasicMaterial( {color: 0xffff00,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 );planeObjects.push( po );scene.add( poGroup );} );})var ground = new THREE.Mesh(new THREE.PlaneBufferGeometry( 9, 9, 1, 1 ),new THREE.ShadowMaterial( { color: 0, opacity: 0.25, side: THREE.DoubleSide } ));ground.rotation.x = - Math.PI / 2; // rotates X/Y to X/Zground.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( - 6 ).max( 6 ).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,);}stats.begin();renderer.render( scene, camera );stats.end();}</script>
</body>
</html>

Three.js OBJ模型的剖切相关推荐

  1. threejs 场景中模型部分剖切

    scene.getObjectByName('A2_4_Building_Opacity').traverse(function (obj) {if (obj.type == 'Mesh') {obj ...

  2. three.js obj模型的mtl材质贴图不显示

    在加载成功后模型会显示黑色或者白色模块,但是mtl文件是加载成功的,如果在mtl中使用了纹理贴图,需要在mtl文件中修改一下纹理贴图的路径(不只是需要mtl文件,还需要贴图文件),这里的路径是图片的相 ...

  3. Unity插件使用(2)_剖切插件CrossSection(HDRP)

    目录 一.插件使用 二.修改增加动态切换的功能 三.包围盒移动问题 四.合并项目中后剖切的效果错误问题 五.剖切面贴图 插件默认是Build-in,需要打开HDRP包,2.7版本才开始支持HDRP,有 ...

  4. Three.js 剖切模型功能封装

    1.获取到需要剖切模型的包围盒,并计算每个包围盒的顶点位置,代码如下: var Box = new THREE.Box3().setFromObject(mesh); console.log(Box) ...

  5. 基于 React hooks + Typescript + Cesium 实现模型剖切分析

    文章目录 效果截图 功能介绍 实现思路 实现步骤 根据数据获取裁剪面 modelMatrix 创建 clippingPlanes 添加鼠标事件 封装 Clipping3D 类 使用 Clipping3 ...

  6. Unity Shader Graph实现模型任意方向剖切(学习笔记/五毛钱特效)

    使用Shader Graph:Unlit Master 使用Position获取物体Object的空间坐标信息. 使用Split拆分物体的空间坐标信息,比如拆分为x.y.z. 使用Split拆分后的x ...

  7. Three.js实现剖切

    three.js-cutting 基于Three.js实现的剖切 欢迎交流与指正,需要源码以及这个小Demo教程的可以评论区留言或者私信我

  8. three.js加载obj模型和材质

    1.Vue中安装three.js和加载用的包 安装three.js使用npm install three --save 安装加载obj和mtl文件的包npm install three-obj-mtl ...

  9. three.js加载OBJ模型

    three.js加载OBJ模型 推荐一个免费下载3D模型的网址https://www.cgtrader.com,包含多种格式(obj, mtl等). three.js现在是es6语法,旧版本是es5的 ...

最新文章

  1. 1080Ti 就搞定最新 SOTA 模型?一个普通研究生勇敢发毕业论文引起热议
  2. 密码学赛后技术总结 小片段啦
  3. matlab 常用数学函数
  4. 启动mysql提1067_win7系统启动mysql服务提升错误1067进程意外终止的解决方法
  5. jquery 回车事件
  6. 010 使用list和tuple
  7. .NET斗鱼直播弹幕客户端(上)
  8. MAC版CRT使用心得
  9. 火星人敏捷开发手册 2011-10-14 发布
  10. hdu 4609 3-idiots——FFT
  11. unity打包出来的exe带有dll文件
  12. OpenCV 学习笔记-day10 图像像素的逻辑操作 (Opencv中rectangle函数与Rect函数的用法以及位相关操作)
  13. Horizontalscrollview
  14. JavaScript的toast
  15. android遥控杆控件,Android自定义滑杆控件SeekBar多功能版本
  16. 数据库基础-以命令方式创建数据库
  17. 8月16最新青龙京东拉库《目前更新的库》
  18. 2021-2027全球与中国双面身份证打印机市场现状及未来发展趋势
  19. 关于Tushare库的使用
  20. K8S(Rancher)Ingress 规则 - Nginx 反向代理重定向

热门文章

  1. 导出excel时报错
  2. 视觉伺服控制工具Visual Servoing Platform---VISP(6)----基于4个平面点的姿态估计
  3. 在uniapp使用微信插件获取不到回调数据问题解决
  4. 对软件公司不要传智播客学生的驳斥
  5. Python源码学习笔记:Python程序执行过程与字节码
  6. ipad越狱后怎么还原?
  7. android ble peripheral,Android - BlueTooth BLE 之 Central 与 Peripheral 理解
  8. 【产品】OEM、ODM、OBM是什么?
  9. 『中级篇』docker-swarm创建一个多节点集群(43)
  10. 汇编语言:矩阵式键盘扫描实验