先看下效果

一、原理说明

让各个子mesh沿着一个固定的方向移动,这个方向就是该子mesh的包围盒中心与爆炸中心(模型整体的包围盒中心)的连线。移动轨迹如下图

二、代码如下

import{Vector3,Box3,Mesh,LineBasicMaterial,Geometry,Line,Matrix4
} from 'three'/*** 使用方法:* 1、调用setSplitModel函数将要拆分的模型传入预处理* 然后两种控制爆炸方式* (1)实时更新*      调用startSplit()/quitSplit()函数开始爆炸/恢复*      需要在three的animate函数中调用update函数* (2)滑动条*      调用setValue函数把滑动条的值传入。*/
export class ModelSplit{constructor(){this.meshList = [];this.running = false;this.targetSplitValue = 0;this.currentSplitValue = 0;this.offset = 1;this.splitScale = 1; //影响拆分距离,就是mesh的包围盒中心与爆炸中心的距离的倍率this.splitSpeed = 100;//影响拆分速度,反比例}setSplitModel(model){if(!model || model instanceof Mesh) {console.warn("只能处理Scene、Object3D、Group")return;}this.quit();this.meshList = [];//计算模型整体包围盒中心作为爆炸中心model.updateMatrixWorld();var box = new Box3().expandByObject(model); var center = box.getCenter(new Vector3());model.traverse(node=>{if(node.type == "Mesh"){//分别计算每个mesh的包围盒中心,其与爆炸中心连线作为爆炸方向let subBox = new Box3().expandByObject(node);let meshCenter = subBox.getCenter(new Vector3());node._splitSrcPos = node.getWorldPosition(new Vector3())//这里计算各个轴向分速度,这样可以使用滑动条控制进度let subSpeed = {x:(meshCenter.x-center.x) * this.splitScale / this.splitSpeed,y:(meshCenter.y-center.y) * this.splitScale / this.splitSpeed,z:(meshCenter.z-center.z) * this.splitScale / this.splitSpeed,}node._splitSpeed = subSpeed;this.meshList.push(node);}            })this.currentSplitValue = 0;this.targetSplitValue = 0;this.running = false;}/*** 开始爆炸*/startSplit(){this.targetSplitValue = this.splitSpeed;this.currentSplitValue = 0;this.offset = 1;this.running = true;this.isQuit = false;}/*** 开始反向爆炸(还原)*/quitSplit(){this.targetSplitValue = 0;this.currentSplitValue = this.splitSpeed;this.offset = -1;this.running = true;this.isQuit = true;}/*** 退出拆分时还原*/quit(){if(this.currentSplitValue != 0 && this.meshList.length > 0){for(var i=0;i<this.meshList.length;i++){let node = this.meshList[i];node.position.copy(node._splitSrcPos);}this.currentSplitValue = 0;this.targetSplitValue = 0;}}/*** 如果用滑动条控制时将滑动条的值传入这个函数* @param {*} value [0,1]的值,表示爆炸进度*/setValue(value){if(value < 0) value = 0;if(value > 1) value = 1;this.currentSplitValue = value * this.splitSpeed;for(var i=0;i<this.meshList.length;i++){let node = this.meshList[i];let x = node._splitSpeed.x * this.currentSplitValue + node._splitSrcPos.x;let y = node._splitSpeed.y * this.currentSplitValue + node._splitSrcPos.y;let z = node._splitSpeed.z * this.currentSplitValue + node._splitSrcPos.z;node.parent.updateMatrixWorld();let invMat = node.parent.matrixWorld.clone().invert();//不同版本的threejs用不同的方法获取逆矩阵,如果用上面的报错就换下面的//let invMat = new Matrix4().getInverse(node.parent.matrixWorld.clone());let pos = new Vector3(x,y,z).applyMatrix4(invMat);node.position.copy(pos);}}/*** 更新* @returns */update(){if(this.running && this.meshList.length > 0){if (this.currentSplitValue != this.targetSplitValue) {this.currentSplitValue += this.offset;}for(var i=0;i<this.meshList.length;i++){let node = this.meshList[i];let x = node._splitSpeed.x * this.currentSplitValue + node._splitSrcPos.x;let y = node._splitSpeed.y * this.currentSplitValue + node._splitSrcPos.y;let z = node._splitSpeed.z * this.currentSplitValue + node._splitSrcPos.z;node.parent.updateMatrixWorld();let invMat = node.parent.matrixWorld.clone().invert();//不同版本的threejs用不同的方法获取逆矩阵,如果用上面的报错就换下面的//let invMat = new Matrix4().getInverse(node.parent.matrixWorld.clone());let pos = new Vector3(x,y,z).applyMatrix4(invMat);node.position.copy(pos);}if(this.currentSplitValue == this.targetSplitValue){this.running = false;if(this.isQuit == true){this.quit();this.isQuit = false;}}}}
}

三、其他效果


使用按键模型滑动条

其他模型效果

四、然而但是

目前来看拆分效果不错,直到遇到某些模型,比如下面的

简单分析下,这种情况就是子mesh拆分方向比较接近,导致都往一个方向跑。
为了解决这个问题,那是不是可以把爆炸中心放在所有子mesh的包围盒中心呢,试试效果

看起来方向是没问题了,不过有些小模型跑很远,有些大模型还重叠了,这是拆分距离问题。这里就把距离固定为模型整体包围盒大小好了,修改后效果如下


看起来好多了。修改后的代码如下

import{Vector3,Box3,Mesh,LineBasicMaterial,Geometry,Line
} from 'three'/*** 使用方法:* 1、调用setSplitModel函数将要拆分的模型传入预处理* 然后两种控制爆炸方式* (1)实时更新*      调用startSplit()/quitSplit()函数开始爆炸/恢复*      需要在three的animate函数中调用update函数* (2)滑动条*      调用setValue函数把滑动条的值传入。*/
export class ModelSplit{constructor(){this.meshList = [];this.running = false;this.targetSplitValue = 0;this.currentSplitValue = 0;this.offset = 1;this.splitScale = 5; //影响拆分距离,就是mesh的包围盒中心与爆炸中心的距离的倍率this.splitSpeed = 100;//影响拆分速度,反比例this.mode = 2;if(this.mode == 2){this.splitScale = 0.3; //影响拆分距离,就是mesh的包围盒中心与爆炸中心的距离的倍率this.splitSpeed = 50;//影响拆分速度,反比例}}setSplitModel(model){if(!model || model instanceof Mesh) {console.warn("只能处理Scene、Object3D、Group")return;}this.quit();this.meshList = [];//计算模型整体包围盒中心作为爆炸中心model.updateMatrixWorld();var box = new Box3().expandByObject(model); var maxLength = box.max.clone().distanceTo(box.min);var center;if(this.mode == 1){center = box.getCenter(new Vector3());}else{  //爆炸中心由子mesh的包围盒中心决定center = new Vector3();var subBox,subCenter,count = 0;;model.traverse(node=>{if(node.type == "Mesh"){//分别计算每个mesh的包围盒中心,其与爆炸中心连线作为爆炸方向subBox = new Box3().expandByObject(node);subCenter = subBox.getCenter(new Vector3());center = center.clone().add(subCenter);count++;}            })center = center.clone().multiplyScalar(1/count);}model.traverse(node=>{if(node.type == "Mesh"){//分别计算每个mesh的包围盒中心,其与爆炸中心连线作为爆炸方向let subBox = new Box3().expandByObject(node);let meshCenter = subBox.getCenter(new Vector3());node._splitSrcPos = node.getWorldPosition(new Vector3())let subSpeed;if(this.mode == 1){subSpeed = {x:(meshCenter.x-center.x) * this.splitScale / this.splitSpeed,y:(meshCenter.y-center.y) * this.splitScale / this.splitSpeed,z:(meshCenter.z-center.z) * this.splitScale / this.splitSpeed,}}else{let targetPos = meshCenter.clone().add(meshCenter.clone().sub(center).normalize().multiplyScalar(maxLength));//这里计算各个轴向分速度,这样可以使用滑动条控制进度subSpeed = {x:(targetPos.x - center.x) * this.splitScale / this.splitSpeed,y:(targetPos.y - center.y) * this.splitScale / this.splitSpeed,z:(targetPos.z - center.z) * this.splitScale / this.splitSpeed,// x:(meshCenter.x-center.x) * this.splitScale / this.splitSpeed,// y:(meshCenter.y-center.y) * this.splitScale / this.splitSpeed,// z:(meshCenter.z-center.z) * this.splitScale / this.splitSpeed,}}node._splitSpeed = subSpeed;this.meshList.push(node);}            })this.currentSplitValue = 0;this.targetSplitValue = 0;this.running = false;}/*** 开始爆炸*/startSplit(){this.targetSplitValue = this.splitSpeed;this.currentSplitValue = 0;this.offset = 1;this.running = true;this.isQuit = false;}/*** 开始反向爆炸(还原)*/quitSplit(){this.targetSplitValue = 0;this.currentSplitValue = this.splitSpeed;this.offset = -1;this.running = true;this.isQuit = true;}/*** 退出拆分时还原*/quit(){if(this.currentSplitValue != 0 && this.meshList.length > 0){for(var i=0;i<this.meshList.length;i++){let node = this.meshList[i];node.position.copy(node._splitSrcPos);}this.currentSplitValue = 0;this.targetSplitValue = 0;}}/*** 如果用滑动条控制时将滑动条的值传入这个函数* @param {*} value [0,1]的值,表示爆炸进度*/setValue(value){if(value < 0) value = 0;if(value > 1) value = 1;this.currentSplitValue = value * this.splitSpeed;for(var i=0;i<this.meshList.length;i++){let node = this.meshList[i];let x = node._splitSpeed.x * this.currentSplitValue + node._splitSrcPos.x;let y = node._splitSpeed.y * this.currentSplitValue + node._splitSrcPos.y;let z = node._splitSpeed.z * this.currentSplitValue + node._splitSrcPos.z;node.parent.updateMatrixWorld();let invMat = node.parent.matrixWorld.clone().invert();let pos = new Vector3(x,y,z).applyMatrix4(invMat);node.position.copy(pos);}}/*** 更新* @returns */update(){if(this.running && this.meshList.length > 0){if (this.currentSplitValue != this.targetSplitValue) {this.currentSplitValue += this.offset;}for(var i=0;i<this.meshList.length;i++){let node = this.meshList[i];let x = node._splitSpeed.x * this.currentSplitValue + node._splitSrcPos.x;let y = node._splitSpeed.y * this.currentSplitValue + node._splitSrcPos.y;let z = node._splitSpeed.z * this.currentSplitValue + node._splitSrcPos.z;node.parent.updateMatrixWorld();let invMat = node.parent.matrixWorld.clone().invert();let pos = new Vector3(x,y,z).applyMatrix4(invMat);node.position.copy(pos);}if(this.currentSplitValue == this.targetSplitValue){this.running = false;if(this.isQuit == true){this.quit();this.isQuit = false;}}}}
}

为了保留原始效果,在构造函数中添加了一个变量this.mode,将值赋为1时是原先的效果,赋为2时是新的效果

Threejs3D模型爆炸效果相关推荐

  1. threejs对obj模型实现爆炸效果

    效果 说明:实现模型加载完成后,对其进行爆炸分解,然后延迟还原. 思路: 1.获取到obj模型后,初始化爆炸数据保存到每个mesh的userdata上: 代码: // 初始化爆炸数据保存到每个mesh ...

  2. Unity 2.Space Shooter(碰撞器Collider,WebGL,刚体中属性,(定时)实例化、销毁游戏对象,触碰OnTriggerEnter/Exit,爆炸效果,音频,文字,定时调方法)

    目录 项目介绍 WebGL发布 游戏对象设置 灯光.相机 背景 移动游戏对象 Debug 制作子弹 射击动作 清理离开边界的游戏对象 制作危险物 添加爆炸,移动小行星,作为预制件 创建游戏控制器 循环 ...

  3. jQuery 打气球小游戏 点击气球爆炸效果

    最近在学习前端,看到偶尔看到前端小游戏,就想自己写一个小游戏,奈何水平有限,只能写打气球这种简单的,所有的气球都是动态生成的,气球的颜色也是随机的 html部分 <div class=" ...

  4. php js特效代码如何用,phpstorm编写代码增加代码爆炸效果

    Awesome Power Mode 下载地址:https://github.com/codeinthedark/awesome-power-mode A curated list of power ...

  5. 斩获VCR竞赛榜第一,腾讯微视推出BLENDer单模型,超越多模型最好效果

    出品 | CSDN(ID:CSDNnews) 视觉常识推理VCR (Visual Commonsense Reasoning )是人工智能领域的前沿热点问题,我国<新一代人工智能发展规划> ...

  6. J2ME 2D小游戏入门之旅(五) 实现爆炸效果、并加入道具导弹(转)

    五.实现爆炸效果.并加入道具导弹 大多数游戏都有着丰富的效果类,在精灵移动类游戏中曾一度以此为一个重要的卖点,.光光是一些丰富的特效是不能够产生一个好的游戏的,但是一个好的游戏是万万不能缺少好的效果的 ...

  7. Cocos2d-x 3.2 lua飞机大战开发实例(三)道具的掉落,碰撞检测,声音,分数,爆炸效果,完善游戏的功能细节

           Cocos2d-X 3.2  lua语言飞机大战开发实例(三) 7.添加声音,更新分数,添加爆炸效果,道具的掉落.道具的碰撞检测等完善游戏功能 爆炸的效果添加 首先需要在GameData ...

  8. CSS3配合JavaScript图片爆炸效果

    CSS3配合JavaScript实现图片爆炸效果 先看看效果图: 代码如下: boom.html <!DOCTYPE html> <html lang="en"& ...

  9. 小学生python游戏编程arcade----动画图片实现爆炸效果

    小学生python游戏编程arcade----动画图片实现爆炸效果 前言 动画图片实现爆炸效果 1.爆炸类的的实现 1.1爆炸图片 1.2 类的定义 1.3 爆炸类的引用 1.4 爆炸类的更新 1.5 ...

  10. 使用Ezy-Slice插件实现类似Beat Saber的模型切割效果(一)

    简单地手动实现模型切割效果 为了更清楚的了解插件是如何使用的,我们先看看如何手动实现这样的切割效果. 1.在Github上下载插件ezy-slice. 地址:https://github.com/Da ...

最新文章

  1. MySQL自学2018/05/02-数据类型
  2. 深度学习框架YOLOv3的C++调用
  3. sim插拔识别时间_特斯拉+树莓派实现车牌识别检测系统
  4. Java面试准备:15个Java面试问题
  5. win7下node.js设置npm环境变量
  6. python2.7安装sqlite3模块
  7. 2021年,彩票店还开的下去吗?
  8. cifs mount fail
  9. 如何下载Mysql安装包?
  10. win10basic模式_BASIC的完整形式是什么?
  11. Allegro设置尺寸单位milmm
  12. 一个IP到底值多少钱
  13. 如何 DIY 一台属于你自己的电脑?
  14. JavaScript数组方法总结
  15. 一文读懂生成对抗网络(GANs)【下载PDF | 长文】
  16. 匈牙利为庆贺第17届奥运会而发行的纪念邮票
  17. linux命令和选项作用,linux tar 命令中 -f选项作用
  18. echarts 好看的柱形图
  19. SpringBoot整合tkMapper
  20. 有什么软件可以复制并粘贴文件?

热门文章

  1. 当下OA系统的使用缺陷以及相关解决方案
  2. CCS编译错误:error #10099-D和error#10234-D unresolved symbols remain解决方法
  3. ARM9开发板初体验----使用Uboot通过USB下载线烧写bin文件
  4. Affinity Mattrix 亲和矩阵总结
  5. shift键计算机功能,电脑shift键常用快捷键使用攻略
  6. php excel导入读取公式本身不计算,使用PHPExcel读取Excel文件时忽略计算值
  7. 中文名颜色大全,妈妈再也不担心我找不着好颜色了.
  8. 编程示例:公农历转换的算法
  9. micropython(3):使用thonny ide 开发,并控制 LED 设备
  10. 在线html调试,debugger调试