Particle System
The ParticleSystem
class is the central component in three.quarks that manages the creation, behavior, and lifecycle of particles. Each particle system defines how particles are emitted, their initial properties, and how they evolve over time.
Overview
A particle system in three.quarks is composed of several key components:
- Emitter: Defines where and how particles are spawned (point, sphere, cone, etc.)
- Initial Properties: Starting values for particles (life, speed, size, color, etc.)
- Behaviors: Components that control how particles evolve over time
- Renderer Settings: How particles are rendered in the scene
Creating a Particle System
Here’s a basic example of creating a particle system:
import * as THREE from "three";
import * as QUARKS from "three.quarks";
const particleSystem = new ParticleSystem({
duration: 5,
looping: true,
shape: new SphereEmitter({
radius: 1,
thickness: 1,
}),
startLife: new IntervalValue(1, 3),
startSpeed: new ConstantValue(5),
startSize: new ConstantValue(0.5),
startColor: new ConstantColor(new Vector4(1, 0.5, 0.1, 1)),
worldSpace: true,
material: new MeshBasicMaterial({
transparent: true,
blending: AdditiveBlending,
}),
behaviors: [
new SizeOverLife(new PiecewiseBezier()),
new ColorOverLife(new Gradient()),
],
});
ParticleSystem Parameters
The ParticleSystem
constructor accepts a configuration object with the following properties:
Core Properties
Property | Type | Default | Description |
---|---|---|---|
duration | number | 1 | Duration of the particle system in seconds |
looping | boolean | true | Whether the particle system should loop after duration |
prewarm | boolean | false | Whether to pre-simulate one loop when first becoming visible |
autoDestroy | boolean | false | Whether the system should be automatically disposed when it finishes emitting particles |
worldSpace | boolean | false | Whether particles are simulated in world space or local space |
Emission Configuration
Property | Type | Default | Description |
---|---|---|---|
shape | EmitterShape | SphereEmitter | Shape that determines where particles are emitted from |
emissionOverTime | ValueGenerator | FunctionValueGenerator | ConstantValue(10) | Particles to emit per second |
emissionOverDistance | ValueGenerator | FunctionValueGenerator | ConstantValue(0) | Particles to emit per unit of distance traveled |
emissionBursts | Array<BurstParameters> | [] | Bursts of particles at specific times |
onlyUsedByOther | boolean | false | Whether this particle system is only used as a sub-system |
Initial Particle Properties
Property | Type | Default | Description |
---|---|---|---|
startLife | ValueGenerator | FunctionValueGenerator | ConstantValue(5) | Initial lifetime of particles in seconds |
startSpeed | ValueGenerator | FunctionValueGenerator | ConstantValue(0) | Initial speed of particles |
startRotation | ValueGenerator | FunctionValueGenerator | RotationGenerator | ConstantValue(0) | Initial rotation of particles |
startSize | ValueGenerator | FunctionValueGenerator | Vector3Generator | ConstantValue(1) | Initial size of particles |
startColor | ColorGenerator | FunctionColorGenerator | ConstantColor | Initial color of particles |
startTileIndex | ValueGenerator | ConstantValue(0) | Initial tile index for sprite sheet animation |
Rendering Properties
Property | Type | Default | Description |
---|---|---|---|
material | Material | Required | Material used to render particles |
instancingGeometry | BufferGeometry | PlaneGeometry | Geometry used for instancing particles |
renderMode | RenderMode | RenderMode.BillBoard | Mode of rendering (Billboard, Trail, Mesh, etc.) |
renderOrder | number | 0 | Rendering order for sorting |
uTileCount | number | 1 | Number of horizontal tiles in texture |
vTileCount | number | 1 | Number of vertical tiles in texture |
blendTiles | boolean | false | Whether to blend between tiles |
softParticles | boolean | false | Whether to use soft particles (fade when close to objects) |
softNearFade | number | 0 | Near fade distance for soft particles |
softFarFade | number | 0 | Far fade distance for soft particles |
layers | Layers | new Layers() | Three.js layers for visibility control |
Behaviors
Property | Type | Default | Description |
---|---|---|---|
behaviors | Array<Behavior> | [] | Behaviors that control how particles evolve over time |
Lifecycle Methods
The ParticleSystem
class provides several methods to control its lifecycle:
play()
Starts or resumes the particle system simulation.
particleSystem.play();
pause()
Pauses the particle system simulation.
particleSystem.pause();
stop()
Stops the particle system simulation and removes all existing particles.
particleSystem.stop();
restart()
Resets the particle system simulation and starts it again.
particleSystem.restart();
endEmit()
Stops emitting new particles but continues simulating existing ones.
particleSystem.endEmit();
dispose()
Removes the particle system’s emitter from the scene. This should be called when the particle system is no longer needed.
particleSystem.dispose();
Adding to the Scene
To use a particle system, you need to add its emitter to the scene and register it with a BatchedRenderer
:
// Create a batched renderer
const batchedRenderer = new BatchedRenderer();
scene.add(batchedRenderer);
// Add the particle system to the scene
scene.add(particleSystem.emitter);
// Register the particle system with the batched renderer
batchedRenderer.addSystem(particleSystem);
// In your animation loop, update the batched renderer
function animate() {
requestAnimationFrame(animate);
// Update with delta time in seconds
batchedRenderer.update(deltaTime);
renderer.render(scene, camera);
}
Utility Methods
The QuarksUtil
class provides utility methods for working with multiple particle systems:
// Add all particle systems in an object to a batched renderer
QuarksUtil.addToBatchRenderer(object, batchedRenderer);
// Play all particle systems in an object
QuarksUtil.play(object);
// Stop all particle systems in an object
QuarksUtil.stop(object);
// Pause all particle systems in an object
QuarksUtil.pause(object);
// Restart all particle systems in an object
QuarksUtil.restart(object);
// End emission for all particle systems in an object
QuarksUtil.endEmit(object);
RenderMode Types
The renderMode
parameter determines how particles are rendered:
Mode | Description |
---|---|
RenderMode.BillBoard | Particles always face the camera |
RenderMode.StretchedBillBoard | Particles face the camera but are stretched based on velocity |
RenderMode.Mesh | Particles are rendered as instanced meshes |
RenderMode.Trail | Particles leave trails behind them |
RenderMode.HorizontalBillBoard | Particles face upward but rotate to face the camera horizontally |
RenderMode.VerticalBillBoard | Particles maintain vertical orientation but face the camera horizontally |
When using RenderMode.Trail
, you need to provide additional settings:
const trailSystem = new ParticleSystem({
// ... other settings
renderMode: RenderMode.Trail,
rendererEmitterSettings: {
startLength: new ConstantValue(30),
followLocalOrigin: false,
},
behaviors: [
new WidthOverLength(new PiecewiseBezier([[new Bezier(1, 0.8, 0.2, 0), 0]])),
],
});
Burst Emission
You can configure burst emissions to emit particles at specific times:
const burstSystem = new ParticleSystem({
// ... other settings
emissionBursts: [
{
time: 0, // Time in seconds when the burst occurs
count: new ConstantValue(20), // Number of particles to emit
cycle: 1, // Number of times to repeat the burst
interval: 0.1, // Interval between cycles
probability: 1, // Probability of the burst occurring
},
{
time: 1,
count: new ConstantValue(10),
cycle: 1,
interval: 0.1,
probability: 0.5,
},
],
});
Serialization
Particle systems can be serialized to and from JSON:
// Convert a particle system to JSON
const json = particleSystem.toJSON(metadata, {
useUrlForImage: true,
});
// Create a particle system from JSON
const loader = new QuarksLoader();
const loadedSystem = loader.parse(json);
Events
The ParticleSystem
class provides events to track its lifecycle:
// Listen for when the particle system stops emitting
particleSystem.addEventListener("emitEnd", (event) => {
console.log("Emission ended");
});
// Listen for when a particle dies
particleSystem.addEventListener("particleDied", (event) => {
console.log("Particle died", event.particle);
});
// Listen for when the particle system is destroyed
particleSystem.addEventListener("destroy", (event) => {
console.log("Particle system destroyed");
});
Example: Complete Fire Effect
Here’s a complete example of a fire effect using many features of the ParticleSystem
:
const fireSystem = new ParticleSystem({
duration: 5,
looping: true,
prewarm: true,
// Emit from a cone shape pointing upward
shape: new ConeEmitter({
radius: 0.5,
angle: Math.PI / 8,
thickness: 0.8,
}),
// Emission settings
emissionOverTime: new ConstantValue(50),
emissionBursts: [
{
time: 1,
count: new ConstantValue(10),
probability: 0.8,
},
],
// Initial particle properties
startLife: new IntervalValue(1, 2),
startSpeed: new IntervalValue(2, 5),
startSize: new IntervalValue(0.5, 1.0),
startColor: new ConstantColor(new Vector4(1, 0.5, 0.1, 1)),
// Rendering settings
worldSpace: true,
material: new MeshBasicMaterial({
map: fireTexture,
transparent: true,
blending: AdditiveBlending,
}),
renderMode: RenderMode.BillBoard,
// Behaviors controlling how particles evolve
behaviors: [
// Size increases then decreases
new SizeOverLife(new PiecewiseBezier([[new Bezier(0.5, 1, 1, 0), 0]])),
// Color changes from yellow to red to black
new ColorOverLife(
new Gradient(
[
[new Vector3(1, 0.8, 0.2), 0],
[new Vector3(1, 0.2, 0.1), 0.5],
[new Vector3(0.1, 0.1, 0.1), 1],
],
[
[1, 0],
[0.8, 0.5],
[0, 1],
],
),
),
// Upward force
new ApplyForce(new Vector3(0, 5, 0), new ConstantValue(1)),
// Rotation
new RotationOverLife(new IntervalValue(-Math.PI, Math.PI)),
// Turbulence
new TurbulenceField(
new Vector3(2, 2, 2),
2,
new Vector3(1, 2, 1),
new Vector3(0.1, 0.1, 0.1),
),
],
});
Best Practices
-
Use Batched Rendering: Always add particle systems to a
BatchedRenderer
for optimal performance. -
Memory Management: Call
dispose()
on particle systems when they’re no longer needed. -
Particle Count: Be mindful of the number of particles and their complexity, especially for mobile devices.
-
World vs. Local Space: Use
worldSpace: true
when particles need to persist independent of the emitter’s movement. -
Soft Particles: Enable
softParticles
when particles need to blend smoothly with scene geometry (requires a depth texture). -
Value Generators: Use appropriate value generators to create variation between particles.
-
Behaviors: Use behaviors to create dynamic, realistic particle motion and appearance.
Next Steps
- Learn about different Emitter Shapes for controlling where particles spawn
- Explore various Behaviors for controlling particle evolution
- Understand Value Generators for creating dynamic particle properties
- Learn about BatchedRenderer for optimizing particle system performance