用three.js搞3个炫酷粒子出场

简介: 出场就要帅气!动画要酷!点进来就看粒子玩出新花样!让你拥有炫酷的粒子出场方式,一闪一闪亮晶晶好像许多的星星!!!

出场就要帅气!

1.画出模型的点

js

  this.loadModel('apple.glb').then((model) => {            let obj = model.children[0].children[0];let geometry = obj.geometry;  //放大两倍            geometry.scale(2, 2, 2);            //形状点整体居中            geometry.center();      //渲染方式为画点      let material = newTHREE.PointsMaterial({              size: 1,//点大小              color: newTHREE.Color(that.color), //颜色              transparent: true, //开启透明                });            this.mesh = newTHREE.Points(geometry, material);  this.scene.add(this.mesh);  //设置视角this.setView(that.cameraPos, that.controlsPos); })

  • 为了操作点方便,所以这里使用的是BufferGeometry,以上是苹果模型的BufferGeometry,如果是一些像球体等封装好的图元,可以采用以下方式转换成对应的缓存图元

js

//转为缓存图元            geometry = newTHREE.BufferGeometry().fromGeometry(geometry);

2.画圆点

  • 可以看到原始的点是正方形的,为了好看,我们需要将点变成圆形,那么我们需要在编译前修改片元着色器

js

  material.onBeforeCompile = (shader) => {   console.log(shader)  })
  • 打印shader.fragmentShader可以看到编译前的代码,有点多和复杂,不用读懂,我们只需要替换其中要用的一句代码,变成我们需要的结果。

js

  material.onBeforeCompile = (shader) => {              //修改片元着色器shader.fragmentShader = shader.fragmentShader.replace(`gl_FragColor = vec4( outgoingLight, diffuseColor.a );`,                `  //需要替换的代码……`              );
  • 以下是画圆点的着色器,实现逻辑:计算每个片元离中心点的距离,远离中心点的片元没有过颜色

c++

//计算离中心点距离float d=distance(gl_PointCoord, vec2(0.5, 0.5)); //离中心点0.5以外没有颜色  if(d>0.5) discard;  //复用替换前的代码 gl_FragColor = vec4(outgoingLight , diffuseColor.a );

3.圆点变光点

  • 一个个圆点貌似有点丑,巴啦啦小魔仙仙,呜呼啦呼,变!

c++

float d=distance(gl_PointCoord, vec2(0.5, 0.5));if(d < 0.3){//保持原色  gl_FragColor = diffuseColor;}else{//透明渐变  gl_FragColor.rgb = diffuseColor.rgb;float cd =(1.0-d*2.0);gl_FragColor.a=diffuseColor.a*cd*0.5;}
  • 离中心点0.3以内的保持原来颜色
  • 其他部分根据离中心点距离透明渐变

  • 但是你会发现当点出现叠加时,有个黑框正方形,那是因为深度冲突的问题,这时候就需要关闭材质的深度测试

js

  let material = newTHREE.PointsMaterial({              size: 1,              color: newTHREE.Color(that.color),               transparent: true,              depthTest: false//关闭深度测试            });

  • 可以看到黑框不见了,发光点正常了!

4.出场方式1:让点从底部逐渐上升

获取顶点位置,然后复制存一份作为原始值,另一份设置成全部点落在底

geometry.boundingBox:形状的包围框,可以获取底部的位置和顶部的位置

js

const positions = geometry.attributes.position;            const pos = positions.clone();            //底部位置            const bottom = geometry.boundingBox.min.y;            this.distance = bottom;            //顶部位置            this.max = geometry.boundingBox.max.y;            const count = pos.count;            for (let i = 0; i < count; i++) {            //将y轴坐标全部置底              pos.setXYZ(i, pos.getX(i), bottom, pos.getZ(i));            }            pos.needsUpdate = true;            geometry.setAttribute('position', pos);            geometry.setAttribute('initialPosition', positions.clone());            geometry.attributes.position.setUsage(THREE.DynamicDrawUsage);

  • 可以看到3D形状像是变成扁平二维了

动起来,让世界变得精彩

  • this.speed:运动速度
  • this.speed1:运动加速度
  • this.distance:上升的距离

动画逻辑:

  1. 当初始点小于上升距离时,则点回到原来的位置
  2. 当点大于上升距离时,点维持上升距离的位置
  3. 上升距离随着时间增大,这样就可以呈现沿着一个平面,点逐步复原的出场效果!
  4. 当上升距离大于最高点则结束动画,出场完成!

js

animateAction() {          if (this.mesh && this.distance <= this.max) {                      this.speed += this.speed1;            this.distance += this.speed;            let dist = this.distance;            const positions = this.mesh.geometry.attributes.position;            const initialPositions = this.mesh.geometry.attributes.initialPosition;            const count = positions.count;            let t = this.max - this.distance;            for (let i = 0; i < count; i++) {              const iy = initialPositions.getY(i);              positions.setXYZ(i, positions.getX(i), iy <= dist ? iy : dist, positions.getZ(i));            }            //通知材质的着色器,点要更新            positions.needsUpdate = true;          }        }

注意:赋值改变点位置后,一定要positions.needsUpdate = true;通知点位置属性要更新

5.出场方式2:让凌乱的点汇聚

  • 逻辑跟第一种出场方式类似,不过这里需要存储一份随机偏移值,用来生成凌乱的点

js

const positions = geometry.attributes.position;            geometry.setAttribute('initialPosition', positions.clone());            const pos = positions.clone();            const count = pos.count;            const displacement = newFloat32Array(count);            for (let i = 0; i < count; i++) {            //随机偏移值              displacement[i] = that.minDistance + that.distance * Math.random();                          }            pos.needsUpdate = true;            geometry.setAttribute('position', pos);//偏移值赋值   geometry.setAttribute('displacement', newTHREE.BufferAttribute(displacement, 1));            geometry.attributes.position.setUsage(THREE.DynamicDrawUsage);

动画逻辑:

  1. 初始点根据时间变换,逐渐减少偏移,最终回调原始的点,形成3D形状
  2. 当偏移距离减少至零,全部点恢复位置,动画结束,出场完成!
  • this.time时间增长值

注意:偏移距离要乘以法向量,这样才能让点四面八方地分布

js

  animateAction() {          if (this.mesh && this.time >= 0) {            this.speed += this.speed1;            this.time += this.speed;            const positions = this.mesh.geometry.attributes.position;            const normal = this.mesh.geometry.attributes.normal;            const initialPositions = this.mesh.geometry.attributes.initialPosition;            const displacement = this.mesh.geometry.attributes.displacement;            const count = positions.count;            let t = 2.0 - this.time;            for (let i = 0; i < count; i++) {            //计算该时间的偏移距离              const d = displacement.getX(i) * t;              const ix = initialPositions.getX(i);              const iy = initialPositions.getY(i);              const iz = initialPositions.getZ(i);              const nx = normal.getX(i);              const ny = normal.getY(i);              const nz = normal.getZ(i);              //初始点减去偏移距离              positions.setXYZ(i, ix - nx * d, iy - ny * d, iz - nz * d);            }            positions.needsUpdate = true;            if (this.time >= 2) {//结束动画              this.time = -1;            }          }        }

6.出场方式3:中心爆炸点

  • 这个与上面的出场方式类似,不过是全部点从一个点出发,然后回到原来的位置,于是就要设置初始点为同一个位置

js

const positions = geometry.attributes.position;const b = geometry.boundingBox;            this.max = Math.max(              Math.abs(b.min.x),              Math.abs(b.min.y),              Math.abs(b.min.z),              Math.abs(b.max.x),              Math.abs(b.max.y),              Math.abs(b.max.z)            );            geometry.setAttribute('initialPosition', positions.clone());            const pos = positions.clone();            const count = pos.count;            for (let i = 0; i < count; i++) {            //全部点设置为原点              pos.setXYZ(i, 0, 0, 0);            }            pos.needsUpdate = true;是是            geometry.setAttribute('position', pos);            geometry.attributes.position.setUsage(THREE.DynamicDrawUsage);

动画逻辑:

  1. 半径随着时间增大,当点原始位置与原点的距离小于半径则回归原位,否则跟着扩张半径运动,直至全部点回到原始位置,形成最终的3D形状!
  2. 半径扩展与时间是比例相乘关系,当时间为1时,则恢复原状,动画结束,出场完成!

注意:每个点与原点的距离有正负值之分,对比时要用绝对值!

js

animateAction() {          if (this.mesh && this.time >= 0) {            this.speed += this.speed1;            this.time += this.speed;            const positions = this.mesh.geometry.attributes.position;            const normal = this.mesh.geometry.attributes.normal;            const initialPositions = this.mesh.geometry.attributes.initialPosition;            const count = positions.count;            const radius = this.time * this.max;            for (let i = 0; i < count; i++) {              const nx = normal.getX(i);              const ny = normal.getY(i);              const nz = normal.getZ(i);              const ix = initialPositions.getX(i);              const iy = initialPositions.getY(i);              const iz = initialPositions.getZ(i);              positions.setXYZ(                i,                radius >= Math.abs(ix) ? ix : radius * nx,                radius >= Math.abs(iy) ? iy : radius * ny,                radius >= Math.abs(iz) ? iz : radius * nz              );            }            positions.needsUpdate = true;            if (this.time >= 1) {              this.time = -1;            }          }        }

7.给光点开启布灵布灵的效果

  • 传入每个点的颜色值,这里采用的是随机颜色,范围是[0,1]

js

            let colors = [];            for (let i = 0; i < positions.count; i++) {              colors.push(Math.random(), Math.random(), Math.random());            }            geometry.setAttribute('color', newTHREE.Float32BufferAttribute(colors, 3));
  • 开启材质的顶点颜色

js

let material = newTHREE.PointsMaterial({              size: 1,              color: newTHREE.Color(that.color),              vertexColors: true,//顶点颜色              transparent: true,              depthTest: false            });
  • 完成出场后,动画帧中添加给每个点不停赋值随机色,就能形成一闪一闪的效果。

js

const colors = this.mesh.geometry.attributes.color;              const count = colors.count;              for (let i = 0; i < count; i++) {                colors.setXYZ(i, Math.random(), Math.random(), Math.random());              }              colors.needsUpdate = true;

注意:点颜色值改变要通知颜色属性要更新

为什么开启点颜色后可以有不同颜色深度的效果呢?

  1. 我们可以改一下编译前的片元着色器,去掉一个分号,让它报错,打印一下编译后的着色器结果

js

material.onBeforeCompile = (shader) => {                          //修改片元着色器,使其变成发光圆点              shader.fragmentShader = shader.fragmentShader.replace(                `gl_FragColor = vec4( outgoingLight, diffuseColor.a );`,                `gl_FragColor = vec4( outgoingLight, diffuseColor.a )`                             );            };

  1. 代码真的好多,看得好头大!是时候展现你的着色器常识了!这个点颜色值肯定是从顶点着色器那边穿过来的,搜一下varying全局变量,果不其然,可以发现一下代码!

c++

173: #ifdef USE_COLOR174: 	varying vec3 vColor;175: #endif229: #ifdef USE_COLOR230: 	diffuseColor.rgb *= vColor;231: #endif
  1. 破案!diffuseColor是点显示的颜色,默认的时候是材质的color属性值,如果开启vertexColors后,vColor传过来,会执行颜色值相乘,即颜色值叠加,就会出现这样不同的深度的颜色。

总结

以上点的运动都是通过计算传入最终位置结果,但其实可以通过修改顶点着色器也能实现同样的效果。

three.js真的封装很全,大家可以弄点报错,看看人家的着色器代码,学习一下,也方便以后修改着色器代码,自定义效果!

GitHub地址

https://github.com/xiaolidan00/my-three

参考https://threejs.org/examples/?q=point#webgl_points_dynamic

相关文章
|
前端开发 容器 API
基于 three.js 的 3D 粒子动效实现
作者:个推web前端开发工程师 梁神 一、背景 粒子特效是为模拟现实中的水、火、雾、气等效果由各种三维软件开发的制作模块,原理是将无数的单个粒子组合使其呈现出固定形态,借由控制器、脚本来控制其整体或单个的运动,模拟出现真实的效果。
3273 0
|
1月前
|
移动开发 前端开发 JavaScript
学习Particles.js 给网页来点粒子特效
学习Particles.js 给网页来点粒子特效
76 0
|
1月前
|
存储 JavaScript 前端开发
使用JS创造一个3D粒子化星空,十分酷炫,大家快进来看看吧
使用JS创造一个3D粒子化星空,十分酷炫,大家快进来看看吧
|
4月前
|
JavaScript
JS+CSS3点击粒子烟花动画js特效
JS+CSS3点击粒子烟花动画js特效
36 0
JS+CSS3点击粒子烟花动画js特效
|
前端开发 CDN 容器
Particles.js基于Canvas画布创建粒子原子颗粒效果
文章目录 使用方法 自定义参数 相关链接 Particles.js是一款基于HTML5 Canvas画布的轻量级粒子动画插件,可以设置粒子的形状、旋转、分布、颜色等属性,还可以动态添加粒子,效果非常炫酷,能和鼠标互动吸附或者是躲避鼠标指针。
1492 0
Cocos2d-JS自定义粒子系统
<span style="font-size:14px;">除了使用Cocos2d-JS的11种内置粒子系统外,我们还可以通过创建ParticleSystem对象,并设置属性实现自定义粒子系统,通过这种方式完全可以实现我们说需要的各种效果的粒子系统。使用ParticleSystem自定义粒子系统至少有两种方式可以实现:代码创建和plist文件创建。<br>代码创建粒子系统需要手工设置这些属性,
1639 0
Cocos2d-JS内置粒子系统
<span style="font-size:14px;">从类图中我们可以看到,Cocos2d-JS中有内置的11种粒子,这些粒子的属性都是预先定义好的,我们也可以在程序代码中单独修改某些属性,我们在上一节的实例中都已经实现了这些属性的设置。<br>内置粒子系统<br>内置的11种粒子系统说明如下: <br>ParticleExplosion。爆炸粒子效果,属于半径模式。<br>Partic
1485 0
|
2月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
70 2
|
2月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的宠物援助平台附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的宠物援助平台附带文章源码部署视频讲解等
63 4
|
2月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的宠物交易平台附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的宠物交易平台附带文章源码部署视频讲解等
55 4