基于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 相关文章推荐
JQuery Tips(2) 关于$()包装集你不知道的
Dec 14 Javascript
精通Javascript系列之数值计算
Jun 07 Javascript
jquery cookie的用法总结
Nov 18 Javascript
jquery.ajax的url中传递中文乱码问题的解决方法
Feb 07 Javascript
jquery实现搜索框常见效果的方法
Jan 22 Javascript
全面解析多种Bootstrap图片轮播效果
May 27 Javascript
浅谈js原生拖放
Nov 21 Javascript
纯js模仿windows系统日历
Feb 04 Javascript
深入浅析ES6 Class 中的 super 关键字
Oct 20 Javascript
微信小程序实现红包功能(后端PHP实现逻辑)
Jul 11 Javascript
vue实现抖音时间转盘
Sep 08 Javascript
原生JavaScript实现购物车
Jan 10 Javascript
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笔试题
2009/08/04 PHP
c#中的实现php中的preg_replace
2009/12/21 PHP
仿AS3实现PHP 事件机制实现代码
2011/01/27 PHP
PHP中break及continue两个流程控制指令区别分析
2011/04/18 PHP
浅析php与数据库代码开发规范
2013/08/08 PHP
ThinkPHP的I方法使用详解
2014/06/18 PHP
PHP实现的简单适配器模式示例
2017/06/22 PHP
php表单处理操作
2017/11/16 PHP
PHP如何通过date() 函数格式化显示时间
2020/11/13 PHP
javascript function、指针及内置对象
2009/02/19 Javascript
基于mootools 1.3框架下的图片滑动效果代码
2011/04/22 Javascript
jquery实现的让超出显示范围外的导航自动固定屏幕最顶上
2011/09/22 Javascript
jquery中ajax学习笔记一
2011/10/16 Javascript
疯狂Jquery第一天(Jquery学习笔记)
2012/05/11 Javascript
JS 获取select(多选下拉)中所选值的示例代码
2013/08/02 Javascript
javascript 实现 原路返回
2015/01/21 Javascript
JS解析XML实例分析
2015/01/30 Javascript
适用于手机端的jQuery图片滑块动画
2016/12/09 Javascript
BootStrap栅格系统、表单样式与按钮样式源码解析
2017/01/20 Javascript
浅谈javascript中的 “ &amp;&amp; ” 和 “ || ”
2017/02/02 Javascript
利用jQuery实现简单的拖曳效果实例代码
2017/10/20 jQuery
解决node修改后需频繁手动重启的问题
2018/05/13 Javascript
详解JavaScript添加给定的标签选项
2018/09/17 Javascript
微信小程序获取用户信息及手机号(后端TP5.0)
2019/09/12 Javascript
Antd下拉选择,自动匹配功能的实现
2020/10/24 Javascript
布同 Python中文问题解决方法(总结了多位前人经验,初学者必看)
2011/03/13 Python
对Python中DataFrame选择某列值为XX的行实例详解
2019/01/29 Python
TensorFlow学习之分布式的TensorFlow运行环境
2020/02/05 Python
django template实现定义临时变量,自定义赋值、自增实例
2020/07/12 Python
纯CSS绘制漂亮的圆形图案效果
2014/05/07 HTML / CSS
纯CSS3制作的鼠标悬停时边框旋转
2017/01/03 HTML / CSS
销售助理岗位职责
2014/02/21 职场文书
2014年秋季开学演讲稿
2014/05/24 职场文书
2014年基层党支部工作总结
2014/12/04 职场文书
毕业实习感受与体会
2015/05/26 职场文书
Vue2.0搭建脚手架
2022/03/13 Vue.js