Calculate fog from world position rather than camera

Godot Version

4.7.stable

Question

Is there any way to make fog (regular or volumetric) calculate from a given world position rather than the camera?

I tried with a post processor quad, but it has problems (transparency doesn’t work, artifacts, see down below). Can those be fixed?

Context

In my game I have a camera similar to that of many tab-targeting MMO’s. The camera is anchored to the player character, but you can zoom in and rotate as you please.

This causes problems when using fog. Since the amount of fog per pixel is calculated from the camera position, the scene changes drastically when you move around with the camera. Even if the player hasn’t moved, an object in the scene can go from hidden-in-the-fog to fully visible depending on how you place the camera. I want the fog on the screen to be invariant of camera positioning.


What I’ve tried

I have tried a post processing shader using a quad that calculates a pixels world position and compares it to the player character’s position.

This works fine, except:

  1. Materials using transparency sometimes gets hidden
  2. Artifacts appear in the edges between near and far away meshes


Custom fog post processing material.

shader_type spatial;
render_mode unshaded, fog_disabled;
uniform sampler2D depth_texture : source_color, hint_depth_texture;
varying mat4 CAMERA;
uniform float near_edge = 15.;
uniform float far_edge = 1000.;
uniform vec3 fog_color : source_color;
uniform vec3 fog_origin; // Player position, updated in script every frame

void vertex() {
  CAMERA = INV_VIEW_MATRIX;
  POSITION = vec4(VERTEX.xy, 1.0, 1.0);
}

float ramp(float val, float low, float upper) {
    return clamp( (val - low)/(upper - low), 0.01, 0.99);
}

void fragment() {
    float depth = texture(depth_texture, SCREEN_UV).x;
    vec3 ndc = vec3(SCREEN_UV * 2.0 - 1.0, depth);
    vec4 world = CAMERA * INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
    vec3 world_position = world.xyz / world.w;

    float dist = distance(fog_origin, world_position);
    float fog_amount = ramp(dist, near_edge, far_edge);

    ALPHA = fog_amount ;
    ALBEDO = fog_color;
}

That makes no sense. Fog effect is subjective, i.e. it’s always viewpoint dependent. For cases like yours best to use some heuristic to adjust the fog ranges depending on where the camera is in the scene and/or in respect to player.

Well, only if you implement it that way. (not my best sentence, sorry)

If it makes more sense, imagine a top down game where a character moves across the screen in a foggy bog. You want the fog to be denser far away from the character, and disappear close to the character. I want that effect but for 3rd person.

Can you make a visual mockup?

The standard fog is typically of constant density all through the volume. The illusion of “more density far away” is in fact a function of the distance from the eye. The “fogginess” accumulates through space/distance. The more space between the eye and an object, the more “dense” will fog veil appear for that object.

You may be mixing two different concepts here.

Make an animated mockup in a 3d app to check if your mental conception is in fact feasible.

Doing what I initially suggested - adjusting ranges depending on the camera relation to the player - might be a good enough solution for the problem of excessive fogginess when zoomed out.