Skip to content

163. 打碎重组粒子效果

Published:

不知道大家有没有见到过这种粒子效果:

2025-06-02 21.19.14.gif

https://ak.hypergryph.com/#world

由一堆粒子构成某种图案,然后再打碎重组成另一种图案。

这种粒子效果也是比较常见的。

那用 three.quarks 能不能实现这种粒子效果呢?

支持,three.quarks 支持在不同粒子序列之间变换。

需要用 TextureSequencer 的 api 来指定粒子序列。

我们来试一下:

npx create-vite three-quarks-texture-sequencer

image.png

进入项目,安装依赖:

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 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(0, 0, 20);
camera.lookAt(0, 0, 0);

const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height)

const clock = new THREE.Clock();
function render() {
    const delta = clock.getDelta();
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}
render();

document.body.append(renderer.domElement);

const controls = new OrbitControls(camera, renderer.domElement);

创建 Scene、Camera、Renderer。

相机在 0,0,20 的位置,正对 XY 平面。

改下 style.css

body {
  margin: 0;
}

安装下粒子效果库:

npm install --save three.quarks

在 mesh.js 初始化下 three.quarks

import * as THREE from "three";
import { ApplySequences, BatchedParticleRenderer,ConeEmitter, ConstantValue, DonutEmitter, GridEmitter, IntervalValue, ParticleSystem, RandomColor, RectangleEmitter, TextureSequencer, Vector3, Vector4 } 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: 10,
    looping: true,
    startLife: new ConstantValue(9),
    startSpeed: new ConstantValue(0),
    startSize: new IntervalValue(0.1, 0.2),
    startColor: new RandomColor(
        new Vector4(1, 1, 1, 1),
        new Vector4(1, 0.7, 0, 1)
    ),
    emissionOverTime: new ConstantValue(0),
    emissionBursts: [
        {
            time: 0,
            count: new ConstantValue(2000),
            probability: 1,
        },
    ],
    shape: new GridEmitter({
        width: 20, 
        height: 20, 
        column: 50, 
        row: 50
    }),
    material: new THREE.MeshBasicMaterial({
        map: texture,
        transparent: true
    })
});

group.add(particles.emitter);

batchRenderer.addSystem(particles);

export {
    batchRenderer
}

export default group;

这次用网格粒子发射器 GridEmitter。

粒子速度为 0 也就是静止不动。

动画总时长是 10 秒,粒子在固定的 9 秒后消失。

重点是这里:

image.png

每次发射为 0,但初始就有 2000 个粒子。

point.png 从这里下载:

https://github.com/QuarkGluonPlasma/threejs-course-code/blob/main/all-shape-three-quarks/public/point.png

image.png

image.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

image.png

2025-06-02 22.33.23.gif

现在初始就是 50 行、50 列的静止不动的粒子。

然后让粒子按照某个序列动起来:

下载官方仓库这个 png,放到 public 目录下:

https://github.com/Alchemist0823/three.quarks/blob/master/packages/three.quarks/examples/textures/text_texture.png

image.png

image.png

在代码里加载:

image.png

const pointTexture = await loader.loadAsync('./text_texture.png');

const seq = new TextureSequencer(0.1, 0.1, new Vector3(-10, 0, 0));
seq.fromImage(pointTexture.image, 0.2);

const applySeq = new ApplySequences(0.001);
applySeq.appendSequencer(new IntervalValue(2, 3), seq);
particles.addBehavior(applySeq);

加载这张图片。

用这张图片的像素点来创建 TextureSequencer,阈值是透明度 0.2 的像素。

TextureSequencer 的三个参数分别是 scaleX、scaleY 和位置。

然后用这个粒子序列来运动。

ApplySequences 的参数是每个粒子运动的时间间隔,间隔 0.001 秒移动下个粒子。

这个序列在 2 到 3s 内的随机时间生效。

看下效果:

2025-06-02 22.43.15.gif

这样,基于序列的粒子运动就完成了。

你也可以设置多个序列。

这里要找一些比较小的 png 图片,我们直接用官方仓库里的现成图片吧:

https://github.com/Alchemist0823/three.quarks/blob/master/packages/three.quarks/examples/textures/logo_texture.png

image.png

如果你自己来准备图片,也要用这种足够小的背景透明的 png 图片(因为小图片像素少,需要的粒子少)。

image.png

加一个新的粒子序列,并且把时间间隔调的更小:

image.png

const logoTexture = await loader.loadAsync('./logo_texture.png');

const seq2 = new TextureSequencer(0.1, 0.1, new Vector3(-5, -5, 0));
seq2.fromImage(logoTexture.image, 0.2);

const applySeq = new ApplySequences(0.0001);
applySeq.appendSequencer(new IntervalValue(2, 3), seq);
applySeq.appendSequencer(new IntervalValue(6, 7), seq2);

2025-06-02 22.50.51.gif

这样,两个粒子序列就都生效了。

案例代码上传了小册仓库

总结

这节我们实现了打碎重组的粒子效果。

three.quarks 支持粒子序列,可以通过 png 图片的像素来提取序列,然后让粒子在按照顺序运动到目标位置。

我们准备了两张背景透明的像素足够小的图片,用它们来做粒子序列,让粒子运动到这俩图片的像素的位置。

这种粒子效果还是比较常见的,用 three.quarks 可以轻易实现。

评论