Ground fog algorithm

Hello everyone.

I’m trying to implement a ground fog shader for my terrain rendering engine.
The technique is described in this article : Inigo Quilez :: computer graphics, mathematics, shaders, fractals, demoscene and more

The idea is to consider the ray going from the camera to the fragment and integrate the fog density function along this ray.

Here’s my shader code :


#version 330 core
in vec2 UV;
in vec3 posw;

out vec3 color;

uniform sampler2D tex;

uniform vec3 ambientLightColor;
uniform vec3 camPos;

const vec3 FogBaseColor = vec3(1., 1., 1.);

void main()
{
    vec3 light = ambientLightColor;
    vec TexBaseColor = texture(tex,UV).rgb;

    //***************************FOG********************************************
    vec3 camFrag = posw - camPos;
    float distance = length(camFrag);
    float a = 0.02;
    float b = 0.01;
 
    float fogAmount = a * exp(-camPos.z*b) * ( 1.0-exp( -distance*camFrag.z*b ) ) / (b*camFrag.z);
    color = mix( light*TexBaseColor, light*FogBaseColor, fogAmount );
}

The first thing is that I don’t understand how to choose a and b and what are their physical role in the fog algorithm.

Then, the result is not what I expect…
I have a ground fog but the transition of fogAmount from 0 to 1 is always centered at the camera altitude. I’ve tried a lot of different a and b but when I don’t have a transition at camera altitude, I either have a full fogged or not fogged at all terrain.

I checked the data I use and everything’s correct :

  • camPos.z is the altitude of my camera
  • camFrag.z is the vertical component of the vector going from the camera to the fragment

I can’t get to understand what part of the equation cause this.

Any idea about this ?

Thank you very much.

Some explanations, based on my understanding:

  • a is the factor that defines the overall density of the fog. The integral formula returns one specific value which is then modified by a, so if you have for example your camera at (100, 100, 100) and looking at (0, 0, 0), the formula would give: a * exp(-1) * (1 - exp(-1731)) / 100). (I think you have a mistake here, (bcamFrag.z) should just be (camFrag.z), according to the original formula). The second value is so ridiculously close to 1 that I assume there is a mistake, maybe caused by not normalizing the direction (you could try modifying that). So the formula gives a * exp(-1) * 1, so a has to be e (2.718) to always cause complete fog at height 100. 0.02 sounds a little small, maybe try 0.2.

  • b is a factor that defines the density increase of the fog. The higher b is, the less the fog will be taken into account by increasing distance (think of it as how far you can see in the fog). Here, depending on the height, the parameter might even be far too big, try dividing it by 10.

Any helpful? :slight_smile:

Thank you Brokenmind and sorry for the delay, thought nobody had an answer.

I don’t understand why b dissappears from the denominator since it is the result of the integration of exp(-bKyt) from 0 to T which gives (1-exp(-bKyT))/(b*Ky).

Thank you for the explanation of a and b factors.
I finally found some values of a and b that give something that looks more or less like ground fog but there’s some problem :

  • This function behaves bad and create artifacts when my camera’s altitude is high. Fog dissappears at some point and if I keep climbing, it comes back but in a binary way and moving with the camera.
  • This approach seems not to be designed to create layered fog, i.e. one or several parameterized layers (altitude, density). I think it is used to create a global atmospheric fog and take into account the fact that the air density varies exponentially with altitude.

What I’m looking for is this kind of effect : http://www.prepar3d.com/wp-content/uploads/2013/10/v2ExampleFog2On.jpg
The integration of a density function along a vector seems to be the good way to achieve this but I have to manage my density with something else than the exp function.

It’s really hard to find articles or implementation examples of layered fog.

I recently found this article and will see what I can get out of it : http://www.terathon.com/lengyel/Lengyel-UnifiedFog.pdf

Damn it, you made me curious.

I looked for an old 3D program (procedural terrain creation in an early stage) to experiment a bit on the formula. I couldn’t get the formula to run, but managed to develop something myself. Maybe it’s helpful!
I inserted two “variables” like in Iñigo Quilez’s post. Some screenshots with differing variables are included below.

In the paper you linked, they mention the angle. This is not included in my version, but you can easily adapt this. If you don’t have an upper fog bound like below, you could even define an upper bound for the log function. This would also create fog in valleys that are “local but no global minima”.


vec3 rayDir = pos - vCamPosition;
float dist = length(rayDir); // distance is a keyword in GLSL

const float maxFogHeight = 900; // higher the fog won't go
const float c = 0.1;
const float b = 0.2;

// if this is not done, the result will look awful
if(pos.z >= maxFogHeight - 1 / c)
{		
    return;
}

// distance in fog is calculated with a simple intercept theorem
float distInFog = dist * (maxFogHeight - pos.z) / (pos.z - vCamPosition.z);

// when dist is 0, log(dist) is 1, so subtract this
float fogAmount = (log(distInFog * c) - 1) * b;

// at the top border, the value can get greater than 1, so clamp
fogAmount = clamp(fogAmount, 0, 1);

// final mix of colors
gl_FragColor = mix(gl_FragColor, fogColor, fogAmount);

Without fog:

With fog (b = 0.2, c = 0.1):

With fog (b = 0.3, c = 0.3):

Hehe ! Terrain and weather rendering is a really interesting subject.

Your fog effect is looking really good. It’s a good simplification if you’re looking for a layered fog that will only be seen from above.
But if you put your camera just at the edge or into your fog layer, you’ll see that doesn’t work anymore and you need a more accurate distInFog computation.

I’ll come back as soon as I’ve some interesting result with Halfspace fog method.

That’s it !

That halfspace fog method works perfectly ! I had some mathematical problems to solve because my terrain is based on an ellipsoid.
The result is exactly what I was looking for : a layer of fog with density and altitude parameters, progressively blending everything when you go through it.

The shader code is in the article. I implemented the linearly varying density method.

Thank you Brokenmind for your help.

That’s it !

That halfspace fog method works perfectly ! I had some mathematical problems to solve because my terrain is based on an ellipsoid.
The result is exactly what I was looking for : a layer of fog with density and altitude parameters, progressively blending everything when you go through it.

The shader code is in the article. I implemented the linearly varying density method.

Thank you Brokenmind for your help.