I came across a very interesting presentation that talks about how to avoid "banding" artifacts in game graphics. The author uses dithering to add noise to an image and break up the visible lines your eye can detect. This even works with audio. When noise is added to the mix, the original tune appears to become more high-fidelity than it actually is:
I was able to use this concept to improve the appearance of banding in shadow acne in a low-resolution spotlight with a large volume. Here is the original situation, purposely created to maximize the banding effect:
And here is the result when some slight dithering is added:
The trick to dithering is to know how much noise to add. If we add too much it starts becoming very apparent:
You want to calculate your noise amplitude as the difference between the two discrete levels you are trying to bridge. In the case of the spotlight shader, random noise is being added to the z coordinate of the shadow lookup, so I want the noise amplitude to be equal to the minimum depth difference the shadowmap can display. I calculated this as follows, but more experimentation is needed to make sure its right:
You also want to make sure your random seed is really random. Using the screen coordinate alone produces bad results because the same random seeds will stay in place as you look around and cause visible patterns. If you use the "currenttime" shader uniform the noise will constantly change as the camera stays still, resulting in a film grain effect. I find it is best to multiply the xy components of the fragment coordinate by some other vec2 value.
Here's another example with a directional light exhibiting a banding appearance due to a low angle on the ground:
And here is the improved image with dithering applied:
This code does not eliminate shadow acne, it just breaks it up with some random noise so that your eye can't as easily detect a continuous line.
The same technique can be used to improve the appearance of the new godrays shader I am working on. With only 16 samples for the rays, banding is very easily visible in this image.
When we add a random offset to the ray starting position, with an amplitude equal to the length of the distance between steps, banding disappears.
Of course more samples are always better, but even at higher sample counts, dithering makes a huge improvement in quality. Leadwerks Engine 2 actually used 64 samples, with worse results than what I got using just 16 samples above. The ease with which I can now modify shaders in Leadwerks Editor and see the results instantly really helped develop these techniques.