GLSL Implementation of Fog Filter (LINEAR, EXP2_CAMERA_TO_DISTANCE & EXP2_DISTANCE_TO_INFINITY)

Here be the code… adding screens after all



Before the code… here is the usage:

[java]

// linear fog - mode, color, useless-density, start-distance, end-distance

FogFilter fog = new FogFilter(FogFilter.FOG_MODE.LINEAR, ColorRGBA.White, 1.0f, 50f, 150f);



// exp2 cam to distance fog - mode, color, density, distance, useless-distance-number

FogFilter fog = new FogFilter(FogFilter.FOG_MODE.EXP2_CAM_TO_DISTANCE, ColorRGBA.White, 1.0f, 50f, 150f);



// exp2 distance to infinity fog - mode, color, density, distance, useless-distance-number

FogFilter fog = new FogFilter(FogFilter.FOG_MODE.EXP2_DISTANCE_TO_INFINITY, ColorRGBA.White, 1.0f, 50f, 100f);



// Excluding sky bucket

fog.setExcludeSky(true);

[/java]



screens use the following setting with a change of modes:

[java]fog.setFogColor(new ColorRGBA(0.3f, 0.5f, 0.3f, 1.0f));

fog.setFogStartDistance(50f);

fog.setFogEndDistance(125f);

fog.setFogDensity(1.0f);

fog.setExcludeSky(true);[/java]

LINEAR:



EXP2_DISTANCE_TO_INFINITY:



EXP2_CAM_TO_DISTANCE:





Note about this filter:

The equations for calculating fog depth are home-brewed… so do me a favor and let me know if they are not matching up with your model scale so I can adjust them accordingly. As far as I can tell… they should be proper.



Other things to keep in mind:

Density has no effect on Linear fog.

1.0 is normal maximum density… however… in EXP2_DISTANCE_TO_INFINITY, raising this past 1.0 will bring infinity closer and closer :wink:

Density (0.0 to 1.0) in EXP2_CAMERA_TO_DISTANCE adjusts the exp2 curve of the fog. values outside of this range? I have no clue on > 1.0 … however, I doubt < 0.0 would do anything…



Filter:

FogFilter.java

[java]/*

  • To change this template, choose Tools | Templates
  • and open the template in the editor.

    */

    package mygame;



    import com.jme3.asset.AssetManager;

    import com.jme3.export.InputCapsule;

    import com.jme3.export.JmeExporter;

    import com.jme3.export.JmeImporter;

    import com.jme3.export.OutputCapsule;

    import com.jme3.material.Material;

    import com.jme3.math.ColorRGBA;

    import com.jme3.post.Filter;

    import com.jme3.renderer.RenderManager;

    import com.jme3.renderer.ViewPort;

    import java.io.IOException;



    /**
  • A filter to render the GLSL implementation of a fog effect
  • @author t0neg0d

    */

    public class FogFilter extends Filter {

    public static enum FOG_MODE {

    LINEAR,

    EXP2_CAM_TO_DISTANCE,

    EXP2_DISTANCE_TO_INFINITY

    }

    private ColorRGBA fogColor = ColorRGBA.White.clone();

    private float fogDensity = 1.0f;

    private float fogStartDistance = 200f;

    private float fogEndDistance = 500f;

    private boolean excludeSky = false;

    private FOG_MODE fogMode = FOG_MODE.EXP2_CAM_TO_DISTANCE;



    /**
  • Creates a FogFilter

    */

    public FogFilter() {

    super("FogFilter");

    }



    /**
  • Create a fog filter
  • @param fogMode the mode to use for rendering fog (default is EXP2_CAM_TO_DISTANCE)
  • @param fogColor the color of the fog (default is white)
  • @param fogDensity the density of the fog (default is 1.0)
  • @param fogStartDistance Start distance is the absolute distance of the fog in mode EXP2_CAM_TO_DISTANCE and the start dstance in modes LINEAR and EXP2_DISTANCE_TO_INFINITY (default is 200)
  • @param fogEndDistance End distance (100% density) in mode LINEAR

    */

    public FogFilter(FOG_MODE fogMode, ColorRGBA fogColor, float fogDensity, float fogStartDistance, float fogEndDistance) {

    this();

    this.fogMode = fogMode;

    this.fogColor = fogColor;

    this.fogDensity = fogDensity;

    this.fogStartDistance = fogStartDistance;

    this.fogEndDistance = fogEndDistance;

    }



    @Override

    protected boolean isRequiresDepthTexture() {

    return true;

    }



    @Override

    protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {

    material = new Material(manager, "MatDefs/Fog.j3md");

    material.setInt("FogMode", fogMode.ordinal());

    material.setColor("FogColor", fogColor);

    material.setFloat("FogDensity", fogDensity);

    material.setFloat("FogStartDistance", fogStartDistance);

    material.setFloat("FogEndDistance", fogEndDistance);

    material.setBoolean("ExcludeSky", excludeSky);

    }



    @Override

    protected Material getMaterial() {



    return material;

    }



    /**
  • setsthe fog mode (default is EXP2_CAM_TO_DISTANCE)
  • @param fogMode

    */

    public void setFogMode(FOG_MODE fogMode) {

    if (material != null) {

    material.setInt("FogMode", fogMode.ordinal());

    }

    this.fogMode = fogMode;

    }



    /**
  • returns the fog mode (default is EXP2_CAM_TO_DISTANCE)
  • @return fogMode

    */

    public FOG_MODE getFogMode() {

    return this.fogMode;

    }



    /**
  • returns the fog color
  • @return fogColor

    */

    public ColorRGBA getFogColor() {

    return fogColor;

    }



    /**
  • Sets the color of the fog
  • @param fogColor

    */

    public void setFogColor(ColorRGBA fogColor) {

    if (material != null) {

    material.setColor("FogColor", fogColor);

    }

    this.fogColor = fogColor;

    }



    /**
  • returns the fog density
  • @return

    */

    public float getFogDensity() {

    return fogDensity;

    }



    /**
  • Sets the density of the fog, a high value gives a thick fog
  • @param fogDensity

    */

    public void setFogDensity(float fogDensity) {

    if (material != null) {

    material.setFloat("FogDensity", fogDensity);

    }

    this.fogDensity = fogDensity;

    }



    /**
  • returns the fog start distance
  • @return fogStartDistance

    */

    public float getFogStartDistance() {

    return fogStartDistance;

    }



    /**
  • Start distance is the absolute distance of the fog in mode EXP2_CAM_TO_DISTANCE and the start dstance in modes LINEAR and EXP2_DISTANCE_TO_INFINITY (default is 200)
  • @param fogStartDistance

    */

    public void setFogStartDistance(float fogStartDistance) {

    if (material != null) {

    material.setFloat("FogStartDistance", fogStartDistance);

    }

    this.fogStartDistance = fogStartDistance;

    }



    /**
  • returns the fog end distance
  • @return fogEndDistance

    */

    public float getFogEndDistance() {

    return fogEndDistance;

    }



    /**
  • End distance (100% density) in mode LINEAR
  • @param fogEndDistance

    */

    public void setFogEndDistance(float fogEndDistance) {

    if (material != null) {

    material.setFloat("FogEndDistance", fogEndDistance);

    }

    this.fogEndDistance = fogEndDistance;

    }



    /**
  • Sets the exclude sky flag
  • @param excludeSky

    */

    public void setExcludeSky(boolean excludeSky) {

    if (material != null) {

    material.setBoolean("ExcludeSky", excludeSky);

    }

    this.excludeSky = excludeSky;

    }



    /**
  • returns the exclude sky flag
  • @return

    */

    public boolean getExcludeSky() {

    return excludeSky;

    }



    @Override

    public void write(JmeExporter ex) throws IOException {

    super.write(ex);

    OutputCapsule oc = ex.getCapsule(this);

    oc.write(fogColor, "fogColor", ColorRGBA.White.clone());

    oc.write(fogDensity, "fogDensity", 0.7f);

    oc.write(fogStartDistance, "fogStartDistance", 200f);

    oc.write(fogEndDistance, "fogEndDistance", 500f);

    oc.write(excludeSky, "excludeSky", false);

    }



    @Override

    public void read(JmeImporter im) throws IOException {

    super.read(im);

    InputCapsule ic = im.getCapsule(this);

    fogColor = (ColorRGBA) ic.readSavable("fogColor", ColorRGBA.White.clone());

    fogDensity = ic.readFloat("fogDensity", 0.7f);

    fogStartDistance = ic.readFloat("fogStartDistance", 200);

    fogEndDistance = ic.readFloat("fogEndDistance", 500);

    excludeSky = ic.readBoolean("excludeSky", false);

    }





    }

    [/java]



    Material Definition:

    Fog.j3md

    [java]MaterialDef Fog {



    MaterialParameters {

    Int NumSamples

    Int NumSamplesDepth

    Texture2D Texture

    Texture2D DepthTexture

    Int FogMode;

    Vector4 FogColor;

    Float FogDensity;

    Float FogStartDistance;

    Float FogEndDistance;

    Boolean ExcludeSky : false

    }



    Technique {

    VertexShader GLSL100: Common/MatDefs/Post/Post.vert

    FragmentShader GLSL100: Shaders/Fog.frag



    WorldParameters {

    WorldViewProjectionMatrix

    }

    }

    }[/java]



    Shader:

    Fog.frag

    [java]uniform sampler2D m_Texture;

    uniform sampler2D m_DepthTexture;

    varying vec2 texCoord;



    uniform int m_FogMode;

    uniform vec4 m_FogColor;

    uniform float m_FogDensity;

    uniform float m_FogStartDistance;

    uniform float m_FogEndDistance;

    uniform bool m_ExcludeSky;



    uniform float g_Time;



    const float LOG2 = 1.442695;



    float computeLinearFogFactor(in float depth, in vec2 frustumNear, in vec2 frustumFar) {

    float depthS = (2.0 * frustumNear.x) / (frustumNear.y + frustumNear.x * (frustumNear.y - frustumNear.x));

    float depthE = (2.0 * frustumFar.x) / (frustumFar.y + frustumFar.x * (frustumFar.y - frustumFar.x));

    float fogDepth = 1.0/((frustumFar.y) /

    (frustumNear.y - depth

    (frustumNear.y)));

    float fogFactor;

    fogFactor = (depthE - fogDepth) / (depthE - depthS);

    fogFactor = clamp( fogFactor, 0.0, 1.0 );

    return fogFactor;

    }



    float computeExp2FogFactorC2D(in float depth, in vec2 frustumNear) {

    float depthS = 1.0-(2.0 * frustumNear.x) / (frustumNear.y + frustumNear.x * (frustumNear.y-frustumNear.x));

    float fogDepth = ((depthS) /

    ((frustumNear.y) - depth

    ((frustumNear.y))))
    (depthS
    4.0);

    float fogFactor = 0.0;

    if (depth < depthS)

    fogFactor = exp2( -m_FogDensity * m_FogDensity * fogDepth * fogDepth * LOG2 );

    fogFactor = clamp(fogFactor, 0.0, 1.0);

    return fogFactor;

    }



    float computeExp2FogFactorD2I(in float depth, in vec2 frustumNear) {

    float depthS = 1.0-(2.0 * frustumNear.x) / (frustumNear.y + frustumNear.x * (frustumNear.y-frustumNear.x));

    float fogDepth = 1.0-((depthS) /

    ((frustumNear.y) - depth *

    ((frustumNear.y))));

    float fogFactor = 1.0;

    if (depth > depthS)

    fogFactor = exp2( -m_FogDensity * m_FogDensity * fogDepth * fogDepth * LOG2 );

    fogFactor = clamp(fogFactor, 0.0, 1.0);

    return fogFactor;

    }



    void main() {

    vec4 texVal = texture2D(m_Texture, texCoord);

    float depth = texture2D(m_DepthTexture,texCoord).r;

    vec2 frustumNear = vec2(1.0,m_FogStartDistance);

    vec2 frustumFar = vec2(1.0,m_FogEndDistance);



    float fogFactor;



    if (m_FogMode == 0) fogFactor = computeLinearFogFactor(depth, frustumNear, frustumFar);

    if (m_FogMode == 1) fogFactor = computeExp2FogFactorC2D(depth, frustumNear);

    if (m_FogMode == 2) fogFactor = computeExp2FogFactorD2I(depth, frustumNear);



    if (m_ExcludeSky) {

    if (depth < 1.0)

    gl_FragColor = mix(m_FogColor,texVal,fogFactor);

    else

    gl_FragColor = texVal;

    } else

    gl_FragColor = mix(m_FogColor,texVal,fogFactor);



    }[/java]
5 Likes

awesome, looks good!

Thanks a lot, I have had the thought that excluding the sky bucket from the fog was what I wanted and now I can have it :slight_smile:



No fog

http://i.imgur.com/o3k50.png



FogFilter.FOG_MODE.EXP2_DISTANCE_TO_INFINITY starting at 200

http://i.imgur.com/A1OfK.png

@t0neg0d said:
Here be the code... adding screens after all
...
[java]float computeLinearFogFactor(in float depth, in vec2 frustumNear, in vec2 frustumFar) {
float fogDepth = 1.0/((frustumFar) /
(frustumNear - depth *
(frustumNear)));[/java]


I get compiler errors on the Mac. Changing to:
[java]
float fogDepth = 1.0/((frustumFar.y) /
(frustumNear.y - depth *
(frustumNear.y)));
[/java]
works fine (looks like a simple oversight in linearFogFactor).
1 Like

@jmaasing

Thanks for this. There is also an issue with computeExp2FogFactorC2D (I think)… but since this a duplication of the existing fog filter, I’ll probably just remove it instead of fix it. Linear and Distance to Infinity were the ones I was most interested in having.