Adding A Performance Counter to your C# Application

Submit New Article

Published On :   October 3, 2008 12:00 AM PDT
Rate
 


Introduction

Look up, waaayyy up!

As the introduction to this article, this is where I tell you about how I recently spent some time looking up at cloud formations in the sky and became inspired to write software to emulate what I saw. That would be a nice introduction, but it would be untrue. The truth is that I became inspired after seeing a procedural cloud demo on Hugo Elias' website. (See the references at the end of the article for a link to the demo). The thing that interested me about the demo was the fact that so much of the work was 'busy work' - filling in pixels and blending stuff together - work that seemed to me to be just the kind of work that graphics cards were put on earth to do.

I'll explain how I implemented the procedural clouds using 3D hardware, and some ways to make it scale across a range of processor and graphics card performance and capabilities. First though, I'd like to point out some observations about clouds and some of their properties. (Yes, I did eventually go look at the sky before putting together the demo). We won't be able to model all of these things, but it's worth noting them to give us a starting point. Fortunately, since I live in Oregon, there was no shortage of clouds to snap some pictures of. I've included a few below, and made some notes on each.

Figure 1A
Figure 1A

  • The sky behind the clouds has some color to it. Usually blue, though it could be black at night. (See figure 1). (Sunsets and sunrises have gradients that go from yellow/red to blue, as we'll see in a later photo)
  • Thin clouds are white. As they get thicker, they turn gray. This isn't just srcalpha-invsrcalpha transparency, but rather indicates that there are two things that have to be modeled, the amount the background is being obscured, and the amount of light the clouds emit in the direction of the viewer (which is light reflected/refracted from all directions).
  • Clouds are for the most part, randomly turbulent. The shape of the patterns can change greatly, often with altitude. Low-lying clouds tend to be thick and billowing, and higher level clouds tend to be thin and uniform.
  • Clouds at lower altitudes tend to obscure light from above more than reflect light from below, and are also usually thicker, and thus are usually darker. Clouds at higher levels are nearly always whiter.


Figure 1B
Figure 1B

  • As you turn toward the sun the clouds tend to be brighter.
  • There are sometimes visible transitions in cloud patterns, such as along a weather front.
  • Atmospheric haze makes the sky and clouds in the distance fade out to a similar color.

Figure 1C
Figure 1C

  • Clouds have thickness, and thus get lit. Those in front of the s un either block or emit light, depending on thickness.
  • Clouds at sunrise and sunset tend to reflect light from below more than transmit light from above.
  • At sunrise and sunset, more colors of the spectrum are reflected, thus the sky color tends to be a gradient from blue to yellow or red, and the clouds tend to be lit with light of orange or red.
  • The sky's cloud layer isn't a plane (or a cube for that matter), but rather a sphere. We just look at it from close to it's circumference, and so often mistake it for a plane.


We're certainly a long way from modeling all of these things. Also, this doesn't begin to list the observations we'd make if we were able to fly up, into and through the clouds. Limiting ourselves to a view from the ground, we'll see how many we can model in a real time application later. First, some background on some of the techniques we'll use.


Making Noise

If you've been reading this publication and others on a regular basis, you've undoubtedly heard talk of procedural textures, and in particular, procedural textures based on Perlin noise. Perlin noise refers to the technique devised by Ken Perlin of mimicking various natural phenomena by adding together noise (random numbers) of different frequencies and amplitudes. The basic idea is to generate a bunch of random numbers using a seeded random number generator (seeded in order to be able to reproduce the same results given the same seed), do "some stuff" to them, and make them look like everything from smoke to marble to wood grain. Sounds like it shouldn't work, but it does.

This is best illustrated with an example. Take the example of a rocky landscape. When looking at it's altitude variation, low frequencies exist (rolling hills), as well as medium frequencies (boulders, rocks) and very high frequencies (pebbles). By creating a random pattern at each of these frequencies, and specifying their amplitude (e.g.mountains are between 0 and 10000 feet, boulders between 0 and 100 feet, pebbles under 2 inches), we can add them together to get the landscape. (See figure 2 for a one dimensional example of this.)

Figure 2 - Waves of different frequencies and amplitudes being summed together. In this case a regular function because the input functions are regular.
Figure 2 - Waves of different frequencies and amplitudes being summed together. In this case a regular function because the input functions are regular.

Taking the one dimensional example to two dimensions, we'd get a bitmap that could represent a heightmap, or in our case cloud thickness. Taking it to three dimensions, we could either be representing a volume texture, or the same two dimensional example animated over time.

Aside from the random number generator, the other thing necessary is a way to interpolate points between sample values. Ideally, we'd want to use a cubic interpolation to get curves like those in the graph, but we are going to use a simple linear interpolation. This won't look as good, but will let us use the hardware to do it.


Summary of the procedural cloud techn ique

The idea behind the technique is to generate a number of octaves of noise (an octave being an integer multiple of a base frequency, such that their ratio is 2:1, 3:1, or other integer ratio) and combine them together to make some turbulent looking noise that resembles smoke or clouds. Each of the octaves is updated at a specific rate, different for each octave, and then smoothed. To generate the turbulent noise for a given frame, we interpolate between the different updates for each octave, and then combine the different snapshots for each octave together to create the turbulence. At that point, some texture blending tricks need to be done to clamp off some ranges of values, and map it onto a sky dome, box, or whatever surface you are working on.

As I mentioned earlier, what intrigued me about the original software-rendered demo was that many of the steps involved (smoothing noise, interpolating between noise updates, combining octaves) seemed like a lot of per-pixel cost. Cost that could be done on the graphics card using alpha blending, bilinear filtering, and by rendering to texture surfaces. A remaining question was whether the simple four-tap sampling of the bilinear filter would be adequate for doing the smoothing. I figured I'd attempt it, and as I think you'll see, the results are acceptable.


Background on rendering to texture surfaces

In the synopsis of the technique above, I mentioned rendering to texture surfaces, something possible on a lot of modern day 3D hardware, and exposed by the DirectX7 API. I wrote an article for gamasutra.com on this subject (a link is included at the end of this article) that goes into more detail, but I will summarize the technique here for those that haven't read it.

In order to render to a texture surface, one has to create a surface that can be both used as a render target and as a texture (there's a DirectDraw surface caps flag for each). If the application is using a Z buffer, it must attach one to the texture surface as well.

Then for each frame, the application does one BeginScene/EndScene pair per render-target. After rendering to the texture and switching back to the back buffer, the application is free to use the texture in that scene.

I use this technique pretty extensively in this demo (.zip, 1.51 MB), but there's no reason it can't be done by rendering to the back buffer and then blitting to a texture surface for later use. In fact, OpenGL doesn't expose the ability to render to a texture, so you'll have to blit to the textures if this is your API of choice. This is also the work around used on hardware that doesn't support render-to-texture.


Enough already! Let's render some clouds

The technique involves a number of steps:


demo (.zip, 1.51 MB) altogether more natural and random looking is to animate any (or all) of the various parameters over time, using a smoothed noise function. This could be used to animate the cloudiness factor to vary the amount of cloud over time, or to animate the speed of the clouds, or the turbulence. The only limit is your imagination (or in my case, the time to implement it all)!


Multi-Texture Meltdown

As I alluded to earlier, an unfortunate problem when doing any effect that requires a significant amount of multi-texture and multi-pass rendering, is the lack of precision that can quickly result in visual artifacts. Where possible, 32-bit textures can be used to minimize (but not always eliminate) this problem. At the same time, it's important to only do so where necessary to minimize the texture memory consumption.

Another problem is the fact that the dynamic range of textures is limited to the 0 to 1 range. This makes it difficult when dealing with anything that gets amplified over multiple stages. For example, the higher frequency noise textures ended up only contributing one or two bits to the end result due to this problem.


Making it Scale

If you're writing games for the PC platform, being able to implement a snazzy effect isn't enough. You also have to be able to make it scale down on basic machines or on video cards with less available memory. Fortunately, this procedural cloud technique lends itself well to scaling in several respects.

On the lowest end systems, you can either use a static texture or generate the texture only once. Other areas for scalability include making updates to the noise less frequently, using fewer octaves or using lower resolution or lower color depth textures.


Doing it Yourself

Generating procedural clouds can allow for unique skies that change over time and with other factors in the environment. This can improve the look of skies over static cloud textures that never change with time. Additionally, they can save on storage, or download size for Internet applications.

Hopefully, some of the techniques presented here will allow you to implement similar things in your applications.

Additional Resources
  • Check out our 3D games forum!
  • This technique is covered in more detail with some additional functionality in the following book:

    Game Programming Gems 2 (PP 463-473), Mark Deloura, Editor. Charles River Media, 2001. ISBN 1-58450-054-9
  • Modeling and Texturing, A Procedural Approach - second edition. David S. Ebert, Editor. AP Professional, 1994. ISBN 0-1 2-228730-4
  • Hugo Elias & Matt Fairclough's procedural cloud demo*
  • Haim Barad's Procedural Texture Using MMX* article
  • Ken Perlin's Noise Machine website - http://www.noisemachine.com/
  • Kim Pallister's article, "Rendering to Texture Surfaces Using DirectX7*"*



Author

Kim Pallister: kim.pallister@intel.com.