前面的粒子效果都是用同一个图片:


能不能在粒子动画的过程中切换图片呢?
也就是粒子动画结合帧动画。
可以的,three-quarks 支持这个。
比如 three.quarks 仓库里的这些图片:

可以在粒子动画的过程中切换不同帧的图片,来实现更复杂的效果。
我们试一下:
npx create-vite three-quarks-frame-animation

进入项目,安装依赖:
npm install
npm install --save three
npm install --save-dev @types/three
改下 src/main.js
import './style.css';
import * as THREE from 'three';
import {
OrbitControls
} from 'three/addons/controls/OrbitControls.js';
import mesh from './mesh.js';
const scene = new THREE.Scene();
scene.add(mesh);
const directionLight = new THREE.DirectionalLight(0xffffff);
directionLight.position.set(500, 600, 800);
scene.add(directionLight);
const ambientLight = new THREE.AmbientLight();
scene.add(ambientLight);
const helper = new THREE.AxesHelper(1000);
scene.add(helper);
const width = window.innerWidth;
const height = window.innerHeight;
const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 10000);
camera.position.set(500, 600, 800);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height)
function render() {
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
document.body.append(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
创建 Scene、Light、Camera、Renderer。
改下 style.css
body {
margin: 0;
}
安装下粒子效果库:
npm install --save three.quarks
在 mesh.js 初始化下 three.quarks
import * as THREE from "three";
import { BatchedParticleRenderer,ConeEmitter, ConstantValue, DonutEmitter, IntervalValue, ParticleSystem, RandomColor } from "three.quarks";
const group = new THREE.Group();
const batchRenderer = new BatchedParticleRenderer();
group.add(batchRenderer);
const loader = new THREE.TextureLoader();
const texture = loader.load('./point.png');
const particles = new ParticleSystem({
duration: 20,
looping: true,
startLife: new IntervalValue(0, 10),
startSpeed: new IntervalValue(0, 100),
startSize: new IntervalValue(0, 20),
startColor: new RandomColor(
new THREE.Vector4(1, 0, 0, 1),
new THREE.Vector4(0, 0, 1, 1)
),
emissionOverTime: new ConstantValue(5000),
shape: new DonutEmitter({
radius: 300,
arc: Math.PI * 2,
donutRadius: 50
}),
material: new THREE.MeshBasicMaterial({
map: texture,
transparent: true,
side: THREE.DoubleSide
})
});
group.add(particles.emitter);
batchRenderer.addSystem(particles);
export {
batchRenderer
}
export default group;
这次用环形粒子发射器。
point.png 从这里下载:


在 main.js 引入:
import { batchRenderer } from './mesh.js';
const clock = new THREE.Clock();
function render() {
const delta = clock.getDelta();
renderer.render(scene, camera);
requestAnimationFrame(render);
if(batchRenderer) {
batchRenderer.update(delta);
}
}
render();
跑一下,看下效果:
npm run dev


现在每个粒子都是小圆点的图片。

把 three.quarks 仓库这张图片下载下来:

放 public 目录下:

那在代码里如何使用呢?
首先替换下 texture 的路径:


uTileCount: 10,
vTileCount: 10,
startTileIndex: new ConstantValue(53)
指定 u 和 v 方向都分割 10 份
startTileIndex 指定第 5 行第 3 列,这里是从 0 开始的
第五行第三列就是这个:

看下效果:

生效了,但是粒子太小看不清。
我们把粒子调大一点,数量调小:

startSize: new IntervalValue(0, 100),
emissionOverTime: new ConstantValue(50),

现在就明显多了。
然后如何让它做帧动画呢?
也是通过 addBehavior 来自定义行为:
从 53 到 61:


particles.addBehavior(
new FrameOverLife(
new PiecewiseBezier(
[
[new Bezier(53, 55, 58, 61), 0]
]
)
)
);
用 FrameOverLife 来自定义声明周期中的帧的变化。
看下效果:

现在粒子运动过程中就会切换图片了。
当然,现在可能看不清楚,我们换其他图片试试:

从 45 到 52

new Bezier(45, 48, 50, 52)

现在就可以明显看出图片的变化了。
案例代码上传了小册仓库
总结
这节我们学了如何在粒子动画的过程中让粒子实现帧动画。
首先准备一个包含多个帧的图片,设置 u 和 v 方向的分割数,然后定义从哪个开始。
之后用 addBehavior 和 FrameOverLife 来定义整个生命周期中帧的变化。
帧动画可以实现更复杂的粒子效果,比如烟雾效果、技能特效。
后面的实战会基于粒子动画和帧动画来实现逼真的粒子效果。