To simulate real fog is surprisingly complicated, and computationally expensive. The way real fog behaves has to do with how light scatters in air when interacting with scattering particles (aerosol). The same phenomena occur in clear weather and are responsible for making the sky look the way it does.
The light scattering that happens in the sky is usually categorised into two types: Mie scattering and Rayleigh scattering. Mie scattering describes how large particles (relative to the wavelength of visible light) scatter light, while Rayleigh scattering deals with smaller particles. Rayleigh scattering scatters light with shorter wavelengths more, i.e. blue light is scattered more than red light, the root cause of the sky being mostly blue.
I was interested in implementing physically based fog rendering, using equations for Mie/Rayleigh scattering, in Chunky. The version currently in the stable release of Chunky (1.3.4) was a half-assed implementation of that. I did some experimentation in a separate Java project to see if I could produce a better scattering implementation. The result was rather nice:
The advantage of simulating light scattering for fog rendering is getting the right color at the horizon. In video games you can often notice a clear border between objects on the horizon and the sky. If both the sky and the fog is rendered by light scattering simulation you will get a nicer transition from horizon to sky.
The problem with using scattering equations for fog simulation is that it’s too computationally expensive, and does not allow tweaking the fog color. I wanted to allow changing the color of fog, contrary to the way real fog works where the color is an indirect result of the light scattering behaviour.
Most equations for light scattering involve an exponential extinction factor. If you really simplify the equations you can make believable fog by using the extinction factor to blend in a fixed fog color. This entirely short-circuits the whole simulation aspect of fog rendering and is much less computationally demanding. I have implemented this system in the latest version of Chunky, and it seems to be working rather well.
There is however a problem with my new fog rendering system: inscatter light intensity (the fog color blending factor) is based on the fraction of lit fog along each light path, i.e. how many inscattering events happen on the light path proportionally to all simulated events. This causes light shaft intensity to drop off too much if there is a lot of non-lit space around the light shaft. This undesired effect can be illustrated with a room where the camera looks through a light shaft at two walls that are far apart:
The fog color scaling by room depth can be “fixed” by multiplying with the light path length for each inscatter contribution. However, that fix makes the inscatter component much larger for distant objects and decreases the convergence rate. If I was better at solving differential equations I might be able to work out how to make the inscatter component same as in the no-shadows case when using the simpler equation, but that would still mean convergence is slower (grain/noise in the render). I think I will just leave the current system as it is because the rendering error is difficult to notice and the fog looks reasonably nice. Here is a comparison between two inscatter equations I’ve been testing:
The lower part of the above image was rendered with an improved equation that tries to compensate for the light path length. It took much longer time to render than using the simpler equation, seen in the top part of the image. The improved equation generates slightly nicer results but it might not be worth the increased rendering time.
Here are a couple of nice articles describing light scatter simulation:
- Preetham et. al., A Practical Analytic Model for Daylight
- Hoffman N., Preetham A. J., Rendering Outdoor Light Scattering in Real Time
- GPU Gems 2, Chapter 16. Accurate Atmospheric Scattering
The first article describes one of the most widely used methods of simulating a day sky. It is not perfect, but I used it in Chunky for the “simulated” sky model.