【可视化学习】59-three.quarks实现粒子特效
发表于:2024-02-04 |

前言

本篇文章将简单和大家介绍一下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
// 导入threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 导入hdr加载器
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();
});

// rgbeLoader 加载hdr贴图
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
// 导入threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 导入hdr加载器
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();
});

// rgbeLoader 加载hdr贴图
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
// 导入lil.gui
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
// 创建GUI
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 PointEmitter(),
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给到了ParticleSysteminstancingGeometry属性中,material给到了ParticleSystemmaterial属性中,并且将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,我们加载叶子模型,然后将叶子模型的几何体给到ParticleSysteminstancingGeometry属性中,material给到了ParticleSystemmaterial属性中,并且将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(),,然后添加了粒子行为Rotation3DOverLifeNoise。将粒子系统旋转了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,
}),
// 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)
)
)

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来设置精灵图的起始位置,uTileCountvTileCount来设置精灵图的行和列。

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,
// renderMode: RenderMode.Trail,
// rendererEmitterSettings:{
// startLength:new ConstantValue(20)
// },
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,本文就到这里了,我这就属于抛砖引玉,希望大家能够有所收获,更多内容,敬请期待。

上一篇:
如何进行简单的博客搭建
下一篇:
最新的镜像源记录