Chapter 3. Building a Simple Particle System

Particle systems are used in computer graphics for drawing fuzzy-shaped objects such as fire, clouds, and trails of dust. The basic idea for such systems is drawing a large number of small, moving particles and controlling their motion.

Here we consider the basic principles of modeling and drawing particle systems and demonstrating them by building a simple 2D particle system. In this chapter, we will cover the following topics:

  • The basics of particle systems
  • A single particle
  • An emitter
  • The attraction, repulsion, and spinning forces
  • Graphical user interface

By the end of this chapter, you will have a fully-featured project for experimenting with the particle system.

Note

This is the only chapter where we create custom C++ classes.

The basics of particle systems

Objects such as clouds and fire have no distinct shape, so it is hard to draw them using polygons. The novel method for drawing such objects was proposed by William T. Reeves in his article, Particle Systems—a Technique for Modeling a Class of Fuzzy Objects (ACM Transactions on Graphics, April 1983). The idea is in using particle systems, which are controllable sets of particles—small independently moving objects, considered as elementary components of the rendered object.

Today, particle systems play an important role in 2D and 3D computer graphics as a tool for photorealistic rendering of real-world fuzzy objects. Also, they are widely used for experimental and creative coding graphics.

Particles are independent objects that move according to some rules such as gravity, force, and friction. Each particle has a number of attributes such as position, velocity, lifetime, size, and color that changes with time.

The most important property of each particle system is an interaction type between the particles. It determines the kinds of objects and behaviors, which can be represented by the particle system, and designates methods of its physical modeling.

Interaction types

The frequently used interaction types are as follows:

  • No interaction between particles: In this case, each particle can have a limited or an infinite lifetime. New particles can be generated from some point or region called emitter. Also, points can attract to or repel from some points or regions. This interaction type is appropriate for modeling sparse objects such as clouds, fire, traits, and also fireworks. Actually, this type was considered by William Reeves in his article and is considered later in this chapter.

    You can play with a particle system consisting of a fixed number of particles with infinite lifetime in the openFrameworks example, examples/math/particlesExample. Use keys 1, 2, 3, and 4 for switching between several modes of the project; in these modes, particles will attract or repel from the mouse position, get attracted to some random points on the screen, or just fall like snowflakes.

  • Particles attract to and repel from other particles: In this case, the attraction and repulsion forces between two particles usually depend on the distance between them. For example, particles that are far attract, and particles that are closer repel. Such particle systems are used for modeling micro or macro physical systems such as molecules or galaxies and also for modeling the flocks.

    Tip

    Number of particles' pairs grow in a square law of particles' number. For example, if we have a particle system with 10,000 particles, there are 10,000 × 9,999 / 2 ~ 50 millions of particles' pairs. So performing direct calculations of all possible pairs' interactions is very inefficient, and methods such as geometric hashing are always used for computations' speedup.

  • Particles interact in a complex way, indirectly, through some underlying nonstationary field. In this case, the field affects the particles' velocity, and (in some models) particles can affect the field itself. The most widely known example of such an interaction is fluid mechanics, modeled by the Navier–Stokes equations. Fluid mechanics is quite complex to implement and consumes a lot of computational resources, but it exhibits behaviors (that are impossible in simpler interaction types), such as vortices and turbulence. Particle systems using this interaction are widely used in 2D and 3D graphics for photorealistic modeling of smoke, water, and many other objects, and, of course, for experimental graphics.

    Note

    In openFrameworks, there exists an excellent implementation of fluid mechanics in an addon, ofxMSAFluid, by Memo Akten. You can download it from ofxaddons.com. See Appendix A, Working with Addons, for details on addons.

Particle systems are quite huge objects, so computing and rendering them can be a challenging task. In the next two subsections, we will consider various methods for doing it.

Computing particles' physics

Usually, each particle in a particle system is constantly moving. Hence, before each rendering step, we need to recompute the position, velocity, size, color, and other attributes using the chosen physical modeling method. The algorithmic complexity of such recomputing linearly depends on the number of particles.

For achieving high-quality graphics, particle systems should consist of thousands and even millions of particles. So computing particles' physics is often a resource-consuming task that affects the structure of the whole project. There are several schemas of organizing such computing. They are as follows:

  • Single core computing: This performs all the computing in the testApp::update() function. This is the simplest method, which uses a single CPU's core. It lets us operate in openFrameworks with 10,000 to 40,000 particles at 60 FPS. This method is used in projects where the number of particles is in the specified range. Also, it is often used for prototyping a project.
  • Multiple core computing: This divides a particle system into several smaller subsystems and processes each of them in a separate thread. The operating system automatically distributes the threads' execution among all the available CPU's cores. This is the simplest way for revealing the power of all your CPUs' cores and to speedup the calculations. For doing this in openFrameworks, use the ofThread class (see its usage in openFrameworks' example, examples/utils/threadExample).

    The speedup in this case highly depends on a number of available cores and the speed of a separate core. A typical PC has 4 to 16 cores working at 2 to 3 GHz, and the Intel Xeon Phi coprocessor has 60 cores working at 1 GHz. So, in principle, it is possible to compute a million particles in real time.

Until now we have considered CPU-based methods. All other methods are GPU-based and let us operate easily with 100,000 to 1,000,000 particles (depending on the video card). Let us see these methods:

  • Using vertex shaders: We use this to set the initial positions of particles in the ofMesh object and then apply the vertex shader for changing its position in time. This method is simple and works fast but is limited. It creates non-interactive particles that fly just by predefined trajectories (specified by the vertex shader). See Chapter 7, Drawing in 3D, and Chapter 8, Using Shaders, for details on the ofMesh class and shaders.

    Note

    It is possible to create interactive particles that change trajectories depending on the changing control parameters (such as the attractor position). To do it, you need to use a vertex shader with OpenGL's Transform Feedback feature.

  • Using fragment shaders: This is used to represent each particle by a pixel in texture. For example, four color components (red, green, blue, and alpha) can hold the x and y coordinates of position and velocity of a particle. We then use a fragment shader for the corresponding processing pixels of the texture using the Ping-Pong FBO method. This method is quite simple, works fast, and can be used for computing particles without interaction and for moving particles in the fluid mechanics model (without computing the field). If you need to use more parameters for representing a particular particle, just use several textures. Each texture gives four additional float parameters.

    Such a method is implemented in the openFrameworks' example, examples/gl/gpuParticleSystemExample.

  • Using compute shaders: Compute shaders are used for universal computations, and they let you perform advanced particles' modeling. See a demonstration of this technology at Stan Epp's video at youtube.com/watch?v=jwCAsyiYimY. The description of this video contains a link to the project's source codes.
  • Using other GPU technologies: Most advanced GPU-based technologies are OpenCL and NVIDIA CUDA. You can use them for performing the most complicated computations. Note, if you are a novice in these technologies, adopting them in the openFrameworks project can require some effort from you.

Rendering particles

Visually, a particle system is a large number of small homogeneous objects called particles, which are drawn using different color and size but have quite a simple shape. There are several ways to render a particle system on the screen:

  • Drawing each particle as a primitive (circle, triangle, or star): We do this by using functions such as ofCircle() or ofTriangle(). This way is the simplest, but works slowly, because each drawing command is sent separately in the video card. This method performs well only when the number of particles is small (from 1,000 to 10,000, depends on a video card).
  • Drawing each particle as a sprite: Here we use one image or array of images having the type ofImage or ofTexture (see Chapter 4, Images and Textures). They represent all possible particles' shapes. We draw each particle using the image.draw( x, y, w, h ) function. This method is as slow as the previous one, but the resulting picture can be more expressive because it lets you create complex and blurred shapes. Also it is possible to use image sequences for creating animated particles such as flying moths (see the Using image sequence section in Chapter 5, Working with Videos).
  • Drawing each particle as a sprite: We do this using ofMesh or ofVboMesh (See details on using ofMesh and ofVboMesh in the Using ofMesh section in Chapter 7, Drawing in 3D). Compared to the previous methods, this method works much faster because all the drawing commands are stored in a single array and are sent to the video card at once. Using this method, you can draw 10,000 to 1,000,000 particles (depends on the video card).

    In this method, you need to tile all the desired particles' images in one "big" image, and then use the mesh object for representing all the particles as quads with specified texture coordinates. Note, in this method, you need to specify four quad corners for each particle, which is a CPU-consuming task.

All the preceding methods can be used with CPU-based computing methods (single-core computing and multiple-core computing). Now we will consider the two most powerful methods that can be used successfully used with all the described methods of computing:

  • Using point sprites: Point sprites is a method of drawing images by specifying only their center. Compared with using quads (described in previous item), it is a simple and also an efficient method but is limited by its expressive capabilities, for example, you cannot use different images for particles and rotate them. Though, you can change the size and color of the particles, which is enough in many projects.

    See openFrameworks' example, examples/gl/pointsAsTextures, where this method is used for drawing particles with different sizes. (For changing sizes, it uses a vertex shader; see details about shaders in Chapter 8, Using Shaders).

  • Using a geometry shader: This method is as fast as point sprites but is free of its limitations—you can draw particles using different sprites and rotate them. In this method, you represent each particle as a vertex and specify its needed attributes such as the drawing position, index of using sprite, color, angle and size and then pass it in the geometry shader. (You can pass a particle's velocity too for affecting the shape of the particle on the screen.)

    The shader translates each particle in a quad that is rendered on the screen.

    Note that a geometry shader can also draw particles not as sprites but as shapes consisting of a number of lines (for example, stars) and constantly change their shape for obtaining vivid particles. See an example of using the geometry shader in the The furry carpet example section in Chapter 8, Using Shaders.

Creating a particle system – summary

Let's sum up all the described categories of interaction, modeling, and drawing. To make a project which draws a particle system, you need to specify its properties:

  • Particles interaction type: This property checks whether the particles are independent (fire and clouds), interact with each other (flocks), or interact via some underlying field (liquid).
  • Visualization: This property lets you define how a particle will be drawn—as a geometrical shape or a sprite—and how the particle's view should change during its lifetime (shape, color, size, and so on).
  • A desired order of the particles' number: This property lets you set a desired order of the particles' number as 1,000 to 10,000, 10,000 to 100,000, 100,000 to 1,000,000, or more.

Having prepared this list, you should choose appropriate methods for physics computing and visualization:

  • 1,000 to 10,000 particles with simple physics can be calculated using a single CPU core and rendered with simple methods (using ofCircle(), ofTriangle(), or image.draw())
  • 10,000 to 100,000 can be calculated with CPU too (using single or several cores) and rendered using ofMesh
  • For 100,000 to 1,000,000 particles and (or) complex physics, you definitely should use the GPU methods for computing and rendering

Tip

If your particle system is big and complex, before implementing it, we strongly suggest creating its prototype with several thousands of particles for debugging basic physics by using a single CPU and simple drawing.

In the rest of the chapter, we will implement a simple but fully-featured particle system consisting of several thousands of particles. These particles will be independent, and we will compute them using a single CPU's core and draw them as circles.

The aim of this project is in exploring the beauty of patterns, generated by a particle system, having a circular symmetry. The style of particles' behavior and a set of control parameters is taken from our Kuflex's project, Abstract Wall (see kuflex.com for details about this project).

Let's begin with modeling and drawing just one particle.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset