A Simple Particle System with ArrayFire

Stefan ArrayFire Leave a Comment

It's the 4th of July today and we're celebrating at ArrayFire! The 4th of July implies fireworks, and fireworks obviously imply particle systems. Particle systems are a collection of many small images or points that can be rendered to represent some complex behaving object. So before we can launch our fireworks, we will need to create a particle system. The large number of particles in a system lends well to GPU computation. Thankfully, ArrayFire's easy to use interface will allow us to do this simply and efficiently. First, let's examine the structure of a typical particle system.

Particle System Structure and Hierarchy

Individual particles in a system typically have a variety of properties that govern their individual behavior. A non-comprehensive list below summarizes some of these:

  • position
  • velocity
  • acceleration
  • lifespan
  • color
  • size
  • rotation

The properties are used to simulate the particles motion and appearance. Together, a collection of particles forms a particle system. The particle system holds additional information about the system as a whole. For example, these properties can include:

  • position of the particle system center of mass
  • total # of particles
  • type of particle system
  • image to draw
  • # of active particles
  • particle system bounding box

The particle system is responsible for keeping track of all of its particles. It simulates and updates their positions. It draws the particles to the screen. It also keeps track of which particles are alive or need to be respawned.
A single scene might have multiple particle systems at once. Fireworks wouldn't be as exciting if there could only be one explosion at a time, so we'll also need a particle system manager. The manager will keep track of active particle systems and make sure each one of them is updated at the appropriate time. Particle systems can be added and removed from the manager and the manager will keep track of the systems in memory. These functional requirements create a straightforward blueprint for a small collection of classes and functions required to make a particle system.

Basic classes and data structures

Starting from the top down, let's create a particle manager:
As we planned, the particle manager supports:

  • adding particle systems
  • (automatically) removing particle systems
  • updating particle systems
  • rendering particle systems

Our actual particle system is still undefined. We want the particle system to implement the updating and rendering functions required by the manager while holding data to keep track of the particle system as a whole. We also want our basic particle system class to be generic so we can extend its functionality to match the complexity of any imaginable particle system.

So far, our particle system is actually missing our particles! In a more traditional approach, we would need to dynamically allocate space for our particles as they come alive and free space as they disappear. The frequent allocations would not work well with a GPU approach or with ArrayFire. Instead, we will be managing a data-structure that is known as a pool. Instead of frequently re-allocating space as each particle appears/disappears, we will instead allocate one large array with the maximum number of particles and keep track of what particles need to be updated. The pool approach maps directly to an af:array. This frees up our need to manage memory and puts all of the heavy lifting on ArrayFire. We will add several pools to our particle system to keep track of the particles' properties:

The key array that lets us use a pool approach is the is_active  array. It will be used as a mask array to update only the particles which are active. Now, we can make our generic functions slightly more useful by addressing the newly added particles.

First, we initialize af::arrays which represent the particles in the constructor. Then we add a method to move the particle system as a whole. The update function shows the key use of the is_active  array where the is_active array is used to index into the positions of the active particles and update only those particles.

Constructing a specific particle system

Now that we have these generic classes we can attempt to make something more concrete, like an explosion! To do this, we will inherit from our basic particle system and mainly implement the rendering function specific to our firework explosion.

In this class, we start by setting up the initial conditions of our explosion particle system. This uses the af::constant and af::randn functions to set the particles' positions and speeds. We set all of the pooled particles to be active from the very beginning and specify the type of our system in case we need to modify it from the manager.
Our update function keeps track of the lifetime of our particles and also limits their maximum speed.
The render function is fairly similar to the update function with regards to the use of the is_active array. Once again, this allows drawing only the active particles. To make our fireworks fade away, we vary the intensity of each particle with respect to its lifetime.

Now that we have a decent particle system and manager, let's set up the framework code and use our shiny new classes.

And Boom! You should have a working particle system.
In our full code, we added one more particle system which shows additional ways the particle manager can be used to manage particle systems. Check it out at this github repository.