Add fog to Lighting.j3md

I have a custom lighting material that adds fog. In almost every discussion about fog it is mentioned that fog is best implemented in the material/shader as opposed to the post processor. I don’t mind implementing this into the default lighting shader (with ifdef preprocessors so it doesnt affect performance if you don’t “enable” fog).

There’s a cheap, medium and expensive version so it suits all cases (low-end/android, etc).

Is there any opposition in doing so?


// @see: http://in2gpu.com/2014/07/22/create-fog-shader/
// @see: https://hub.jmonkeyengine.org/t/fog-glsl-library-for-materials/40943

// Float FogDensity: 0.05
// Float FogDensity: 0.003
// Float FogStart = 20.0
// Float FogEnd = 80.0

// .vert: varying float dist;
// .vert: dist = distance(g_CameraPosition, (g_WorldMatrix * modelSpacePos).xyz);

// pick one...
// .frag: vec4 fogResult = getFogLinear(gl_FragColor, fogColor, start, end, dist);
// .frag: vec4 fogResult = getFogExp(gl_FragColor, fogColor, m_FogDensity, dist);
// .frag: vec4 fogResult = getFogExpSquare(gl_FragColor, fogColor, m_FogDensity, dist);

vec4 getFogLinear(in vec4 diffuseColor, in vec4 fogColor, in float start, in float end, in float distance) {

    float fogFactor = (end - distance) / (end - start);
    fogFactor = clamp(fogFactor, 0.0, 1.0);

    return mix(fogColor, diffuseColor, fogFactor);
}

vec4 getFogExp(in vec4 diffuseColor, in vec4 fogColor, in float fogDensity, in float distance) {

    float fogFactor = 1.0 / exp(distance * fogDensity);
    fogFactor = clamp( fogFactor, 0.0, 1.0 );

    return mix(fogColor, diffuseColor, fogFactor);
}

vec4 getFogExpSquare(in vec4 diffuseColor, in vec4 fogColor, in float fogDensity, in float distance) {

    float fogFactor = 1.0 / exp( (distance * fogDensity) * (distance * fogDensity));
    fogFactor = clamp( fogFactor, 0.0, 1.0 );

    vec4 finalColor = mix(fogColor, diffuseColor, fogFactor);
    return finalColor;
}
6 Likes

Just a note… this will give you strange distances. g_CameraPosition is in world space but WorldViewMatrix is transforming to view space. Probably you want g_WorldMatrix here.

https://javadoc.jmonkeyengine.org/com/jme3/shader/UniformBinding.html#WorldMatrix

1 Like

Yes you’re right. I actually have that change in my shader, I just didn’t update the notes.

And probably for PBRLighting too ?

Building on Alis Question: can we implement it as glslib and just include it then?

2 Likes

Yeah I planned to make it a library so you can add it to other materials by copying the way its added. PBRLighting would just be a direct port virtually from Lighting, so if I do Lighting first, it’s just a couple of commits for PBR.

PR Submitted.

7 Likes

There have been no objections or scrutiny at this point. Shall I leave it another week?

i might be wrong, but i think it would be much easier for @pspeed if you could add code for PBRLighting too. i know it will be like copy paste mostly.

i also cant comment if something would be better to use in shader(because i dont know), for me it looks fine.

The point of not including it in all materials is so that the fog and implementation itself can be scrutinized, then expand it to include PBR and single-pass lighting.

ok got it :slight_smile:

i seen main functional code is in glsllib so it can be shared easly too.

seen this:
fog_distance = distance(g_CameraPosition, (g_WorldMatrix * modelSpacePos).xyz);

out of glsllib, i understand reason is to avoid recalculating it, but thought if its possible move it somehow to glsllib this 3 functions too. not required, but i just think of it.

Go ahead and push the merge button :slightly_smiling_face:

I’ll add it to SPLighting too so that Lighting.j3md is complete, merge the PR, then add it for PBRLighting and merge that PR. I might do all at once. Depends on time.

3 Likes

Ok. I’ve merged the fog for Lighting.j3md (single and multi-pass) and shortly will do the same for PBRLighting. I’ll update the docs, but I’ll write it here for now.

There are 3 types of fog:

  • linear (cheapest, best for android).
  • exponential (medium, better than linear).
  • exponential squared (best, most expensive).

Enable fog by setting “UseFog” to true in your material and setting a “FogColor”.

material.setBoolean("UseFog", true);
material.setColor("FogColor", new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f);

To determine the type of fog you’ll use, set the value for the fog you require.

Linear Fog

For linear fog you set a start and end value. The start value determines when the fog starts fading in and the end value determines when the fading will end. Any distance before start will not have fog and any distance after end will have full fog.

float start = 10.0f;
float end = 60.0f;
material.setVector2("LinearFog", new Vector2f(start , end));

The graph below shows the level of fog as a minimum distance of 10 and maximum distance of 60.

Exponential fog

Pretty simple stuff. It follows an exponential line. The larger the number the quicker the fog will reach full fog. The smaller the number the longer it will take to reach full fog.

float exp = 0.015f;
material.setFloat("ExpFog", exp);

Exponential Squared

Again - basic math functions. This method takes longer to fade in before “jumping” to a full rapid fade increase.

float expsq = 0.02f;
material.setFloat("ExpSqFog", expsq);

The image below shows all three functions together. Each line shows the increase in fog level over distance.

  • Red - Linear
  • Green - Exponential
  • Blue - Exponential Squared.

And finally, there is a test class available for those that speak code better than English :slight_smile:

9 Likes

I disapprove everything :penguin:, but i’m not here to discuss this, i just want to point out that there is another type of fog that you guys might be interested in: gradient based fog.
It’s very easy to implement, you only need to linearize the depth and use it to sample a gradient texture, the color and alpha of the sample will determine the fog color and density at that depth.

This guy implemented it in unity https://halisavakis.com/my-take-on-shaders-firewatch-multi-colored-fog/

9 Likes

Wow, looks amazing. :smiley:
It will probably best match with my gradient tree pack on the island.

1 Like

Is it possible to add this new material shader based Fog implementation in Lighting.j3md to TerrainLighting.j3md? Terrains look best with a bit of fog the color of the sky in the distance as a depth cue. And you can use it to hide the edges.

Yeah it is possible. I meant to update TerrainLighting.j3md to include this material fog when I added the PBR terrain shaders, but I forgot and never did it.

Although it is really easy to do if you have any experience editing the frag, vert, and j3md file of a shader. The fog code is minimal so it just requires copy/pasting a few lines of code to the terrain shader files, so I could try to submit a PR sometime soon if no one else does it first.

However If you use either of the new PBR terrain shaders (“PBRTerrain.j3md” or “AdvancedPBRTerrain.j3md” added as of version 3.4) then those do support material fog already.

3 Likes

Thanks! I’ll try the PBR terrain shaders. If they work, problem solved. Otherwise yeah i’ll have to learn how to make a custom MatDef with TerrainLighting.j3md + the fog from Lighting.j3md. I was trying to avoid going down the shader/MatDef programming rabbit (monkey?) hole but maybe it won’t be as hard as I think.

see also my solution: