Now, I said I wanted to get the editor written up before I posted the basic system, but I have had so many requests for it I thought I may as well get the base up and I can build on it as the system evolves.
Before I get into the code, I'll explain a little of it's evolution. I first started out with a point sprite cloud system. This had a point sprite base cloud and a point sprite cloud manager, but found (on my Graphics card) that you get a kind of parting of the waves effect. I have recently seen this code ran on a much more superior laptop (Paul Foster from Microsoft) and he does not get the issue, anyway I digress. So with this issue I thought I would go with a billboard system, this then gave rise to a billboard cloud and respective manager as well as the point sprite version and there associated shaders. Now I am under the impression that a point sprite system is more cost effective than billboards, so what I have done in the final system in merge the two. The initial idea was to have the close up cloud sprites to be billboards and the ones in the distance to be point sprite . I decided to go a little further and you can have that and specify the distance where the two cross over or have just billboards or have just point sprites, or have none (switch the lot off)
Now you will notice in the early clips of the cloud system, the cloud sprites look a bit odd, now that's because in my shader I was using the sprites as colour sprites and not alpha sprites, this was pointed out to me by my good friendchr0n1x. Also you will see the cloud sprites are, well not too good. This is because I hacked the sprites out individually from the sprite sheet in Niniane Wangs white paper. I now pass the whole sheet to the shader and get it to sort out what image is required.Which reminds me, as it is part of this source zip, if you use this code for any commercial gain DO NOT USE THIS SPRITE SHEET, it is released under a similar license as the XNA assets so fine for educational use. To be specific, see this license.
Where to start, well I was going to start with the particle structures, but they are very similar to the ones I have posted before on both point sprite and billboard particle systems, so won't bother repeating myself.
Ill begin with the basic class that these volumous objects are derived from, VolumusObject
So a pretty strait forward class, the only special feature really is the BoundingBox parameter, this is used to define the size of the volume.
Now into the first bit of nitty-gritty, the base cloud class that is derived from VolumetricObject, BaseCloud.
So, we have the regular particle system type of stuff, particle colour, scale and count, but we have another, disipation (miss spelt it, how like me that is..); it simply regulates the alpha level of the cloud so giving the impression of forming and dissipating. The BuildCloud() method is where all the actions at. What this method does it place the cloud sprites randomly in the volume and scales them to take up the volume space. It then randomly sets the sprite image to be used for each cloud sprite.
We now have a basic, randomly generated cloud, and this was great in the early days, but I wanted to control what image sprites are used in the cloud, so I created the Cloud class which inherits from BaseCloud.
This is even simpler, all we do is add an array of integers to describe the cloud images to use off the sprite sheet.
So far, pretty simple eh? Well then lets take a look at the cloud manger and see if it gets complicated....
OK, a fair bit in there, but it looks complex because of the splitting of billboard and pint sprite particles, other than that is it a pretty strait forward particle emitter. There are some areas of note here though...
AddCloud() & AddClouds()
This adds a cloud to the manager. All it does is increment the overall particle count and adds the cloud to the cloud list.
This method populates the master particle list with the clouds that have been added.
SortClouds()And that is about it, pretty simple stuff I know, looks good though don't it. Wait....I am missing the vital bit that brings all this stuff together, the shader.
Now, this bit might be interesting, this is where the draw order is sorted. I started off with a bubble sort to do this, but guess what? It was very, very slow. Now I lack the education to implement great sorting algorithms (some suggested by Leaf) so I used my ingenuity and decided to use the sort method of the List object to do my dirty work. You will see there is a reference to a class called distData, this is a class that I derived from IComparer and holds my sorting logic. You will find it in the Utility.cs source file. So using that I just get the List.Sort method to do all the work for me, great!
I am just going to put up the billboard shader as it and the point sprite shader are very similar.
I am quite chuffed with this shader as it is all my own work, I must be learning something :)
What is going on here? Well, in the Vertex shader, as before we are orientating the board to face the camera and scaling it, then we ready the tex coords and the color, then set the image index, this is the sprite on the sprite sheet to be used, when you look at the sprite sheet the index works like this:
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
How am I getting a single sprite off the one sheet of 16 sprites? This is done in the GetTexture function, what I am doing here is taking the texcoord and manipulating it to give me the required uv value on the sprite sheet for the given index. This manipulation is done by quartering the texcoord value (the sprite sheet is 4x4 sprites) then adding the uv value I have set up in my imageUV array for the given image index. You can see this is getting multiplied by 100, and in the code the value gets divided by 100, I had tried to pass this as an int but it never worked out so, I force it to be a float/half to 2 decimal places. Once I have this modified texccord I can get the image from the sprite sheet.
Then depending on the distance of the camera from the sprite we fade it, this is done so as you approach a mass of cloud it thins out giving a misting effect.
Then in the Pixel shader I get the rgb values from the sprite sheet for the given sprite index (remember they are alpha sprites), then, as I had integrated it with my SkySphere it calcs the shade of the cloud based on time of day and sets the color of the cloud based on base cloud color and the ambient light color then the alpha is set using the rgb from the sprite sheet. I then thin the cloud out from top to bottom giving the sprite a wispy effect. Also, you will notice in the sprite sheet assets pipeline properties I have set it so it is resized to the power of two, this speeds it up a little. You may notice on your PC or laptop that as you get close to a cloud or view a lot of clouds your FPS will drop (if you have a regular card like me), this is due to your card reaching it's fill rate, on the Xxbox 360 you don't seem to get this issue.
In the Game class you will see I have set up 3 skyTypes, this is just to show how you can have varying sky's with the system, you can do SO much more than I have in this sample, but I guess I will show you that in later posts.
And so that is about it....that is my basic volumetric cloud system. I hope you have as much fun using it as I have had creating and posting about it. Hope it lives up to your expectations too. This is not the end of it though, I will keep posting on it's evolution and giving code updates. There are a few of us working on this system now and I have set up an SVN so we can improve it, Kyle Hayward aka GraphicsRunner is helping out with the lighting and other design elements as is Michael Hansen over at EvoFX Studio we are hoping to get it integrated into his XNA games engine as well as Sora.
As ever, don't see this as a finished entity, it is a WIP, also if you do use it in your game then please give me a shout, would love to see it used. And do remember if you use this for a commercial venture DO NOT USE THE SPRITE SHEET, CREATE YOUR OWN! Again, read the license for this asset here.
Download the cloud solution here.