
Introduction to 3D Game Programming with DirectX.9.0 - F. D. Luna
.pdf

Chapter 14
Particle Systems
Many natural phenomena consist of many small particles that all behave in a similar manner (for example, flakes of snow falling, sparks from a firework, and the “bullets” a futuristic space gun emits). Particle systems are used to model such phenomena.
Objectives
To learn the attributes that we give to a particle and how we describe a particle in Direct3D
To design a flexible particle system base class that includes attributes and methods general to all particle systems
To model three concrete particle systems, namely snow, an explosion, and a particle gun
14.1Particles and Point Sprites
A particle is a very small object that is usually modeled as a point mathematically. It follows then that a point primitive (D3DPT_POINTLIST of D3DPRIMITIVETYPE) would be a good candidate to display particles.
However, point primitives are rasterized as a single pixel. This does not give us much flexibility, as we would like to have particles of various sizes and even map entire textures to these particles. Before Direct3D 8.0, the way to get around the limitations of point primitives was to not use them at all. Instead, programmers would use a billboard to display a particle. A billboard is a quad whose world matrix orients it so that it always faces the camera.
Direct3D 8.0 introduced a special point primitive called a point sprite that is most applicable to particle systems. Unlike ordinary point primitives, point sprites can have textures mapped to them and can change size. Unlike billboards, we can describe a point sprite by a single point; this saves memory and processing time because we only have to store and process one vertex over the four needed to store a billboard (quad).
235



238Chapter 14
D3DRS_POINTSIZE_MAX—Specifies the maximum size that a point sprite can be. Here is an example of setting the maximum to 5.0:
_device->SetRenderState(D3DRS_POINTSIZE_MAX, d3d::FtoDw(5.0f));
D3DRS_POINTSCALE_A, D3DRS_POINTSCALE_B, D3DRS_ POINTSCALE_C—These three constants control how a point sprite’s size changes with distance—the distance being the distance from the point sprite to the camera.
Direct3D uses the following formula to calculate the final size of a point sprite based on distance and these constants:
1
FinalSize ViewportHeight Size
A B D C D2
where:
FinalSize: The final size of the point sprite after the distance calculations
ViewportHeight: The height of the viewport
Size: Corresponds to the value specified by the D3DRS_POINTSIZE render state
A, B, C: Correspond to the values specified by D3DRS_POINTSCALE_A, D3DRS_POINTSCALE_B, and D3DRS_POINTSCALE_C, respectively
D: The distance of the point sprite in view space to the camera’s position. Since the camera is positioned at the origin in view space,
this value is D x 2 y2 z |
2 , where (x, y, z) is the position of |
the point sprite in view space. |
|
The following code sets the point sprite distance constants so that the point sprites will get smaller with distance:
_device->SetRenderState(D3DRS_POINTSCALE_A, d3d::FtoDw(0.0f)); _device->SetRenderState(D3DRS_POINTSCALE_B, d3d::FtoDw(0.0f)); _device->SetRenderState(D3DRS_POINTSCALE_C, d3d::FtoDw(1.0f));
14.1.3 Particles and Their Attributes
A particle consists of many more attributes than its position and color; for instance, a particle has a certain velocity. However, these additional attributes are not needed to render the particle. Therefore, we keep the data needed to render a particle and particle attributes in separate structures. When we are creating, destroying, and updating particles,

Particle Systems 239
we work with the attributes; then when we are ready to render, we copy the position and color over to the Particle structure.
The attributes of a particle are specific to the particular kind of particle system that we are modeling. However, we can generalize a bit and come up with common attributes. The following example structure contains some common particle attributes. Most systems won’t need all of these, and some systems may need additional attributes not listed.
struct Attribute |
|
|
{ |
|
|
D3DXVECTOR3 _position; |
|
|
D3DXVECTOR3 _velocity; |
|
|
D3DXVECTOR3 _acceleration; |
|
|
float |
_lifeTime; |
|
float |
_age; |
|
D3DXCOLOR |
_color; |
|
D3DXCOLOR |
_colorFade; |
|
bool |
_isAlive; |
|
}; |
|
|
_position—The position of the particle in world space |
|
|
_velocity—The velocity of the particle, which we usually mea- |
|
|
sure in units per second |
|
|
_acceleration—The acceleration of the particle, which we usu- |
|
|
ally measure in units per second |
|
|
_lifeTime—How long the particle can live before it dies. For |
|
|
instance, we might kill a laser beam particle after a certain period |
I |
|
of time. |
|
II |
|
art |
|
_age—The current age of the particle |
||
|
|
P |
_color—The color of the particle
_colorFade—How the color of the particle fades over time
_isAlive—True if the particle is alive, false if it has died
14.2 Particle System Components
A particle system is a collection of particles and is responsible for maintaining and displaying these particles. The particle system keeps track of global properties that affect all particles in the system, such as the size of the particles, the location that the particles originate from, the texture to apply to the particles, etc. Functionality-wise, the particle system is responsible for updating, displaying, killing, and creating particles.


Particle Systems 241
_emitRate—The rate at which new particles are added to the system. This is usually measured in particles per second.
_size—The size of all the particles in the system
_particles—A list of particle attributes in the system. We work with this list to create, destroy, and update particles. When we are ready to draw the particles, we copy a portion of the list nodes to the vertex buffer and draw the particles. Then we copy another batch and draw the particles, and we repeat this process until all the particles have been drawn. This is an oversimplification; we explain the drawing process in detail in section 14.2.1.
_maxParticles—The maximum number of particles that the system is allowed to have at a given time. For instance, if particles are being created faster than they are being destroyed, we end up with a huge amount of particles over time. This member helps us avoid that scenario.
_vbSize—The number of particles that our vertex buffer can hold at a given time. This value is independent of the number of particles in the actual particle system.
Note: The data member _vbOffset and _vbBatchSize are used to render the particle system. We defer a discussion of them until section 14.2.1.
Methods: |
|
|
|
|
III |
||
PSystem / ~PSystem—The constructor initializes default values |
|
||
|
and the destructor releases device interfaces (vertex buffer, |
|
rt |
|
texture). |
|
Pa |
init—This method does Direct3D device-dependent initialization |
|
|
|
|
|
||
|
work, such as creating the vertex buffer to store the point sprites |
|
|
|
and creating the texture. The vertex buffer creation contains some |
|
|
|
flags that we have discussed but haven’t used until now: |
|
|
|
|
|
|
|
hr = device->CreateVertexBuffer( |
|
|
|
_vbSize * sizeof(Particle), |
|
|
|
D3DUSAGE_DYNAMIC | D3DUSAGE_POINTS | D3DUSAGE_WRITEONLY, |
|
|
|
Particle::FVF, |
|
|
|
D3DPOOL_DEFAULT, |
|
|
|
&_vb, |
|
|
|
0); |
|
|
Notice that we are using a dynamic vertex buffer. This is because we will need to update our particles every frame, which means we will need to access the vertex buffer’s memory. Recall that accessing a static vertex buffer is unacceptably slow; we therefore use a dynamic vertex buffer.

242Chapter 14
Observe that we use the D3DUSAGE_POINTS flag, which specifies that the vertex buffer will hold point sprites.
Take note that the vertex buffer size is predefined by _vbSize and has nothing to do with the number of particles in the system. That is, _vbSize will rarely equal the number of particles in the system. This is because we render the particle system in batches and not all at once. We explain the rendering process in section 14.2.1.
We use the default memory pool instead of the usual managed memory pool because dynamic vertex buffers cannot be placed in the managed memory pool.
reset—This method resets the attributes of every particle in the system:
void PSystem::reset()
{
std::list<Attribute>::iterator i;
for(i = _particles.begin(); i != _particles.end(); i++)
{
resetParticle( &(*i) );
}
}
resetParticle—This method resets the attributes of a particle. How a particle’s attributes should be reset is dependent upon the specifics of a particular particle system. Therefore, we make this method abstract to force the subclass to implement it.
addParticle—This method adds a particle to the system. It uses the resetParticle method to initialize the particle before adding it to the list:
void PSystem::addParticle()
{
Attribute attribute;
resetParticle(&attribute);
_particles.push_back(attribute);
}
update—This method updates all the particles in the system. Since the implementation of such a method is dependent upon the specifics of a particular particle system, we declare this method abstract to force the subclass to implement it.
render—This method displays all the particles in the system. The implementation is quite involved, and we devote subsection 14.2.1 to a discussion of it.
