Fog Fog Fog
Begin with story
I remembered back in a day that I’ve failed in adding fog in my new ground deferred shading. So I decided to pursue another route just for survival alternative, and that was particle fog.

The time passed, and someone popped up a need to fully take control of fog system. Then that thing heads back and bangs me to the code!
I have done fog in shading sense before. But that time with normal forward rendering technique. So it’s quite straight forward as merely no other big stuff to be considered and would block out the way. It should be something, I always think to myself, that I miss it out in that time. Finally, I go back to Castle Wolfenstein!!
In fact, it’s a same same approach that be done inside effect file with shading programming. I would guess that because I saw everything shaded into white, then I suddenly gave up.
Wait! What’s wrong with particle fog?
Particle system can be implemented in 2 major ways.
- Triangle or Quad-based
- Point sprite
My current particle system uses a 2nd approach.
The resource needed for point sprite to represent one particle is one vertice compared to another way which may need 3 or 4 vertices based on technique of implementation. So it’s ideal to have point sprite in place for particle system as number of particles surely will be high. But it comes into some costs as well.
Firstly, Point sprite will be scaled up/down based on the distance to the camera. Thus whenever we go closer and closer, the particle (or point sprite) will be really small so we won’t be able to see nice detail of the particle itself. Consider that a building catches on fire. Far away, you clearly see its effect and recognize that it’s fire. But when you come closer, particles get smaller and smaller til you see the house itself which seems like nothing has done anything to it.
Secondly, in fact both of the approaches, it’s harder to control behavior of particle system although you have a good tool to simulate the behaviors and it acts like that in the real simulation. I believe for most hands-on code in purity, if it’s not designed to be a tool, it will have to be manually adjusted in terms of particle’s position, number of particles, direction it goes, size, randomness stuff, etc.
It would conclude that I should stay away from point sprite (in case of fog effect), and use triangle / quad - based instead. Anyway, a better approach is to use shader. This way we gain all control for whenever to do shading, fog’s settings in terms of density or anything else, and the most importantly, it’s uniform on the whole screen buffer.
Side-note: Shawn Hargreaves stated in his post that point sprite won’t be supported in XNA 4.0. DirectX 10 and 11 will cut out its future from there as triangle / quad - baseds is proved to be as efficient and sometime better than point sprite approach.
Okay, here the things
Each individual entity has its own effect (or shader) file that describes how to render itself on screen. Surely, every of my shader files include one common include-file where inside has basic operations in both terms of transformation, and pixel shading that they can use and adapt up on to more specific operation.
Fog related variables lie inside that include-file. The following is HLSL language declares fog related variables as shared ones across all shader files as I want fog to be globally effect. Just set its settings once, then every single entity, every mesh in simulation will be rendered with consideration of fog shading.
// -- Fog properties --
// Linear Fog
shared float fogNear;
shared float fogFar;
shared float fogAltitudeScale;
shared float fogThinning;
// Exponential Fog
shared float fogDensity;
// Others
shared float4 fogColor;
shared bool fogEnabled;
shared int fogFormulae; // 1 = LINEAR FOG, 2 = Exponential Fog, 3 = Exponential Fog ^ 2
shared const float E_CONST = 2.71828;
To be more elaborate on the above variables, I researched through the Internet and found this one. Its information dated back in time of DirectX 9, but the theory still holds. A notable graph comparison at the bottom is really nice and blowing benefit.
I decided to implement them all for 3 types of fog which are Linear, Exponential 1 and 2. That graph tells everything! Whenever you want any other behavior of fog, you compare them and choose what’s right for you.
// Linear Fog
float4 LinearFogPS(float4 color, float3 worldPosition)
{
float d = length(worldPosition - cameraPosition);
float l = saturate((d - fogNear) / (fogFar - fogNear) / clamp(worldPosition.y / fogAltitudeScale + 1, 1, fogThinning));
return lerp(color, fogColor, l);
}
// Exponential Fog
float4 ExpoFogPS(float4 color, float3 worldPosition)
{
float f = pow(E_CONST, length(worldPosition - cameraPosition) * fogDensity);
f = 1.0 / f;
return saturate(f * color + (1-f) * fogColor);
}
// Exponential 2 Fog
float4 Expo2FogPS(float4 color, float3 worldPosition)
{
float f = pow(E_CONST, pow(length(worldPosition - cameraPosition) * fogDensity, 2));
f = 1.0 / f;
return saturate(f * color + (1-f) * fogColor);
}
saturate() is added for sane of color stability. Note f*color + (1-f)*fogColor that merely explains the essential of f by means of “how much should color should be seen after combined with fog color”. This is why Maths is so lovely in hidden message, it won’t directly speak in normal human language but instead purity of its own tradition.
fogFormulae will be a key for us as I will allow users or whoever uses my framework to be able to instantly change the method to calculate fog on-the-fly. Thus I wrote a handy function just to check for which way to calculate for fog. This function will be used across shader files.
float4 fogFormulaePS(float4 color, float3 worldPosition)
{
if(fogFormulae == 1)
return LinearFogPS(color, worldPosition);
else if(fogFormulae == 2)
return ExpoFogPS(color, worldPosition);
else if(fogFormulae == 3)
return Expo2FogPS(color, worldPosition);
else
return color;
}
This means that caller has a sole responsibility to send-in forFormulae value into shader file as well as other related fog variables. But as long as they don’t frequently change the fog setting, they just have to do it once and for all.
Now, we already set things up.
Just a side-note for myself, an include-file is going bigger and bigger. Once only pixel shader model (PS Model) version 2 is a way to go, but as things go bigger, I have to change it to version 3. This is a two-sword decision. It would cut users who use PS v2 capable graphics card, and target only for those who use PS v3 and above. Anyway, think it in a positive way, as my framework is bandwidth hunger, setting it to PS v3 will told the requirement for any system to run it in the first place. Misunderstand and self-thinking would not happen.
Now, it’saa bout time!
So it’s time to make use of the things we already set up previously. I excerpted the code inside airport’s shader file of pixel shader function to let you see how we take the code into a real production.
RenderGBufferPixelOutput RenderGBufferPS(RenderGBufferPixelInput input)
{
RenderGBufferPixelOutput output;
//Color
//Lerping base and target texture
//get color from baseTexture
float4 baseColor = tex2D(baseSampler, input.TexCoords);
//get color from targetTexture
float4 targetColor = tex2D(targetSampler, input.TexCoords);
//lerp from baseTexture to targetTexture (the dTime must be set properly)
float4 blendedColor = lerp(baseColor, targetColor, dtTime);
// fog
if(fogEnabled)
blendedColor = fogFormulaePS(blendedColor, input.WorldPosition);
...
// do whatever you like here
...
Not to worry about why I do lerping here. I have 3 different weathers so there’s some chance that some parts of airport would do lerping from normal weather texture to snow weather texture.
Line to note is commented out with “// fog”. We just test it whether at the moment fog is enabled to apply or not, if so then we use our handy function to handle decision over calculating fog on each pixel whether it’s Linear, Exponential 1 or 2. We plug in the color, and its world position.
See the result
See the full gallery below of what the result may look like. I reserve the space to the next post for another simple technique I implemented in parallel to fog (as in my case) in order to make it looks much better along the edge of the ground map interfacing with skydome.
Til next time.
posted 4 weeks ago