I've always been interested in computer graphics and embedded systems. So when I got a Raspberry Pi for $35 with its high-resolution HDMI video interface, I knew it would make a good platform for experimentation. Since then, I have written about how to setup the Raspberry Pi (Nuts & Volts magazine, March 2013), have made it into an Internet Radio/Music player (Nuts & Volts magazine, August 2013), and have performed numerous experiments in computer graphics. In this article, I describe and demonstrate the particle system code I developed for the Raspberry Pi. From this point forward, I will refer to the Raspberry Pi by its typical abbreviation, RPi. My RPi setup and coding process are described at the end of this article.
To quote Wikipedia, a particle system refers to a "computer graphics technique that uses a large number of very small sprites or other graphic objects to simulate certain kinds of 'fuzzy' phenomena, which are otherwise very hard to reproduce with conventional rendering techniques usually highly chaotic systems, natural phenomena, and/or processes caused by chemical reactions." Examples include fire, explosions, smoke, dust, clouds, rain, sparks, meteors, and many others visual effects characterized as being fluid and dynamic. Similar to fractals, particle systems are an example of stochastic procedural modeling, which allows complex-looking effects to be created with minimal amounts of effort.
Particle systems were invented by William (Bill) Reeves while working at LucasFilm Ltd. and were used in the Star Trek movie "The Wrath of Khan" for the Genesis Effect. Bill received a Technical Academy Award for his invention. His seminal 1983 paper, Particle System: A Technique for Modeling a Class of Fuzzy Object [PDF], introduced the world to his technique.
In this article, I describe basic operations of a particle system, the RPi Particle System code I have written. I provide links to videos showing the RPi running some of my particle system experiments, and I explain how to set up your RPi for reproducing my results.
Note: The RPi lacks sufficient computing power to produce movie-quality special effects but it can produces some beautiful, artistic effects in real time, which was exactly what I was after. Since computing power is the limiting factor, over-clocking the RPi helps quite a bit. I have my RPi running at 900MHz with no discernible side effects other than increased performance.
The Particle System
My particle system is made up of three major components: particles, which are the visible entities displayed on the RPi screen; emitters, which create and parameterize the particles in "fuzzy" ways; and manipulators, which modify the behavior of groups of particles over time.
This particle system code creates an animation frame-by-frame and renders it onto the RPi's screen in real time. For every frame:
- All the emitters in the system are run to see if they need to create new particles.
- All manipulators in the system are run and their effect on all of the current particles is calculated and applied.
- All alive particles in the Particle System are examined and the effects of aging on their position, their size, and their color are applied. If it is determined a particle has exceeded its lifetime, it is removed from the system and its memory reclaimed.
- All updated particles are written to the display and the process starts over.
I should mention I coded this particle system in Objective-C and use the Simple Directmedia Layer (SDL) for particle display on the RPi, but more on that later. The code accompanying this article is available for download.
All particles have the same basic behavior, which is coded into the
BaseParticle class (BaseParticle.h and BaseParticle.m in the code). Subclassing
BaseParticle, I created high-level particle classes for
ImageParticles. The names of these classes tell you what kind of particles they are, and how they are visually represented.
All particles have a
color (including transparency), and
size in addition to more esoteric attributes such as:
blank,which, if set, causes the previous position of a particle on screen to be erased before its new position is drawn. Not setting this attribute will cause particles to leave trails mapping out their trajectories over time.
colormode, which determines how and if a particle's color changes over its lifetime.
colorvariability, which, if set, allows the color of the particle to change over its lifetime.
filled, which, if set, causes the geometric particles (circles, rectangles, and triangles) to be filled with color. If not set, only the particle outline is displayed.
sizevariability,which, if set, allows the size of the particle to change over its lifetime.
Every particle type must implement the
ParticleProtocol and provide the following methods:
- (void) renderParticleUsingSDL: (SDLObject *) sdlObject andGFX: (SDLgfx *) sdlGfx;
- (void) eraseParticleUsingSDL: (SDLObject *) sdlObject andGFX: (SDLgfx *) sdlGfx BGColor: (uint32_t) bgColor;
- (void) positionAndSize: (SDL_Rect *) pRect;
- (BOOL) update;
renderParticleUsingSDL method is called by the Particle System to make the particle render itself on the screen at its current position. This is done with the help of the system-wide
sdlGfx objects. The
eraseParticleUsingSDL method causes the particle to erase its previous position on the screen if the particle's
blank attribute is set; it is a
nop otherwise. The
positionAndSize method is used to inform manipulators of the particle's current whereabouts and its size. Finally, the
update method causes a particle to recalculate its color and size, updates its position using its previous position and velocity, and to decrement its lifetime value.
Particles are given their marching orders by their emitter. Particles carry out those orders without question until the end of their lifetimes. Particles die and are removed from the particle system at end of life or when they leave the RPi's display window and are no longer visible.
In general small numbers of particles can be used for artistic purposes, while large numbers of particles are generally required for realistic effects, such as smoke and fire.
All emitters have the same basic behavior that is coded into the
BaseEmitter class (see BaseEmitter.h and BaseEmitter.m in the code). Emitters create particles and give them "fuzzy" behavior by randomly selecting attribute values within established limits. For example, an emitter might be designed to emit particles only within +/- 45 degrees of vertical. In this case, the emitter's
min angle attribute would be set to
-45 and its
max angle attribute to
+45. The emitter would randomly pick a value between these limits (say, -21 degrees) and assign it as a velocity to the particle it emits. If the emitter emits a bunch of particles, they will all fall between these directional limits.
Configurable emitter attributes include:
- Location and size of the emitter itself. Particles can be emitted from a point source if emitter width and height are set equal to 1 or emitted randomly from a rectangular area of specified width and height centered at this location.
- Min and max lifetime values in frames.
- Min and max angle values as described above.
- Min and max speed values, which determine how fast an emitted particle moves across the display.
- Start and end sizes for the particles.
- Start and end colors for the particles.
Emitters also have an
EMITTER_MODE enumeration consisting of the following values:
EMITTER_OFF, EMITTER_SEQUENTIAL, EMITTER_METERED, EMITTER_SINGLE, EMITTER_ONESHOT.
EMITTER_OFFAn emitter in this mode will not emit any particles until its mode changes.
EMITTER_SEQUENTIALIn this mode, an emitter emits a single particle each frame.
EMITTER_METEREDIn this mode, an emitter emits a specified number of particles per frame. This is controlled by the emitter's
EMITTER_SINGLEIn this mode, an emitter will emit a single particle and then do nothing until the particles dies. At that point, it will emit another single particle.
EMITTER_ONESHOTIn this mode an emitter will emit all of its available particles in a single frame. This mode is good for creating explosions.
Currently, a hard limit of
MAX_PARTICLES (300) is assigned to each emitter. This is the maximum number of live particles an emitter can dispense. This constraint was imposed for performance reasons on the RPi. As particles die, they inform their emitter, so the emitter can create and emit another particle in its place.
- (void) setBaseAttributesOnParticle: (BaseParticle *) bp;
BaseEmitter is what picks the fuzzy values assigned to newly created particles.
There are emitters for each particle type and a special
ShapeEmitter, which randomly emits pixel, circle, triangle, and rect particles. An example of its use will be shown a little later.
All manipulators have a unique
manipulatorID that is assigned at time of creation. This ID is optionally passed to individual particles via the emitter they were created with. All manipulators are subclasses of
BaseManipulator and all implement the following method:
- (void) manipulateParticles: (MutableArray *) particles;
The actions performed in this method depend upon the functionality implemented for the manipulator. Every frame the Particle System passes its collection of active particles to each manipulator registered with the system. A check is made within this method to determine if a particle has a registered interest in the operation of this manipulator, and if not, the particle is ignored. If a particle has this manipulator ID, the manipulator processes the particle. In this way, many manipulators can be defined and registered within the particle system, but they will operate only on the particles with which they have an association.
The following manipulators are currently defined:
BoundaryManipulatorcauses the particle emitted into the area it defines to bounce off of the boundary like pool balls hitting a rail.
VortexManipulatorcauses particles to be pulled towards a vortex and subsequent death.
XYForceManipulatorcan simulate forces like friction and gravity.