Introduction to Particle Systems

This tutorial will walk you through Cesium’s particle system API and how to add special effects like smoke, fire, and sparks to your Cesium app.

Final particle system

What is a Particle System?

Particle systems are a graphical technique that simulates complex physically-based effects. Particle systems are collections of small images that when viewed together form a more complex “fuzzy” object, such as fire, smoke, weather, or fireworks. These complex effects are controlled by specifying the behavior of individual particles using properties such as initial position, velocity, and lifespan.

Particle system effects are common in movies and video games. For example, to represent damage to a plane, a technical artist could use a particle system to represent an explosion on the plane’s engine and then render a different particle system representing a smoke trail from the plane as it crashes.

Particle System Basics

Let’s take a look at the code for a basic particle system.

var particleSystem = viewer.scene.primitives.add(new Cesium.ParticleSystem({
    // Particle appearance
    image : '../../SampleData/fire.png',
    imageSize : new Cesium.Cartesian2(20, 20);
    startScale : 1.0,
    endScale : 4.0,
    // Particle behavior
    particleLife : 1.0,
    speed : 5.0,
    // Emitter parameters
    emitter : new Cesium.CircleEmitter(0.5),
    emissionRate : 5.0,
    emitterModelMatrix : computeEmitterModelMatrix(),
    // Particle system parameters
    modelMatrix : computeModelMatrix(),
    lifetime : 16.0
}));

The resulting particle system looks like this:

Basic particle system

The above code creates a ParticleSystem, an object parameterized to control the appearance and behavior of individual Particle objects over time. Particles, which are born from a ParticleEmitter, have a position and type, live for a set amount of time, and then die.

Some of these properties can be dynamic. Notice for example that instead of using the available single color property color, there is a startColor and endColor. These allow the user to specify that the color of the particles be blended smoothly between these two colors over the course of the particle’s life. startScale and endScale work similarly.

Other ways of affecting the visual output of this system include the maximum and minimum attributes. For every variable with a maximum and minimum input, the actual value for that variable on the particle will be randomly assigned to be between the maximum and minimum input and stay statically at that value for the entire life of the particle. For example, when creating a particle with an initial speed, the user can either just use the speed variable directly or specify the minimumSpeed and/or maximumSpeed as bounds for the actual randomly chosen speed. Attributes that allow for changes like this include imageSize, speed, life, and particleLife.

A wide variety of different effects can be created by changing these parameters – Try it in our Particle System demo.

Mastering Cesium particle systems is just a matter of getting familiar with the different parameters. Let’s discuss these particle system properties in more detail.

Emitters

When a particle is born, its initial position and velocity vector are controlled by the ParticleEmitter. The emitter will spawn some number of particles per second, specified by the emissionRate parameter, initialized with a random velocity dependent on the emitter type.

Cesium has various ParticleEmitter’s that you can use out of the box.

BoxEmitter

The BoxEmitter class initializes particles at randomly-sampled positions within a box and directs them out of one of the six box faces. It accepts a Cartesian3 parameter specifying the width, height and depth dimensions of the box.

particleSystem : {
    image : '../../SampleData/fire.png',
    emissionRate: 50.0,
    emitter: new Cesium.BoxEmitter(new Cesium.Cartesian3(10.0, 10.0, 10.0))
}

Particle system with a BoxEmitter

CircleEmitter

The CircleEmitter class initializes particles at randomly-sampled positions within a circle in the direction of the emitter’s up axis. It accepts a single float parameter specifying the radius of the circle.

particleSystem : {
    image : '../../SampleData/fire.png',
    emissionRate: 50.0,
    emitter: new Cesium.CircleEmitter(5.0)
}

Particle system with a CircleEmitter

If no emitter is specified, a CircleEmitter will be created by default.

ConeEmitter

The ConeEmitter class initializes particles at the tip of a cone and directs them at random angles out of the cone. It takes a single float parameter specifying the angle of the cone. The cone is oriented along the up axis of the emitter.

particleSystem : {
    image : '../../SampleData/fire.png',
    emissionRate: 50.0,
    emitter: new Cesium.ConeEmitter(Cesium.Math.toRadians(30.0))
}

Particle system with a ConeEmitter

SphereEmitter

The SphereEmitter class initializes particles at randomly-sampled positions within a sphere and directs them outwards from the center of the sphere. It takes a single float parameter specifying the radius of the sphere.

particleSystem : {
    image : '../../SampleData/fire.png',
    emissionRate: 50.0,
    emitter: new Cesium.SphereEmitter(5.0)
}

Particle system with a SphereEmitter

Configuring Particle Systems

Cesium has numerous options for fine-tuning particle behavior.

Particle emission rate

The emissionRate property controls how many particles are emitted per second, which changes the density of particles in the system.

You can also specify an array of burst objects to emit bursts of particles at specified times (as demonstrated in the animations above). This is a great way to add some variety or explosions to your particle system.

Add this property to your particleSystem.

bursts : [
    new Cesium.ParticleBurst({time : 5.0, minimum : 300, maximum : 500}),
    new Cesium.ParticleBurst({time : 10.0, minimum : 50, maximum : 100}),
    new Cesium.ParticleBurst({time : 15.0, minimum : 200, maximum : 300})
],

These bursts will emit between min and max particles at the given times.

Life of the particle and life of the system

A few properties control the lifetime of a particle system. By default, particle systems will run forever.

To make a particle system to run for a set duration, set the lifetime property to the desired duration in seconds and set the loop property to false. For example, to run a particle system for 5 seconds use:

particleSystem : {
    lifetime: 5.0,
    loop: false
}

Setting particleLife to 5.0 will set every particle in the system to have that value for particleLife. To randomize the output for each particle a bit more, the user can use the variables minimumParticleLife and maximumParticleLife. For example, to make particles live between 5 and 10 seconds, use:

particleSystem : {
    minimumParticleLife: 5.0,
    maximumParticleLife: 10.0
}

Styling particles

Color

In addition to the base particle texture specified with the image property, particles can be styled with a color, which can change over the particle’s lifetime. This helps create more dynamic-looking effects.

For example, the following code will make the fire particles reddish when they are born and then transition to a partially transparent yellow as they die:

particleSystem : {
    startColor: Cesium.Color.RED.withAlpha(0.7),
    endColor: Cesium.Color.YELLOW.withAlpha(0.3)
}

Size

The general size of a particle is controlled with the imageSize. If the user chooses to randomize the particles a bit more, each particle can also be born with a width in pixels between minimumImageSize.x and maximumImageSize.x and a height between minimumImageSize.y and maximumImageSize.y.

This will create particles with size between 30 and 60 pixels:

particleSystem : {
    minimumImageSize : new Cesium.Cartesian2(30.0, 30.0),
    maximumImageSize : new Cesium.Cartesian2(60.0, 60.0)
}

Like color, the size of a particle can be modulated over its lifetime with the startScale and endScale properties. This lets you make particles grow or shink over time.

This code will make the particles grow to 4x their start size as they age:

particleSystem : {
    startScale: 1.0,
    endScale: 4.0
}

Speed

While the emitter controls the initial position and direction of the particles, speed is controlled by just the speed or by both the minimumSpeed and maximumSpeed settings. Let’s make our particles go between 5 and 10 and meters per second:

particleSystem : {
    minimumSpeed: 5.0,
    maximumSpeed: 10.0
}

UpdateCallback

To add to the realism of a effect, particle systems can also apply an update function. This can act as a manual updater for the user to make changes to each particle due to gravity or wind, apply color changes outside of the simple linear interpolating, etc.

Each particle system has an updateCallback that modifies properties of the particle during the simulation. This callback is a function that takes in a particle and a simulation time step. Most physically-based effects will modify the velocity vector to change direction or speed. Here’s an example that will make particles react to gravity:

var gravityScratch = new Cesium.Cartesian3();
function applyGravity(p, dt) {
    // Compute a local up vector for each particle in geocentric space.
    var position = p.position;

    Cesium.Cartesian3.normalize(position, gravityScratch);
    Cesium.Cartesian3.multiplyByScalar(gravityScratch, viewModel.gravity * dt, gravityScratch);

    p.velocity = Cesium.Cartesian3.add(p.velocity, gravityScratch, p.velocity);
}

This function computes a gravity vector and uses the acceleration of gravity (-9.8 meters per second squared) to alter the velocity of the particle.

Now set the force as the particle system’s updateFunction like this:

particleSystem: {
    forces: applyGravity
}

Positioning

Particle systems are positioned using two Matrix4 transformation matrices:

  • modelMatrix : transforms the particle system from model to world coordinates.
  • `emitterModelMatrix : transforms the particle system emitter within the particle system’s local coordinate system.


You can use just one of these transformation matrices and leave the other as an identity matrix, but we provide both for convenience. To practice creating the matrices, let’s walk through the process of positioning our particle emitter relative to another entity.

First let’s create an entity for our particle system to accent. Open the Hello World Sandcastle example and add the following code:

var entity = viewer.entities.add({
    // Load the Cesium plane model to represent the entity
    model : {
        uri : '../../SampleData/models/CesiumAir/Cesium_Air.gltf',
        minimumPixelSize : 64
    },
    position : Cesium.Cartesian3.fromDegrees(-112.110693, 36.0994841, 1000.0)
});
viewer.trackedEntity = entity;

This will create a plane model centered in the viewer.

Next, let’s determine how to place the particle system within our scene. In this case, let’s add a fire effect coming from one of the plane engines. First let’s create a model matrix that will position and orient the particle system the same as the plane entity. This means that we’ll want to use the plane’s model matrix for the particle system as well, can get the modelMatrix like this:

function computeModelMatrix(entity, time) {
    var position = Cesium.Property.getValueOrUndefined(entity.position, time, new Cesium.Cartesian3());
    if (!Cesium.defined(position)) {
        return undefined;
    }
    var orientation = Cesium.Property.getValueOrUndefined(entity.orientation, time, new Cesium.Quaternion());
    if (!Cesium.defined(orientation)) {
        var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(position, undefined, new Cesium.Matrix4());
    } else {
        modelMatrix = Cesium.Matrix4.fromRotationTranslation(Cesium.Matrix3.fromQuaternion(orientation, new Cesium.Matrix3()), position, new Cesium.Matrix4());
    }
    return modelMatrix;
 }

Now we have a matrix that places our particle system at the center of the plane. We want the emitter positioned at one of our plane engines, so we can create a matrix with no rotation and a translation to the engine in model space. We can compute that transformation matrix like this:

function computeEmitterModelMatrix() {
    hpr = Cesium.HeadingPitchRoll.fromDegrees(0.0, 0.0, 0.0, new Cesium.HeadingPitchRoll());
    var trs = new Cesium.TranslationRotationScale();
    trs.translation = Cesium.Cartesian3.fromElements(2.5, 4.0, 1.0, new Cesium.Cartesian3());
    trs.rotation = Cesium.Quaternion.fromHeadingPitchRoll(hpr, new Cesium.Quaternion());
    return Cesium.Matrix4.fromTranslationRotationScale(trs, new Cesium.Matrix4());
}

Now that we have the means to compute our transformation matrices, we can go ahead and create the particle system with some basic parameters. Here’s the code:

var particleSystem = viewer.scene.primitives.add(new Cesium.ParticleSystem({
    image : '../../SampleData/fire.png',
    startScale : 1.0,
    endScale : 4.0,
    particleLife : 1.0,
    speed : 5.0,
    imageSize : new Cesium.Cartesian2(20, 20),
    emissionRate : 5.0,
    lifetime : 16.0,
    modelMatrix : computeModelMatrix(entity, Cesium.JulianDate.now()),
    emitterModelMatrix : computeEmitterModelMatrix()
}));

This gives us the fire particle effects positioned at the engine as depicted in all our previous examples.

Positioned particle system

Also note that we can update the model or emitter transformation matrices to change over time. For instance, if we wanted to animate the emitter position on the plane, we can modify the emitterModelMatrix while leaving the modelMatrix untouched. This makes it easy to reposition the effect in model space.

This positioning method isn’t as straightforward as just specifying a position, but it provides efficient, flexible transformations that allow for a wide range of effects.

That’s it for particle system basics! We can’t wait to see what effects you’ll make with them.

For more cool effects with Particle Systems using more advanced techniques, see the Particle System More Effects Tutorial

Fireworks!

For more example code see: