Many games use clouds and dynamic weather systems to convey an immersive gameplay atmosphere. In Metal Gear Solid 2, for example, part of the action takes place under very heavy rainfall, creating an oppressive feeling. Other games, like flight simulators, need clouds to offer the realism players are expecting from the game. Whichever the case, clouds are quite important in many genres. They can be simulated successfully using several methods, from the very simple for decorative clouds to the quite complex for games requiring clouds as part of their gameplay.
In one extreme, we can just use a skybox, a textured large object, which, once applied to sky textures, can become our sky. We will discuss this method in a moment. On the opposite end of the spectrum, we could implement a volumetric cloud system with properties such as density and temperature, so a plane can traverse a thunderstorm and the player can feel the turbulence. As usual, the choice will depend on the amount of clock cycles you are willing to devote to cloud simulation. Additional features rarely come at no cost.
Skyboxes and Domes
The easiest way to get a cloudy sky is to use a skybox or skydome. These are simply large boxes or hemispheres with normals looking inward, and a texture that tiles across the triangles representing the sky.
For a skybox, five quads are used. They are generally called top, front, back, left, and right; and each one has a unique texture, which blends seamlessly with neighboring quads. Because the quads are painted with no lighting at all, the seams between them are impossible to see, and the sky looks like a whole.
The only difficulty in creating skyboxes is obtaining the source pictures so they integrate perfectly. The best way to obtain them is to create them with a rendering package that has a procedural sky generator. One of the most popular packages is Terragen, which creates full landscapes with terrain, water, and skies.
Using Terragen, the method basically consists of defining a good-looking sky, and then placing a camera in the center of the scenario. Four renders are then performed at yaw angles of 0, 90, 180, and 270. And then another render is computed with the camera looking upward. A sixth render with the camera looking down can be useful for some applications, but most games use five images.
Once images are created with Terragen, it is all a matter of testing until they are properly laid on the five quads. Then, the sense of being in a box should disappear, and only a continuous sky should be visible.
Another technique that can be useful is using billboarded clouds, so each cloud can move and react independently. Clouds only need to be aligned to the viewer and moved very slowly. Billboarded clouds can be combined with a skybox easily, but the downside is that we cannot cross or enter the cloud. That would kill the illusion of volume because we would realize the cloud is nothing but a plane. We need a better system to create realistic cloud representations.
For those applications that need an extra-realistic look, a volumetric cloud model can be implemented, so the player can enter a thunderstorm, be immersed into volumetric fog, and so on. Be warned, though: Volumetric cloud systems require a significant amount of resources, both in the CPU and GPU, and thus can decrease the overall performance of your title.
There are a variety of volumetric cloud approaches. Thus, I will expose a generic cloud system that can be customized or extended to fit many uses.
The first component we will need is a data structure to hold the clouds. This can range from the very simple to the very complex. On the simple side, we could do a list of 3D points and radii, and implement the clouds as an isosurface that we would evaluate per frame. The cloudiness of a point in space would be computed using a variant of a field function. This approach can work well for systems with very few elements where the number of cloud generators is relatively small.
If we need a more accurate representation, we could divide our game world into a voxel structure, with each voxel node representing the cloud density at that point. Although voxel systems are sometimes rejected for their relatively large memory footprint, a cloud system can be rendered efficiently using voxels at a reasonable cost. It all depends on selecting the right cell size. As an example, a 5x5 km scenario, sampled from the water level to 500 m in height (suitable for any city and most outdoors games) at a homogeneous resolution of 50 meters per sample (each sample being one byte) takes only 100KB. Clearly, voxel systems must be controlled carefully because they tend to grow, but that does not necessarily make them unfeasible.
For those situations where a voxel system is too expensive, a two-level fractal compressed cloud system can be implemented. Here the idea is to have a low-resolution voxel system and a "detail" voxel, which we place inside each of the larger voxel's cells, much like the recursion found in fractal systems. Because we will interpolate the detail voxel values according to the four corners of the larger cell, self-similarity will remain unnoticed, and a large cloud system will fit in any memory constraint.
As for the rendering portion of the algorithm, volumetric clouds are best treated as a very slow moving particle system. Cloud elements are just billboards with very low opacity value, and by layering them, we achieve the sense of density we are aiming for. We can even use a color table to ensure that clouds look realistic or even extract the color from a semivolumetric approach; that is, paint each element in a color proportional to the summation of cloud density above it. This way we can represent the gradation of colors found in the different kinds of clouds, from very pure cirrus layers to a dark gray thunderstorm.
Mark Harris from the University of North Carolina at Chapel Hill suggests using impostors to reduce the depth complexity and fill rate required to render the clouds. After all, don't forget we are talking about particle systems, and these take up lots of system resources. Creating a realistic cloud can take between 1,000 and 5,000 particles, and that's definitely not what you want to render on a per-frame basis: The cost is simply too high. Thus, we can condense clouds into IBRs, which is what impostors are all about. We render the cloud to a texture and use it until we have moved significantly, and thus we need to recompute the impostor. By using impostors, we are dramatically reducing the computational cost of our cloud system. All we need to do is establish an error metric that governs the moment when we will recompute the cloud.