Was browsing the boards as I do and came across a thread on Ziggyware where a member was looking for a Particle Engine tutorial, at first I did not quite get what he wanted, so tried to clarify what he wanted with a post and suggested I could create a simple particle library, to which another member and the original poster said it would be a good idea and so I though the best thing would be a very simple tutorial describing what I know of particle system.
Now, I am no expert but I think I have an idea of the basic principles of a particle system and so I am going to put up a series of tutorials describing how to create a simple particle system. I know I have already got 2 samples up here, but it it just holds code samples and no explanation of what is going on under the hood.
In this series I am going to do both a 3D and 2D tutorial. As I already have a 3D sample I will start with the 3D system. In this first tutorial I am going to cover a PonitSprite particle system.
So, where do I start when creating a particle system, well I guess the best place is the particle it's self, to me this means the data structure I am going to use to hold the data required to draw and move my particle, think about what you want the particle to represent, what data you will want to store to draw the particle. So I create my own vertex element, this describes the primitive to be used to draw the particle. Now this is a tutorial so I am going to be quite generic in my particle description, it is going to contain what I think are the basic elements for any particle, you will probably want to add more to this structure for your system(s) or this might just do the job. It is going to hold position, colour, sprite size and an alpha value.
So I create a new source file in my project and name it after the structure, in this case I have called it PointSpriteElement.cs. In the file I create a new structure definition
I now add my elements
So, my position is a vector 3 and will store this particles position in space, colour (I am in the habit of using the American spelling now) will store the color I want to draw the particle in and data is a vector 2 that is going to hold my sprite size in the X and the alpha value in the Y.
I now give this structure some properties so I can access the particle elements.
I now need to create the definition of the particle that will be used by my shader, this is done using a VertexElement array.
Right, as you can see this looks a bit messy, but all it is doing is describing my vertex input structure, or I guess a simpler description is how the particle is passed to the shader. So the first line describes the vector 3 that is used for the position, the first parameter is the stream to be used and can be used for stuff like multi texturing as it saves the duplication of vertex data (see this link for a better description), I have only ever used 0. As this is the first element in the element array it has an offset of 0, a format of Vector 3 as that is the data type, the method is default, usage is Position and the channel is 0. So in the shader this would be
described as half3 Position : Position0; And so the other to elements are described in the same vein, you can see the offset (second parameter) increased by the last elements size.
I also include a property so I don't have to keep typing in the particle element size.
The whole structure now looks like this:
Right we now have a definition for our particle that is going to be used in the shader to draw our particle in the game world. We now need an array of these particles and a class to manage this particle array. In my project I create a new source file called PointSpriteParticleEmitter.cs, this is going to hold my particle emitter class. Again, you need to think about what kind of particle emitter you want to create, in this tutorial I am going to create a simple trail.
So we start by creating our class
Now, like any other object in the game world we will need a position a scale and rotation. I am also going to give it a color so I can set all the particle colors if I wish. We will also need a variable to store the number of particles to have in this instance. I also have in this emitter some variables for my particle physics, I will go into more detail in the Update override. Also, we need an Effect object to load the shader into. Oh, and a Game object to bind to the calling game class.
So the class now looks like this
Now for the constructor (ctor), very simple, the calling game and the particle count are passed and used to build the emitter and all values are set to arbitrary default values.
We are inheriting from DrawableGameComponent so we now need to override some of the base methods to load, update and draw our particles. Lets start with LoadContent. In this method we are going to load the shader, texture and load our particle array. I will go into more detail regarding the shader and the particle texture later on, but for now I will concentrate on the loading of our particles. We are also setting up some of the particle physics variables ready for use. Also, at the top of the method we will setup the vertex declaration on the graphics device.
I have put the loading of particles into a separate method as you might want to call it again at a later date. So our method to load the particle array is going define the size of out particle array, and set each particle up ready for use in the system. Now in here if you were using it for say a weather effect like rain or snow you would want to set the positions to random values in the scene, as I say it all depends on what you want the effect to achieve.
My LoadParticles method looks like this
Pretty simple stuff, we are setting the position of each particle to the emitters position, then (as in this example we are going to do additive blending and so the alpha value is redundant, but will show that in the next tutorial) setting the particle color to black and it's size to 1.
Now for the fun bit, this is where the behaviour of your particle emitter will be defined. In this tutorial we are doing a trail so we have a target position we are chasing, we need to know what the targets last position was, so that too is stored as well as the current particle we want to work with. I have been terming this particle physics, this is a very lose term, what I really mean is particle behaviour. In this sample we are doing all the particle physics on the CPU, in a later tutorial I will show how you can move this to the GPU.
So here is the work horse of the emitter, the Update override.
All we are doing here is moving through our particle array, finding the particle we want to work with and setting it's new position and color, also we are sizing the particle based on how far it is from the target so giving a tapering effect to the trail. We then store the last position of the target and (as this is a tutorial and I don't want to code another object to use as a target) we move the target.
And now onto the draw method. This is where we are going to define out blending method, in this tutorial, additive blending. We are also setting up a few parameters in the shader, but I will explain those in the shader code it's self in a short while.
So our emitter class now looks like this
Now we have a particle system we need to implement it, as we have derived our emitter from DrawableGameComponent this is pretty easy. We just create an instance of the object and add it to the game components like this
As you can see I have created an emitter with 100 particles in it and placed it 20 units in front of the Camera (see sample download for Camera code)
OK, now it's time for the shader. I have adapted this from the shader that can be found in my previous 3D particle sample. It is pretty simple, we have a texture to use for the particle, two matrices for world view projection and projection, and view port height, used to calculate the particles size.
In the other particle sample the shader is written so it can be used on the PC and the XBox 360, as this it a tutorial, I have removed the XBox 360 elements so the shader is easier to read.
I have created one data structure for passing data from the application to the Vertex Shader and one to pass data from the Vertex Shader to the Pixel Shader. As you can see the former matches the structure we created in our PointSpriteElement structure (Now I am still pretty new to this, but you will notice the color element in out application structure is represented by a single float, yet in the shader it is 4, if I set the application structure up so the color is size 4*4 rather 1*4 then I get some odd results...)
So the structures looks like this
Our Vertex Shader looks like this
So in here we tell the Vertex Shader to expect out VS_INPUT structure as the incoming parameters and that it is going to return a VS_OUTPUT structure. We create a VS_OUTPUT structure and initalize it setting all the values to 0. We then calculate the position of the vertex in the world and load it into our return structure, then load the color, color alpha and the particle size and finally return the structure to the calling pass.
Our Pixel Shader looks like this
In this Pixel Shader we tell it to expect a VS_OUITPUT structure as the input from the Vertex Shader and that is will return a half4 and that this half4 is a COLOR. We get the color of the pixel ath this TexCoord, multiply it by the color we want to tint the texture by and then pass it out to the calling pass.
So the complete shader looks like this
I guess that brings this first tutorial to a close, hope you find it of help and has made particles a little clearer. As I say at the top, I am no expert, but this is how I am implementing PointSprite particles and it seems to be doing the job for me.
If you want the example source for this tutorial you can find it here.