Sunday, June 23, 2013

Creating a particle effects engine in Javascript and Three.js


Image of Particle Engine Example
Video of Particle Engine Examples

The engine we will describe consists of two main components: the particles themselves and the emitter that controls the creation of the particles.

Particles typically have the following properties, each of which may change during the existence of the particle:

  • position, velocity, acceleration (each a Vector3)
  • angle, angleVelocity, angleAcceleration (each a floating point number). this controls the angle of rotation of the image used for the particles (since particles will always be drawn facing the camera, only a single number is necessary for each)
  • size of the image used for the particles
  • color of the particle (stored as a Vector3 in HSL format) 
  • opacity - a number between 0 (completely transparent) and 1 (completely opaque)
  • age - a value that stores the number of seconds the particle has been alive
  • alive - true/false - controls whether particle values require updating and particle requires rendering

Particles also often have an associated function that updates these properties based on the amount of time that has passed since the last update.

A simple Tween class can be used to change the values of the size, color, and opacity of the particles.  A Tween contains a sorted array of times [T0, T1, ..., Tn] and an array of corresponding values [V0, V1, ..., Vn], and a function that, at time t, with Ta < t < Tb, returns the value linearly interpolated by the corresponding amount between Va and Vb.  (If t < T0, then V0 is returned, and if t > Tn, then Vn is returned.)

Typically, we never interact with individual particle data directly, rather, we do it through the emitter.

Our emitter will contains the following data:

  • base and spread values for all the particle properties (except age and alive).
    When particles are created, their properties are set to random values in the interval (base - spread / 2, base + spread / 2); particle age is set to 0 and alive status is set to false.
  • enum-type values that determine the shape of the emitter (either rectangular or spherical) and the way in which initial velocities will be specified (also rectangular or spherical)
  • base image to be used for each of the particles, the appearance of which is customized by the particle properties
  • optional Tween values for changing the size, color, and opacity values
  • blending style to be used in rendering the particles, typically either "normal" or additive (which is useful for fire or light-based effects)
  • an array of particles
  • number of particles that should be created per second
  • number of seconds each particle will exist
  • number of seconds the emitter has been active
  • number of seconds until the emitter stops -- for a "burst"-style effect, this value should be close to zero; for a "continuous"-style effect, this value should be large.
  • the maximum number of particles that can exist at any point in time, which is calculated from the number of particles created per second, and the duration of the particles and the emitter  
  • Three.js objects that control the rendering of the particles: a THREE.Geometry, a THREE.ShaderMaterial, and a THREE.ParticleSystem.

 The emitter also contains the following functions:

  • setValues - sets/changes values within the emitter 
  • randomValue and randomVector3 - takes a base value and a spread value and calculates a (uniformly distributed) random value in the range (base - spread / 2, base + spread / 2). These are helper functions for the createParticle function
  • createParticle - initializes a single particle according to parameters set in emitter
  • initializeEngine - initializes all emitter data
  • update - spawns new particles if necessary; updates the particle data for any particles currently existing; removes particles whose age is greater than the maximum particle age and reuses/recycles them if new particles need to created 
  • destroy - stops the emitter and removes all rendered objects
Fortunately, all the graphics processing is handled by Three.js.  There is an object in Three.js called "THREE.ParticleSystem" which allows you to assign an image to individual vertices of a geometry. The appearance of the particles is controller by a "THREE.ShaderMaterial", which requires us to write a custom vertex shader and a custom fragment shader with attributes which take all of our particle properties into account (image, angle, size, color, opacity, visibility) when rendering each particle.

To try out a live demo, go to:

Also, feel free to check out the Particle Engine source code, the parameters for the examples in the demo, and how to include the particle engine in a Three.js page.

Happy coding!