基本思路
使用bufferGeometry创建沿着轨迹的网格(geometry),再使用threjs的point对象实体化粒子组成的路径,最后在动画回调中更新每个粒子的位置,然后更新网格的points
粒子对象
使用class管理整个粒子系统
import {
BufferGeometry,
Float32BufferAttribute,
Points,
PointsMaterial,
} from "three";
/**
* roll particles through a path form series of vec3
*/
export default class ParticleGroup {
path = []; //PATH OF LINE
pathLength = 0;
particles = []; //PARTICLES points
counts = 300; //Particles mounts
radius = 1;
mesh;
t = 0;
constructor(path) {
this.path = path;
this.pathLength = path.length;
this.init();
}
init = () => {
let { counts, particles, path, radius, pathLength } = this;
for (let i = 0; i < counts; i++) {
const index = Math.floor((pathLength / counts) * i);
const current = path[index];
const { x, y, z } = current;
const dx = (Math.random() - 0.5) * radius;
const dy = (Math.random() - 0.5) * radius;
const dz = (Math.random() - 0.5) * radius;
// vertices.push(x + dx, y + dy, z + dz);
const particle = new Particle(x, y, z, dx, dy, dz, index, i);
particles.push(particle);
// const
}
const vertices = this.getVertices();
const geometry = new BufferGeometry();
geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3));
const material = new PointsMaterial({ size: 0.03, color: 0x00ffff });
let points = new Points(geometry, material);
this.mesh = points;
// scene.add(points);
};
// get current paritcle group geometry's vectices
getVertices = () => {
let { path, pathLength } = this;
const vertices = [];
this.particles.forEach((particle) => {
let { dx, dy, dz, index, id } = particle;
const current = path[index];
const { x, y, z } = current;
let expandScale =
// (3 - (index / pathLength) * 3) * Math.sin((id * index) / 10000) + 0.8;
(5 - (index / pathLength) * 5) * Math.sin(id) * Math.sin(index / 100) +
0.3;
// (3 - (index / pathLength) * 3) * Math.sin(id) + 0.3;
vertices.push(
x + dx * expandScale,
y + dy * expandScale,
z + dz * expandScale
);
});
return vertices;
};
// update particle index
upDateParitcles = () => {
let { particles, pathLength } = this;
particles.forEach((particle) => {
particle.index += 1;
if (particle.index == pathLength) {
particle.index = 0;
}
});
};
// update mesh geometry
upDateMeshGeometry = () => {
let vertices = this.getVertices();
const geometry = this.mesh.geometry;
geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3));
this.mesh.geometry = geometry;
};
render = () => {
requestAnimationFrame(this.render);
let { particles, pathLength } = this;
this.t += 0.01;
this.upDateParitcles();
this.upDateMeshGeometry();
};
// add mesh to group or scene
addto = (group) => {
group.add(this.mesh);
};
}
class Particle {
x;
y;
z;
dx;
dy;
dz;
index;
id;
constructor(x, y, z, dx, dy, dz, index, id) {
this.x = x;
this.y = y;
this.z = z;
this.dx = dx;
this.dy = dy;
this.dz = dz;
this.index = index;
this.id = id;
}
}
在场景中使用
const path = new THREE.CatmullRomCurve3([
new THREE.Vector3(0, -4, -4),
new THREE.Vector3(-4, 0 - 4),
new THREE.Vector3(-4, 0, 4),
new THREE.Vector3(4, 0, 4),
new THREE.Vector3(4, 0, -4),
]);
const PS = path.getPoints(2000);
const parts = new ParticleGroup(PS);
console.log(parts);
parts.addto(scene);
parts.render();