基于three.js实现的3D粒子动效实例代码


Posted in Javascript onApril 09, 2019

一、背景

粒子特效是为模拟现实中的水、火、雾、气等效果由各种三维软件开发的制作模块,原理是将无数的单个粒子组合使其呈现出固定形态,借由控制器、脚本来控制其整体或单个的运动,模拟出现真实的效果。three.js是用JavaScript编写的WebGL的第三方库,three.js提供了丰富的API帮助我们去实现3D动效,本文主要介绍如何使用three.js实现粒子过渡效果,以及基本的鼠标交互操作。

(注:本文使用的关于three.js的API都是基于版本r98的。)

基于three.js实现的3D粒子动效实例代码

二、实现步骤

1. 创建渲染场景scene

scene实际上相当于一个三维空间,用于承载和显示我们所定义的一切,包括相机、物体、灯光等。在实际开发时为了方便观察可添加一些辅助工具,比如网格、坐标轴等。

scene = new THREE.Scene();
 scene.fog = new THREE.Fog(0x05050c, 10, 60);
 scene.add( new THREE.GridHelper( 2000, 1 ) ); // 添加网格

2. 添加照相机camera

THREE里面实现了几种相机:PerspectiveCamera(透视相机)、 OrthographicCamera(正交投影相机)、CubeCamera(立方体相机或全景相机)和 StereoCamera(3D相机)。本文介绍我们主要用到的 PerspectiveCamera(透视相机):

视觉效果是近大远小。

配置参数 PerspectiveCamera(fov, aspect, near, far)。

fov:相机的可视角度。

aspect:相机可视范围的长宽比。

near:相对于深度剪切面的远的距离。

far:相对于深度剪切面的远的距离。

camera = new THREE.PerspectiveCamera(45, window.innerWidth /window.innerHeight, 5, 100);
 camera.position.set(10, -10, -40);
 scene.add(camera);

3. 添加场景渲染需要的灯光

three.js里面实现的光源:AmbientLight(环境光)、DirectionalLight(平行光)、HemisphereLight(半球光)、PointLight(点光源)、RectAreaLight(平面光源)、SpotLight(聚光灯)等。配置光源参数时需要注意颜色的叠加效果,如环境光的颜色会直接作用于物体的当前颜色。各种光源的配置参数有些区别,下面是本文案例中会用到的二种光源。

let ambientLight = new THREE.AmbientLight(0x000000, 0.4);
 scene.add(ambientLight);
 let pointLight = new THREE.PointLight(0xe42107);
 pointLight.castShadow = true;
 pointLight.position.set(-10, -5, -10);
 pointLight.distance = 20;
 scene.add(pointLight);

4. 创建、导出并加载模型文件loader

创建模型,可以使用three.js editor进行创建或者用three.js的基础模型生成类进行生成,相对复杂的或者比较特殊的模型需要使用建模工具进行创建(c4d、3dmax等)。

使用three.js editor进行创建,可添加基本几何体,调整几何体的各种参数(位置、颜色、材质等)。

基于three.js实现的3D粒子动效实例代码

使用模型类生成。

let geometryCube = new THREE.BoxBufferGeometry( 1, 1, 1 );
 let materialCube = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
 let cubeMesh = new THREE.Mesh( geometryCube, materialCube );
 scene.add( cubeMesh );

导出需要的模型文件(此处使用的是 obj格式的模型文件)。

加载并解析模型文件数据。

let onProgress = function (xhr) {
 if (xhr.lengthComputable) {
  // 可进行计算得知模型加载进度
 }
 };
 let onError = function () {};
 particleSystem = new THREE.Group();
 var texture = new THREE.TextureLoader().load('./point.png');
 new THREE.OBJLoader().load('./model.obj', function (object) {
 // object 模型文件数据
 }, onProgress, onError);

5. 将导入到模型文件转换成粒子系统Points

获取模型的坐标值。

拷贝粒子坐标值到新建属性position1上 ,这个作为粒子过渡效果的最终坐标位置。

给粒子系统添加随机三维坐标值position,目的是把每个粒子位置打乱,设定起始位置。

let color = new THREE.Color('#ffffff');
 let material = new THREE.PointsMaterial({
 size: 0.2,
 map: texture,
 depthTest: false,
 transparent: true
 });
 particleSystem= new THREE.Group();
 let allCount = 0
 for (let i = 0; i < object.children.length; i++) {
 let name = object.children[i].name
 let _attributes = object.children[i].geometry.attributes
  let count = _attributes.position.count
  _attributes.positionEnd = _attributes.position.clone()
  _attributes.position1 = _attributes.position.clone()
  for (let i = 0; i < count * 3; i++) {
  _attributes.position1.array[i]= Math.random() * 100 - 50
  }
  let particles = new THREE.Points(object.children[i].geometry, material)
  particleSystem.add(particles)
  allCount += count
 }
 particleSystem.applyMatrix(new THREE.Matrix4().makeTranslation(-5, -5,-10));

6. 通过tween动画库实现粒子坐标从position到position1点转换

利用 TWEEN 的缓动算法计算出各个粒子每一次变化的坐标位置,从初始位置到结束位置时间设置为2s(可自定义),每次执行计算之后都需要将attributes的position属性设置为true,用来提醒场景需要更新,在下次渲染时,render会使用最新计算的值进行渲染。

let pos = {
 val: 1
 };
 tween = new TWEEN.Tween(pos).to({
 val: 0
 }, 2500).easing(TWEEN.Easing.Quadratic.InOut).onUpdate(callback);
 tween.onComplete(function () {
 console.log('过渡完成complete')
 })
 tween.start();
 function callback() {
 let val = this.val;
 let particles = particleSystem.children;
 for (let i = 0; i < particles.length; i++) {
  let _attributes = particles[i].geometry.attributes
  let name = particles[i].name
  if (name.indexOf('_') === -1) {
  let positionEnd =_attributes.positionEnd.array
  let position1 =_attributes.position1.array
  let count =_attributes.position.count
  for (let j = 0; j < count *3; j++) {
   _attributes.position.array[j] = position1[j] *val + positionEnd[j] * (1 - val)
  }
  }
  _attributes.position.needsUpdate = true // 设置更新
 }
 }

7. 添加渲染场景render

创建容器。

定义render渲染器,设置各个参数。

将渲染器添加到容器里。

自定义的渲染函数 render,在渲染函数里面我们利用 TWEEN.update 去更新模型的状态。

调用自定义的循环动画执行函数 animate,利用requestAnimationFrame方法进行逐帧渲染。

let container = document.createElement('div');
 document.body.appendChild(container);
 renderer = new THREE.WebGLRenderer({
 antialias: true,
 alpha: true
 });
 renderer.setPixelRatio(window.devicePixelRatio);
 renderer.setClearColor(scene.fog.color);
 renderer.setClearAlpha(0.8);
 renderer.setSize(window.innerWidth, window.innerHeight);
 container.appendChild(renderer.domElement); // 添加webgl渲染器
 
 function render() {
 particleSystem.rotation.y += 0.0001;
 TWEEN.update();
 particleSystem.rotation.y += (mouseX + camera.rotation.x) * .00001;
 camera.lookAt(new THREE.Vector3(-10, -5, -10))
 controls.update();
 renderer.render(scene, camera);
 }
 function animate() { // 开始循环执行渲染动画
 requestAnimationFrame(animate);
 render();
 }

8. 添加鼠标操作事件实现角度控制

我们还可以添加鼠标操作事件实现角度控制,其中winX、winY分别为window的宽高的一半,当然具体的坐标位置可以根据自己的需求进行计算,具体的效果如下图所示。

document.addEventListener('mousemove', onDocumentMouseMove, false);
 function onDocumentMouseMove(event) {
 mouseX = (event.clientX - winX) / 2;
 mouseY = (event.clientY - winY) / 2;
 }

基于three.js实现的3D粒子动效实例代码

三、优化方案

1. 减少粒子数量

随着粒子数量的增加,需要的计算每个粒子的位置和大小将会非常耗时,可能会造成动画卡顿或出现页面假死的情况,所以我们在建立模型时可尽量减少粒子的数量,能够有效提升性能。

在以上示例中,我们改变导出模型的精细程度,可以得到不同数量的粒子系统,当粒子数量达到几十万甚至几百万的时候,在动画加载时可以感受到明显的卡顿现象,这主要是由于fps比较低,具体的对比效果如下图所示,左边粒子数量为30万,右边粒子数量为6万,可以明显看出左边跳帧明显,右边基本保持比较流畅的状态。

基于three.js实现的3D粒子动效实例代码

2. 采用GPU渲染方式

编写片元着色器代码,利用webgl可以为canvas提供硬件3D加速,浏览器可以更流畅地渲染页面。目前大多数设备都已经支持该方式,需要注意的是在低端的设备上由于硬件设备原因,渲染的速度可能不及基于cpu计算的方式渲染。

四、总结

综上所述,实现粒子动效的关键在于计算、维护每个粒子的位置状态,而three.js提供了较为便利的方法,可以用于渲染整个粒子场景。当粒子数量极为庞大时,想要实现较为流畅的动画效果需要注意优化代码、减少计算等,也可以通过提升硬件配置来达到效果。本文中的案例为大家展示了3D粒子动效如何实现,大家可以根据自己的实际需求去制作更炫酷的动态效果。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
IE FF OPERA都可用的弹出层实现代码
Sep 29 Javascript
用js来解决ajax读取页面乱码
Nov 28 Javascript
调试Node.JS的辅助工具(NodeWatcher)
Jan 04 Javascript
jquery实现图片左右间隔滚动特效(可自动播放)
May 08 Javascript
使用jquery局部刷新(jquery.load)从数据库取出数据
Jan 22 Javascript
jQuery中:first选择器用法实例
Dec 30 Javascript
javascript实现仿腾讯游戏选择
May 14 Javascript
JS自定义选项卡函数及用法实例分析
Sep 02 Javascript
基于JS实现数字+字母+中文的混合排序方法
Jun 06 Javascript
Vue.js实现简单ToDoList 前期准备(一)
Dec 01 Javascript
JavaScript Dom实现轮播图原理和实例
Feb 19 Javascript
Vue CLI中模式与环境变量的深入详解
May 30 Vue.js
Koa 中的错误处理解析
Apr 09 #Javascript
简单说说如何使用vue-router插件的方法
Apr 08 #Javascript
利用Bootstrap Multiselect实现下拉框多选功能
Apr 08 #Javascript
纯javascript实现选择框的全选与反选功能
Apr 08 #Javascript
详解小程序如何避免多次点击,重复触发事件
Apr 08 #Javascript
「中高级前端面试」JavaScript手写代码无敌秘籍(推荐)
Apr 08 #Javascript
微信小程序BindTap快速连续点击目标页面跳转多次问题处理
Apr 08 #Javascript
You might like
PHP基于回溯算法解决n皇后问题的方法示例
2017/11/07 PHP
PHP的PDO预处理语句与存储过程
2019/01/27 PHP
laravel 解决后端无法获取到前端Post过来的值问题
2019/10/22 PHP
jquery 选择器引擎sizzle浅析
2013/02/06 Javascript
Javascript中Array.prototype.map()详解
2014/10/22 Javascript
JS实现简洁、全兼容的拖动层实例
2015/05/13 Javascript
js实现温度计时间样式代码分享
2015/08/21 Javascript
JavaScript的Backbone.js框架入门学习指引
2016/05/07 Javascript
Javascript之Number对象介绍
2016/06/07 Javascript
JavaScript必知必会(五) eval 的使用
2016/06/08 Javascript
Bootstrap modal使用及点击外部不消失的解决方法
2016/12/13 Javascript
一步步教你利用webpack如何搭一个vue脚手架(超详细讲解和注释)
2018/01/08 Javascript
新手必须知的Node.js 4个JavaScript基本概念
2018/09/16 Javascript
angular使用md5,CryptoJS des加密的方法
2019/06/03 Javascript
Swiper.js实现移动端元素左右滑动
2019/09/08 Javascript
Layui带搜索的下拉框的使用以及动态数据绑定方法
2019/09/28 Javascript
JS实现网站吸顶条
2020/01/08 Javascript
[48:00]EG vs LGD 2018国际邀请赛淘汰赛BO3 第二场 8.26
2018/08/29 DOTA
python获得图片base64编码示例
2014/01/16 Python
python中常用检测字符串相关函数汇总
2015/04/15 Python
Python实现合并字典的方法
2015/07/07 Python
使用Python AIML搭建聊天机器人的方法示例
2018/07/09 Python
python3.4控制用户输入与输出的方法
2018/10/17 Python
wxPython实现列表增删改查功能
2019/11/19 Python
如何利用Python识别图片中的文字
2020/05/31 Python
Python装饰器结合递归原理解析
2020/07/02 Python
html5绘制时钟动画
2014/12/15 HTML / CSS
英国HYPE双肩包官网:英国本土时尚潮牌
2018/09/26 全球购物
eBay比利时购物网站:eBay.be
2019/08/09 全球购物
列车长先进事迹材料
2014/01/25 职场文书
财务支持类个人的自我评价
2014/02/14 职场文书
蓝颜请假条
2014/04/11 职场文书
团队精神口号
2014/06/06 职场文书
2015年六一儿童节活动总结
2015/02/11 职场文书
小学数学新课改心得体会
2016/01/22 职场文书
浅谈MySQL 亿级数据分页的优化
2021/06/15 MySQL