前言
本篇文章将简单和大家介绍一下three.quarks的使用,three.quarks是一个three.js的粒子特效库,可以用来实现一些粒子特效,我这里就简单介绍一些基本的使用方法,更多内容留给大家自行去开发体验。
three.quarks的文档地址
https://github.com/Alchemist0823/three.quarks
搭建基础代码
这部分我就省略了,我加上了一个环境纹理贴图,最终代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer({ antialias: true, }); renderer.shadowMap.enabled = true; renderer.toneMapping = THREE.ReinhardToneMapping; renderer.toneMappingExposure = 1; renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement);
camera.position.z = 8; camera.position.y = 2.5; camera.position.x = 3; camera.lookAt(0, 1.2, 0);
const axesHelper = new THREE.AxesHelper(5); scene.add(axesHelper);
const gridHelper = new THREE.GridHelper(50, 50); gridHelper.material.opacity = 0.3; gridHelper.material.transparent = true; scene.add(gridHelper);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; function animate() { controls.update(); requestAnimationFrame(animate); renderer.render(scene, camera); } animate();
window.addEventListener("resize", () => { renderer.setSize(window.innerWidth, window.innerHeight); camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); });
let rgbeLoader = new RGBELoader(); rgbeLoader.load("./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr", (envMap) => { envMap.mapping = THREE.EquirectangularRefractionMapping; scene.background = new THREE.Color(0x000000); scene.environment = envMap; });
|
安装three.quarks
1
| npm install three.quarks
|
three.quarks的基础使用
图片准备
使用这个库之前要先准备一张图片,我这里就准备了这张图片。
导入
1 2 3 4 5 6 7 8 9
| import { ConstantValue, IntervalValue, PointEmitter, RenderMode, ParticleSystem, BatchedParticleRenderer, RandomColor, } from "three.quarks";
|
这里我简单介绍一下:
- ConstantValue是给我们定义常量值的
- IntervalValue是给我们定义区间值的
- PointEmitter是给我们定义发射器的,这里我们使用的是点发射器
- RenderMode是给我们定义渲染模式的
- ParticleSystem是给我们定义粒子系统的
- BatchedParticleRenderer是给我们定义批量粒子渲染器的
- RandomColor是给我们定义随机颜色的
创建粒子渲染器
1 2 3 4 5
| let batchRenderer;
batchRenderer = new BatchedParticleRenderer();
scene.add(batchRenderer);
|
添加纹理
1 2 3
| const textureLoader = new THREE.TextureLoader(); const texture = textureLoader.load("./texture/particle_default.png");
|
创建粒子系统
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| const particles = new ParticleSystem({ duration: 5, looping: false, startLife: new IntervalValue(0, 1), startSpeed: new IntervalValue(0, 1), startSize: new IntervalValue(0, 0.2), startColor: new RandomColor( new THREE.Vector4(1, 0.91, 0.51, 1), new THREE.Vector4(1, 0.44, 0.16, 1) ), worldSpace: true, maxParticles: 1000, emissionOverTime: new ConstantValue(1000), shape: new PointEmitter(), material: new THREE.MeshBasicMaterial({ map: texture, blending: THREE.AdditiveBlending, transparent: true, side: THREE.DoubleSide, }), renderMode: RenderMode.BillBoard, rendererOrder: 1, });
|
添加粒子系统
1 2
| scene.add(particles.emitter); batchRenderer.addSystem(particles);
|
粒子系统每一帧的更新
1 2 3 4 5 6 7 8 9 10 11
| let clock = new THREE.Clock();
let batchRenderer; function animate() { let delta = clock.getDelta(); controls.update(); requestAnimationFrame(animate); renderer.render(scene, camera); batchRenderer && batchRenderer.update(delta); }
|
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
| import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js"; import { ConstantValue, IntervalValue, PointEmitter, RenderMode, ParticleSystem, BatchedParticleRenderer, RandomColor, } from "three.quarks";
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer({ antialias: true, }); renderer.shadowMap.enabled = true; renderer.toneMapping = THREE.ReinhardToneMapping; renderer.toneMappingExposure = 1; renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement);
camera.position.z = 8; camera.position.y = 2.5; camera.position.x = 3; camera.lookAt(0, 1.2, 0);
const axesHelper = new THREE.AxesHelper(5); scene.add(axesHelper);
const gridHelper = new THREE.GridHelper(50, 50); gridHelper.material.opacity = 0.3; gridHelper.material.transparent = true; scene.add(gridHelper);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; let clock = new THREE.Clock();
let batchRenderer; function animate() { let delta = clock.getDelta(); controls.update(); requestAnimationFrame(animate); renderer.render(scene, camera); batchRenderer && batchRenderer.update(delta); } animate();
window.addEventListener("resize", () => { renderer.setSize(window.innerWidth, window.innerHeight); camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); });
let rgbeLoader = new RGBELoader(); rgbeLoader.load("./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr", (envMap) => { envMap.mapping = THREE.EquirectangularRefractionMapping; scene.background = new THREE.Color(0x000000); scene.environment = envMap; });
batchRenderer = new BatchedParticleRenderer();
scene.add(batchRenderer);
const textureLoader = new THREE.TextureLoader(); const texture = textureLoader.load("./texture/particle_default.png");
const particles = new ParticleSystem({ duration: 5, looping: false, startLife: new IntervalValue(0, 1), startSpeed: new IntervalValue(0, 1), startSize: new IntervalValue(0, 0.2), startColor: new RandomColor( new THREE.Vector4(1, 0.91, 0.51, 1), new THREE.Vector4(1, 0.44, 0.16, 1) ), worldSpace: true, maxParticles: 1000, emissionOverTime: new ConstantValue(1000), shape: new PointEmitter(), material: new THREE.MeshBasicMaterial({ map: texture, blending: THREE.AdditiveBlending, transparent: true, side: THREE.DoubleSide, }), renderMode: RenderMode.BillBoard, rendererOrder: 1, });
scene.add(particles.emitter); batchRenderer.addSystem(particles);
|
最终效果
重新播放
此时我们就实现了一个简单的粒子特效,这时候如果我们想让动画重新播放,我们可以在代码中添加如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
const gui = new GUI(); particles.emitter.name = "particles"; let options = { emitParticles: function () { particles.emitEnded = false; particles.time = 0; }, };
gui.add(options, "emitParticles");
|
此时效果如下,点击按钮就可以重新播放粒子特效了。
设置不同形状的发射器
下面都是我自己随便起的名字哈,不算官方定义的名字。
球发射器
这里的导入我就先写一下,后续大家看到new的都是要导入的,我就省略不提了。
1 2 3 4 5 6 7 8 9 10 11 12 13
| import { SphereEmitter, } from "three.quarks"; const particles = new ParticleSystem({ shape: new SphereEmitter({ radius: 1, thickness: 0.1, arc: Math.PI * 2, }), });
|
圆锥发射器
1 2 3 4 5 6
| shape: new ConeEmitter({ radius: 0, height: 1, arc: Math.PI * 2, angle: Math.PI / 9, }),
|
1/4球发射器
1 2 3 4 5
| shape: new HemisphereEmitter({ radius: 1, arc: Math.PI / 2, thickness: 0.2, }),
|
圆形发射器
1 2 3 4 5
| shape: new CircleEmitter({ radius: 1, thickness: 0.2, arc: Math.PI * 2, }),
|
圆环发射器
1 2 3 4 5 6
| shape: new DonutEmitter({ radius: 2, thickness: 0.1, arc: Math.PI * 2, donutRadius: 0.2, }),
|
自定义粒子的几何体和材质
比如我们定一个立方体标准材质的粒子,这里我将boxGeometry
给到了ParticleSystem
的instancingGeometry
属性中,material
给到了ParticleSystem
的material
属性中,并且将renderMode
改为了RenderMode.Mesh
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| let boxGeometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ roughness: 0.5, metalness: 0.5, color: 0xffffff, });
const directionalLight = new THREE.DirectionalLight(0xffffff, 1); directionalLight.position.set(1, 1, 1); scene.add(directionalLight);
const particles = new ParticleSystem({ instancingGeometry: boxGeometry, duration: 5, looping: false, startLife: new IntervalValue(0, 1), startSpeed: new IntervalValue(0, 10), startSize: new IntervalValue(0, 0.2), startColor: new RandomColor( new THREE.Vector4(1, 0.91, 0.51, 1), new THREE.Vector4(1, 0.44, 0.16, 1) ), worldSpace: true, maxParticles: 1000, emissionOverTime: new ConstantValue(1000), shape: new PointEmitter(), material: material, renderMode: RenderMode.Mesh, rendererOrder: 1, });
|
添加粒子行为
这里我添加了粒子的大小、颜色、速度的在他生命周期的变化,new Bezier
是根据贝塞尔曲线变化,而不是均匀变化的意思
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| particles.addBehavior( new SizeOverLife(new PiecewiseBezier([[new Bezier(1, 0.95, 0.75, 0), 0]])) );
particles.addBehavior( new ColorOverLife( new ColorRange( new THREE.Vector4(1, 0.9, 0.5, 1), new THREE.Vector4(1, 0.1, 0.0, 1) ) ) );
particles.addBehavior( new SpeedOverLife(new PiecewiseBezier([[new Bezier(1, 0.75, 0.5, 0), 0]])) );
|
利用粒子行为实现漫天飞舞的树叶
添加叶子模型
ok,我们加载叶子模型,然后将叶子模型的几何体给到ParticleSystem
的instancingGeometry
属性中,material
给到了ParticleSystem
的material
属性中,并且将renderMode
改为了RenderMode.Mesh
,shape
改成了GridEmitter
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| gltfLoader.load("model/leave.glb", (gltf) => { let geo = gltf.scene.children[0].geometry; const particles = new ParticleSystem({ instancingGeometry: geo, duration: 5, looping: false, startLife: new IntervalValue(0, 5), startSpeed: new IntervalValue(0, 2), startSize: new IntervalValue(0, 0.2), startColor: new RandomColor( new THREE.Vector4(1, 0.91, 0.51, 1), new THREE.Vector4(1, 0.44, 0.16, 1) ), worldSpace: true, maxParticles: 1000, emissionOverTime: new ConstantValue(1000), shape: new GridEmitter({ width: 10, height: 10, rows: 10, column: 10, }), material: gltf.scene.children[0].material, renderMode: RenderMode.Mesh, rendererOrder: 1, }); });
|
添加粒子行为
这里我设置了粒子初始的旋转状态startRotation: new RandomQuatGenerator(),
,然后添加了粒子行为Rotation3DOverLife
和Noise
。将粒子系统旋转了90度和向上平移了10个单位。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| const particles = new ParticleSystem({ instancingGeometry: geo, duration: 5, looping: false, startLife: new IntervalValue(0, 5), startSpeed: new IntervalValue(0, 2), startSize: new IntervalValue(0, 0.2), startColor: new RandomColor( new THREE.Vector4(1, 0.91, 0.51, 1), new THREE.Vector4(1, 0.44, 0.16, 1) ), startRotation: new RandomQuatGenerator(), worldSpace: true, maxParticles: 1000, emissionOverTime: new ConstantValue(1000), shape: new GridEmitter({ width: 10, height: 10, rows: 10, column: 10, }), material: gltf.scene.children[0].material, renderMode: RenderMode.Mesh, rendererOrder: 1, });
particles.addBehavior( new Rotation3DOverLife( new AxisAngleGenerator( new THREE.Vector3(0, 0.5, 2).normalize(), new ConstantValue(1) ), false ) );
particles.addBehavior( new Noise(new ConstantValue(0.1), new ConstantValue(2)) );
particles.emitter.name = "particles"; particles.emitter.rotation.x = Math.PI / 2; particles.emitter.position.set(0, 10, 0);
|
重力实现打铁花效果
这里我添加了一个重力行为ApplyForce
,然后将粒子系统向上平移了2个单位。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| const textureLoader = new THREE.TextureLoader(); const texture = textureLoader.load("./texture/particle_default.png");
const particles = new ParticleSystem({ duration: 2, looping: false, startLife: new IntervalValue(0, 2), startSpeed: new IntervalValue(0, 2), startSize: new IntervalValue(0, 0.2), startColor: new RandomColor( new THREE.Vector4(1, 0.91, 0.51, 1), new THREE.Vector4(1, 0.44, 0.16, 1) ), worldSpace: true, maxParticles: 1000, emissionOverTime: new ConstantValue(1000), shape: new PointEmitter(), material: new THREE.MeshBasicMaterial({ map: texture, blending: THREE.AdditiveBlending, transparent: true, side: THREE.DoubleSide, }), renderMode: RenderMode.Trail, rendererEmitterSettings: { startLength: new ConstantValue(20) }, rendererOrder: 1, });
particles.emitter.name = "particles"; particles.emitter.position.set(0, 2, 0); scene.add(particles.emitter); batchRenderer.addSystem(particles);
particles.addBehavior( new ApplyForce( new THREE.Vector3(0, -1, 0), new ConstantValue(1) ) )
|
粒子反弹效果
这里我添加了一个ApplyCollision
实现了粒子的反弹效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| const particles = new ParticleSystem({ duration: 5, looping: false, startLife: new IntervalValue(0, 2), startSpeed: new IntervalValue(0, 2), startSize: new IntervalValue(0, 0.2), startColor: new RandomColor( new THREE.Vector4(1, 0.91, 0.51, 1), new THREE.Vector4(1, 0.44, 0.16, 1) ), worldSpace: true, maxParticles: 1000, emissionOverTime: new ConstantValue(1000), shape: new PointEmitter(), material: new THREE.MeshBasicMaterial({ map: texture, blending: THREE.AdditiveBlending, transparent: true, side: THREE.DoubleSide, }), rendererOrder: 1, });
particles.emitter.name = "particles"; particles.emitter.position.set(0, 2, 0); scene.add(particles.emitter); batchRenderer.addSystem(particles);
particles.addBehavior( new ApplyForce( new THREE.Vector3(0, -1, 0), new ConstantValue(1) ) )
particles.addBehavior( new ApplyCollision( { resolve: function (pos, normal) { if (pos.y < 0) { normal.set(0, 1, 0) return true } else { return false } } }, 0.6 ) )
|
使用精灵图实现粒子动画
精灵图
这里我使用了这样一张精灵图片,精灵图可能对于新一代前端来说这个比较陌生,对于老一代的切图仔来说这个就很熟悉了。
代码
这里的核心其实就是导入精灵图,然后我们可以用startTileIndex
来设置精灵图的起始位置,uTileCount
和vTileCount
来设置精灵图的行和列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| const textureLoader = new THREE.TextureLoader(); const texture = textureLoader.load("./texture/texture1.png");
const particles = new ParticleSystem({ duration: 5, looping: true, startLife: new IntervalValue(0.6, 0.8), startSpeed: new IntervalValue(0.1, 2), startSize: new IntervalValue(0.75, 1.5), startColor: new RandomColor( new THREE.Vector4(0.6, 0.6, 0.51, 0.3), new THREE.Vector4(1, 1, 1, 0.5) ), startRotation: new IntervalValue(-Math.PI, Math.PI), worldSpace: true, maxParticles: 1000, emissionOverTime: new ConstantValue(0), emissionBursts: [ { time: 0, count: new ConstantValue(15), probability: 1 } ], shape: new ConeEmitter({ angle: Math.PI / 9, radius: 0.3, thickness: 1, arc: Math.PI * 2 }), material: new THREE.MeshBasicMaterial({ map: texture, blending: THREE.AdditiveBlending, transparent: true, side: THREE.DoubleSide, }), renderMode: RenderMode.Mesh, rendererOrder: 1, startTileIndex: new ConstantValue(46), uTileCount: 10, vTileCount: 10, });
particles.emitter.name = "particles"; particles.emitter.position.set(0, 2, 0); scene.add(particles.emitter); batchRenderer.addSystem(particles);
particles.addBehavior( new ApplyForce( new THREE.Vector3(0, -1, 0), new ConstantValue(1) ) )
particles.addBehavior( new ApplyCollision( { resolve: function (pos, normal) { if (pos.y < 0) { normal.set(0, 1, 0) return true } else { return false } } }, 0.6 ) )
particles.addBehavior( new FrameOverLife( new PiecewiseBezier( [[new Bezier(28, 31, 34, 36), 0]] ) ) )
particles.addBehavior( new RotationOverLife( new IntervalValue(-Math.PI / 4, Math.PI / 4) ) )
particles.addBehavior( new ColorOverLife( new ColorRange( new THREE.Vector4(1, 1, 1, 1), new THREE.Vector4(0.5, 0.5, 0.5, 0.1) ) ) )
|
实现粒子变化祝福语
ok,来到本文的最后一部分了,接下来我们要实现一个粒子变化祝福语的效果,首先你得准备祝福语的图片,而且要保证是png,背景透明的图片
透明背景祝福语图片获取
艺术字网站
首先去找一个艺术字生成网站,我用的是这个
地址:https://www.yishuzi.cn/
扣背景网站
这时候我们拿到的图片是有白色背景的
然后我们可以通过这个网站去扣背景
地址:https://www.remove.bg/zh
简单操作之后我们就得到了透明背景的祝福语图片
以此类推,大家可以得到自己想要的祝福语
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| const textureLoader = new THREE.TextureLoader(); const texture = textureLoader.load("./texture/texture1.png"); const textTexture1 = textureLoader.load("./texture/new_year_1.png"); const textTexture2 = textureLoader.load("./texture/new_year_2.png"); const textTexture3 = textureLoader.load("./texture/new_year_3.png"); const textTexture4 = textureLoader.load("./texture/new_year_4.png"); const textTexture5 = textureLoader.load("./texture/new_year_5.png");
THREE.DefaultLoadingManager.onLoad = function () { const particles = new ParticleSystem({ duration: 23, looping: true, startLife: new ConstantValue(22), startSpeed: new ConstantValue(0), startSize: new IntervalValue(0.05, 0.2), startColor: new RandomColor( new THREE.Vector4(1, 0.6, 1, 1), new THREE.Vector4(1, 1, 1, 1) ), worldSpace: true, maxParticles: 2500, emissionOverTime: new ConstantValue(0), emissionBursts: [ { time: 0, count: new ConstantValue(2500), probability: 1 } ], shape: new GridEmitter({ width: 15, height: 15, column: 50, row: 50, }), material: new THREE.MeshBasicMaterial({ map: texture, blending: THREE.AdditiveBlending, transparent: true, side: THREE.DoubleSide, }), renderMode: RenderMode.BillBoard, rendererOrder: 0, startTileIndex: new ConstantValue(0), uTileCount: 10, vTileCount: 10, });
particles.emitter.name = "particles"; scene.add(particles.emitter); batchRenderer.addSystem(particles);
let seq = new TextureSequencer(0.15, 0.15, new THREE.Vector3(-7.5, -2, 0)) seq.fromImage(textTexture1.image, 0.3); let seq2 = new TextureSequencer(0.15, 0.15, new THREE.Vector3(-7.5, -2, 0)) seq2.fromImage(textTexture2.image, 0.3); let seq3 = new TextureSequencer(0.05, 0.2, new THREE.Vector3(-7.5, -2, -2)) seq3.fromImage(textTexture3.image, 0.1); let seq4 = new TextureSequencer(0.05, 0.2, new THREE.Vector3(-7.5, -2, -2)) seq4.fromImage(textTexture4.image, 0.1); let seq5 = new TextureSequencer(0.1, 0.2, new THREE.Vector3(-7.5, -2, -1)) seq5.fromImage(textTexture5.image, 0.1); let applySeq = new ApplySequences(0.001) applySeq.appendSequencer(new IntervalValue(1.0, 2.0), seq) applySeq.appendSequencer(new IntervalValue(5.0, 6.0), seq2) applySeq.appendSequencer(new IntervalValue(9.0, 10.0), seq3) applySeq.appendSequencer(new IntervalValue(13.0, 14.0), seq4) applySeq.appendSequencer(new IntervalValue(17.0, 18.0), seq5) particles.addBehavior(applySeq)
console.log(particles); let options = { emitParticles: function () { particles.emitEnded = false; particles.time = 0; }, };
gui.add(options, "emitParticles"); }
|
效果
这个效果视频有点大,大家可以下载看,或者多等一会
总结
ok,本文就到这里了,我这就属于抛砖引玉,希望大家能够有所收获,更多内容,敬请期待。