So, we have seen PointSprite particles in 3D, but what about billboarding??
You may be asking at this point, what is billboarding and why would I want to use it when I already have a nice PoitSprite system that does the job I want...?
A billboard object is basically a textured quad, again the question "What is a textured quad?" Well this is two triangle primitives used to draw a square and has a texture applied to it. It gets the name quad as it is four points in space rather than three.
As to why use them, well I can only tell you my experiences of the two, first off I go for a PointSprite system pretty much every time when it comes to effects as it tends to be faster and 9 times out of 10 will do all that you need to, but in the case of the cloud system I am doing my best to write, Leaf pointed out that what is described as the "Parting of the Red Sea" effect by Ninian in here paper can be avoided by using billboarding. I assume because the position of a PointSprite is indeed that a single point in space, where as a billboard is a textured quad so has five points, I say five as you have each corner plus the centre and so when passing through the camera does not give that odd parting effect, also I think you are limited to a maximum PointSprite size, this can be altered in the RenderState using PointSizeMax but this too has an upper limit, a billboard does not. So like in the CC sample for billboarding, they can be used for grass and trees or like my cloud system.....clouds. Also, if a point sprite system does all that you need, then don't worry about billboarding.
The main issues you have with creating billboards is keeping them facing the camera in the way you want them to, now you could do this how I used to in my old RandomChaos Engine and orientate each billboard on the CPU, this, as you can imagine is not a very good way of doing things as it just kills your FPS once you have a decent number of particles. The best way, just like in the CC billboard sample is to do it in the shader.
Onto some code, and I will start where I did with the other tutorials with the particle definition, this is pretty much identical to the PointSprite definition, but we do need to add a TEXCOORD field to it, if we don't then we will not be able to apply textures to the billboard correctly. I guess if you really needed it you could also add Tangent data here if you wanted to bumpmap your particles, I have done this in the past, but it looked a little odd, may well have been my lighting algorithms though. Anyway, here is the data structure we are going to use for our billboard particle system
As you can see there is little difference in this structure and the one used for the PointSprite system. I have added a Vector2 for the TEXCOORD field and so in the Element array I have added an extra element so it can be passed to the Vertex Shader.
Now onto the emitter, this again is almost identical to the one in tutorial I, only this time I am going to set up up ready to be bolted into our game Agent. There area couple of new elements to go into this emitter, we need a DynamicVertexBuffer to store our particle array in, and a DynamicIndexBuffer to store the draw order of the primitives, and a VertexDeclaration, this gives the Graphics Device a definition of our particle structure. All the other fields are pretty much the same as what we have in the previous tutorials, the real difference comes when we are loading, updating and drawing the particles. The emitter class looks like this
The first obvious change is in the LoadContent method, other than the assets beign from different files at the top we set up the VertexDeclarartion. LoadParticles is TOTALY different. This is because we have to set up a billboard per particle, so at the top we have to set our array to the number of particles we want * 4 as each bill board has 4 corners, then we have the main loop that go through each particle and in that a second loop that moves us through each point of that particle.
For the given array element we create a new particle instance, the position is set to that of the emitter for each corner, don't worry about this as the billboards size will be set in the shader. We set the color and the data of the particle setting the scale and alpha (unsing additive blending in this example so alpha wont be used).
We then need to set the TEXCOORD of this corner, as I am sure you know TEXTCOORDS range from 0,0 which is the top left corner to 1,1 which is the bottom right corner. So if I had a texture that was 100 x 100 pixels and I wanted to get the pixel in the very centre (well just off centre) it would be located at .5,.5 this would get me pixel 50,50 off the image.
We then set up the draw order of our billboard corners in the DynamicIndexBuffer, you will see I am using a short array to store this data, this is because I have a very poor Graphics Card that can't handle 32 bit index buffers, if you have a good card, swap these types for an int. You will also notice that we have 6 indices per particle, this is because the bill board is made up from 2 triangles.
The Update method is really the same as before only we have to jump 4 elements per particle as we now have 4 elements per particle. The major change here is the addition of setting the DynamicVertexBuffer
The Draw method again has few changes, at the top we set the VertexDeclaration, bind the vertex buffer and index buffer to the device, setup our blending method and shader, send the particles to the device and reset our RenderStates.
So as you can see the major difference is setting up the Graphics device to use our particle array and the fact that our particles are now made from 4 corners.
In this tutorial I have gone strait to binding the emitter to a game agent, for simplicity I have taken the BasicModel class I did for my A.I. Finite State Machine sample and removed the A.I. from it, added an instance of our emitter and that was pretty much it. The BasicModel class now looks like this
All we need to do now is add our agent to the Game.Components collection and away we go.
OK now as you can see in all that code, not once do we try and make the billboard face the camera, this is all done in the shader. This is how I do it.
First of all I get me world UP vector, this tells me what orientation is the UP vector, I do this by getting the cross product of -1,0,0 and 0,0,-1, I then need the eye vector of the viewer and I can get this from the View elements of the ViewProjection Matrix like this vp._m02_m12_m22, I can then get the side and up vector of the billboard orientation by getting the normalized cross produce of the eyeVector and the world up vector. I can then apply this to the final position of my Vertex. This is done in two steps, the first manages the sideVector orientation, I subtract .5 from the current TEXCOORD.x multiply that by the sideVector and the scale I want the particle to be, the second step I subtract the current TEXCCORD.y from .5 and multiply that by the upVector and the scale I want the particle to be. As you can see with that methods you can scale both the X and Y dimensions of your billboard differently.
Why am I subtracting .5 from the TEXCOORD in the first stage and the TEXCOORD from .5 in the second stage. Well, as I said before .5 puts us in the centre of our billboard so for the side orientation the left most point with be at x:0 so giving us -.5 to add to our particle position, this will give us a point in space .5 to the left of the particle, the right most will be x:1 so this will give us the right most point from the centre in space. In the case of the second stage, subtracting .5 from the TEXCOORD puts us at -.5 above the centre, and from the other extream it puts us .5 bellow that centre.
The rest of the shader is pretty much as before, other than the extra handling TEXCOORDS
Here is another example of billboards being used, the code and sahders are a little more work than shown here but it is pretty much the same technique.
In the sample source I'm using the smoke texture provided by MS, and the biplane model that comes with the DirectX SDK. You can download the solution project here.