Advanced Particle System Effects
This tutorial will walk you through some more effects, such as weather and rocket blasters.
To go over the basics of a particle system, see the Introduction to Particle Systems tutorial.
The beginning of the snow tutorial follows the implementation used for NORAD Tracks Santa.
We’ll begin by explaining how to make snow, then how to convert this snow into rain.
To make a snow effect, we’ll add snowflake images for each particle and define the movement behavior and other dynamic elements of the particles in the
Regarding the images that represent each particle, we could start with images of any color: red, green, white, etc. We use PNGs because they support transparency, so the uncolored areas of the image remain invisible. The following three images are the PNGs used for the billboards to create particle system effects in these tutorials. On the left is the PNG used for the rain in this tutorial; the center image was used for the snow in this tutorial; the right image is the one used for the fire in the Introduction to Particle Systems tutorial.
Once we’ve selected an image, we can modify its appearance in Cesium, as explained below. For example, we modified the circular particle image on the left to be long and blue to look more like raindrops, as seen at the beginning of this section. The fire image can be changed to green for leaves, yellow for electric sparks, and even white for the water’s splash/foaming effect at the bottom of a waterfall. Creativity is advantageous here!
Additionally, in both the rain and snow systems, we set the starting opacity to 0 and final opacity to whatever is required visually, meaning they are completely invisible when they are first created. This is so we don’t see random particles popping up out of nowhere when they first materialize.
The Update Function
The update function is where we have the most free reign regarding the movement, arrangement, and visualization of the particles. Here we can modify simple things such as the particle’s
particleLife, etc. Use this method for as little or as much modification as needed. We can even modify the particles based on their distance to the camera (as done below), to an imported model, to the earth itself, etc.
Here is our update function for snow:
The first part of the function makes the particles fall downward as if by gravity.
As an added feature, the update function also includes a distance check that allows particles to disappear the farther away the user is from them. The farther away a particle is, the more invisible it is, allowing for an almost distance fog effect.
Additional Weather Effects
Along with the visual of particles disappearing by distance, the example also includes a fog and atmosphere effect change to match the type of weather we’re trying to replicate.
hueShift changes the color along the color spectrum. The
saturationShift changes how much color versus black and white the visual actually entails. The
brightnessShift changes how vivid the colors are.
The fog density changes how opaque the overcover on the earth is with the fog’s color. The fog minimumBrightness, being the minimum bound for how bright the fog is, acts as a way to darken the fog.
The above snow skyAtmosphere is mostly dark gray with very little coloring, and the fog is a strong white.
To actually have separate visuals, we create two different
Particle Systems, one for the snow and one for the rain.
The below system creates our particles using a sphere emitter based on the center location. Additionally, the size of the image used for each particle is randomized between the given size and twice its size to allow for more of a variance of particles.
The snow system has the following attributes and all the previous functions we discussed:
The rain system is almost exactly like snow with just a few different features.
Just like with snow, the below system creates our particles using a sphere emitter based on the center location. However, we chose a different image to represent the rain,
circular_particle.png, which we colored a shade of blue and stretched vertically to give the rain an elongated look. Unlike the snow example where the image sizes were randomized, here we set every particle to have the exact same
imageSize, which has a height that is double its width.
Additionally the rain’s update function is also slightly different in that it falls at a much faster speed than snow. The below code shows how we augmented the scalar multiplication for gravity to match this visualization, and we’re modifying
particle.position instead of
Finally, to make the environment match the mood of the scene, we modify the atmosphere and fog to match the rain. The below code makes a dark blue sky with a thin fog cover.
For additional help, see the Sandcastle example for both snow and rain.
Comet and Rocket Tails
Using Multiple Particle Systems
While the weather system examples required just one particle system, to create comet and rocket trails, we will need multiple particle systems. Each location on a ring of particles created by the examples is actually a completely separate particle system from those around it. That is, we’re creating a circle of particle systems that each emit a particle following a path out from the emitted location. This allows us to better control the direction of the movement of the systems in a more uniform fashion. An easy way to visualize this effect is to limit
cometOptions.numberOfSystems to 2 and
cometOptions.colorOptions to include just two colors, as shown in the image below. It’s easy to trace out the path of the particles of each system as they are created.
To streamline the different sets of systems, we create arrays to carry the separate systems associated with the comet versus those associated with the rocket example.
Additionally, we create two different options for objects, mostly for ease of organization; one for the comet version and one for the rocket version. This allows for a varied look between the two with different initial number of systems, offset values, etc.
colorOptions for each is an array of colors to allow for a more randomized visual. That is, rather than having a select initialization color, we have each system start with one specific color dependent on the current system being created. In the below example,
i represents the current iteration.
We use the below function as the initializer for each system:
Stepping through this system creation function,
options represents whether we are currently building a comet’s tail or a rocket’s tail. As mentioned in The Systems Collections section,
systemsArray is just so that once we create all our
ParticleSystems, we have a way of working with all the systems associated with the input
Since both tail versions are very similar to one another, we can have the same inputs for everything except for color and force, both of which change between the comet and rocket examples. Additionally, the
emitterModelMatrix is also completely separate for each system to create that “rotational offset” in which each newly created ring seems to have its particles shifted slightly offset from the previous one.
Additionally, here we’re not actually loading in an image file. Instead, we’re creating an image directly using the HTML canvas. Though we’re using it to directly draw a circle, this function makes the image creation a lot more malleable. For example, by adding a parameter to
getImage that stands for the current iteration, we can have each image be created slightly differently depending on the parameter, allowing for multiple different visual outputs.
Create the Particle Image from Scratch
Now that we have the main idea of what we want to build, we also need a way to actually visualize the particles being created. Instead of loading in an image as before, here we create the image. This approach removes the dependency on loading a file, allowing for a more code-based approach.
Move the Particles in the Systems
Hold onto your seats, we’re getting to the exciting part of actually moving the particles. Here is where we need to fill in our
But, what’s this? This function doesn’t match the name of what’s actually input into the particle system. When we created our particle system before, we filled in
var force = forceFunction(options, i);. This calls the below helper function that returns our actual force function.
iterationOffset) whenever the function is called by a particle in the system. To fix this, we created a helper function that returns the proper function we need.
The Force Function Deconstructed
Now what does our
forceFunction, actually do? Just as with
createParticleSystems, where we create each system with a circular offset from the one before it, we also want each particle to be updated in that direction to provide an even further circular effect as the system moves away from its initialization point.
The iteration offset on the particle not only creates the spinning look but also allows for an either smooth or rougher looking spinning, as seen comparing both the comet and rocket visual. Instead of setting the new position to the cosine and sine of the newly calculated angle, we actually add this new amount to the previous position. Therefore, smaller iteration offsets won’t adjust the angle enough, allowing the radius to grow steadily larger as the system continues. In turn, larger iteration offsets will change the angle much faster and added to the original position; this will make a much tighter, jittery, and more cylindrical output such as in the rocket example.
In this tutorial, we’re mostly using just sine and cosine functions for circular effects; however, the user can take these a step further making shapes such as the Lissajous curve, the Gibbs phenomenon, or even creating a square wave as needed. Additionally, the user can scrap trigonometry altogether and do position-based noise manipulations on the particle’s movement and even more fun stuff. This is where the user’s ingenuity will shine!
Just as we did in the original Particle Systems Tutorial, now that we have the effect we want to produce, we want to incorporate it in its proper location behind the plane. Since our systems are vertical, to get the proper positioning of the system relative to the plane, we need to do a slight offset by using our
particleOffset value. We use this to create our
particlesModelMatrix to act as each system’s overall global positioning matrix. As shown in our
createParticleSystems function, for each system we create, we fill in the
emitterModelMatrix with the offset calculated depending on which iteration in the circle this system is creating.
For additional help see the Sandcastle example for both tails examples.
For more example code, see: