17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-31 19:19:03 +01:00

Steady the volumetric fog and add a switch for the old headlight beams

This commit is contained in:
2026-10-16 23:23:51 +02:00
parent 8b929fc1da
commit 6ee628aaa5
9 changed files with 114 additions and 32 deletions

View File

@@ -2,7 +2,8 @@
// depth), how dense the fog is and how much light it scatters towards the camera. the density: the haze, the patches of the
// ground fog, the low clouds; the light: the colour the fog has towards the horizon (weather_common.glsl), less what the
// shadows of the sun and of the clouds hold back, and the lamps and headlights of the light clusters. blended with the
// previous frame, found where the cell was then; the cells are sampled at another depth every frame.
// previous frame, found where the cell was then; the cells are sampled at another point every frame, each cell going through
// the points from a place of its own, so the volume doesn't flicker as a whole and the blending smooths the edges of the cells.
// rgb: light scattered towards the camera per metre, a: extinction per metre
#include <common>
@@ -22,7 +23,8 @@ layout(binding = HEADLIGHT_SHADOW_TEX) uniform sampler2DArrayShadow headlight_sh
layout(location = 0) uniform ivec3 grid;
layout(location = 1) uniform mat4 inverse_projection;
layout(location = 2) uniform mat4 view_to_world;
// x: near, y: far, z: slices / log(far / near), w: where in its slice the cell is sampled this frame (0-1)
// x: near, y: far, z: slices / log((far + FOG_VOLUME_BIAS) / (near + FOG_VOLUME_BIAS)), w: where in its slice the cell is
// sampled this frame (0-1)
layout(location = 3) uniform vec4 slicing;
// world axes to the clip space of the previous frame, the camera of the previous frame at the origin
layout(location = 4) uniform mat4 previous_projection_view;
@@ -32,8 +34,41 @@ layout(location = 5) uniform vec3 previous_camera;
layout(location = 6) uniform vec4 options;
// the light of the cab lamp, which lights no fog (the air of the cab is clear), or -1
layout(location = 7) uniform int cablamp;
// x: near, y: slices / log(far / near) of the previous frame
// x: near, y: slices / log((far + FOG_VOLUME_BIAS) / (near + FOG_VOLUME_BIAS)) of the previous frame
layout(location = 8) uniform vec2 previous_slicing;
// where in its cell the cell is sampled this frame across the view (0-1), as slicing.w is along the depth
layout(location = 9) uniform vec2 jitter;
// ray of the view space through a point of the view (0-1), one metre of the depth long
vec3 view_ray(vec2 coords)
{
vec4 near = inverse_projection * vec4(coords * 2.0 - 1.0, 1.0, 1.0);
vec3 ray = near.xyz / near.w;
return ray / -ray.z;
}
// view depth at a place along the slices: slice number and the part of the slice
float slice_depth(float slice)
{
return (slicing.x + FOG_VOLUME_BIAS) * exp(slice / slicing.z) - FOG_VOLUME_BIAS;
}
// where in the cell it is sampled this frame, 0-1 on each axis: the point of the sequence of the frames moved by a value of
// the cell's own (pcg3d hash), the middle without the blending
vec3 cell_offset(ivec3 cell)
{
if (options.x >= 1.0)
return vec3(0.5);
uvec3 v = uvec3(cell) * 1664525U + 1013904223U;
v.x += v.y * v.z;
v.y += v.z * v.x;
v.z += v.x * v.y;
v ^= v >> 16U;
v.x += v.y * v.z;
v.y += v.z * v.x;
v.z += v.x * v.y;
return fract(vec3(jitter, slicing.w) + vec3(v >> 8U) * (1.0 / 16777216.0));
}
float henyey_greenstein(float cosangle, float g)
{
@@ -108,12 +143,10 @@ void main()
ivec3 cell = ivec3(gl_GlobalInvocationID);
if (any(greaterThanEqual(cell, grid)))
return;
vec2 coords = (vec2(cell.xy) + 0.5) / vec2(grid.xy);
vec4 near = inverse_projection * vec4(coords * 2.0 - 1.0, 1.0, 1.0);
vec3 ray = near.xyz / near.w;
ray /= -ray.z;
float depth = slicing.x * exp((float(cell.z) + slicing.w) / slicing.z);
vec3 view = ray * depth;
vec3 offset = cell_offset(cell);
vec2 coords = (vec2(cell.xy) + offset.xy) / vec2(grid.xy);
float depth = slice_depth(float(cell.z) + offset.z);
vec3 view = view_ray(coords) * depth;
vec3 world = mat3(view_to_world) * view;
float height = weather_camera.x + world.y;
@@ -161,14 +194,17 @@ void main()
result = vec4(light * fog_haze.w * extinction, extinction);
}
// the previous frame, where the cell was then
// the previous frame, where the middle of the cell was then: the cells of the history hold what the cells were on the whole,
// not at the point they were sampled at
float share = options.x;
if (share < 1.0)
{
vec4 clip = previous_projection_view * vec4(world - previous_camera, 1.0);
vec3 centre = mat3(view_to_world) * (view_ray((vec2(cell.xy) + 0.5) / vec2(grid.xy)) * slice_depth(float(cell.z) + 0.5));
vec4 clip = previous_projection_view * vec4(centre - previous_camera, 1.0);
if (clip.w > 0.0)
{
vec3 previous = vec3(clip.xy / clip.w * 0.5 + 0.5, log(max(clip.w / previous_slicing.x, 1e-6)) * previous_slicing.y / float(grid.z));
float previousslice = log(max((clip.w + FOG_VOLUME_BIAS) / (previous_slicing.x + FOG_VOLUME_BIAS), 1e-6)) * previous_slicing.y;
vec3 previous = vec3(clip.xy / clip.w * 0.5 + 0.5, previousslice / float(grid.z));
if (all(greaterThanEqual(previous, vec3(0.0))) && all(lessThanEqual(previous, vec3(1.0))))
result = mix(textureLod(fog_history, previous, 0.0), result, share);
}

View File

@@ -12,7 +12,7 @@ layout(rgba16f, binding = 0) uniform writeonly image3D fog_integrated;
layout(location = 0) uniform ivec3 grid;
layout(location = 1) uniform mat4 inverse_projection;
// x: near, y: far, z: slices / log(far / near)
// x: near, y: far, z: slices / log((far + FOG_VOLUME_BIAS) / (near + FOG_VOLUME_BIAS))
layout(location = 3) uniform vec4 slicing;
void main()
@@ -31,7 +31,7 @@ void main()
float front = slicing.x;
for (int slice = 0; slice < grid.z; slice++)
{
float back = slicing.x * exp(float(slice + 1) / slicing.z);
float back = (slicing.x + FOG_VOLUME_BIAS) * exp(float(slice + 1) / slicing.z) - FOG_VOLUME_BIAS;
float span = (back - front) * stretch;
front = back;
vec4 cell = texelFetch(fog_scattering, ivec3(column, slice), 0);

View File

@@ -8,6 +8,29 @@
layout(binding = FOG_VOLUME_TEX) uniform sampler3D fog_volume;
// the volume at a point of the view (0-1) and a coordinate along the slices, filtered across the view with a cubic B-spline
// (four fetches of the linear filter): a cell is several pixels wide, and the linear filter alone draws the shafts of light and
// the edges of shadows in the fog as steps
vec4 fog_volume_at(vec2 coords, float slice)
{
vec2 size = vec2(textureSize(fog_volume, 0).xy);
vec2 texel = coords * size - 0.5;
vec2 base = floor(texel);
vec2 f = texel - base;
vec2 f2 = f * f;
vec2 f3 = f2 * f;
vec2 w0 = (1.0 - 3.0 * f + 3.0 * f2 - f3) / 6.0;
vec2 w1 = (4.0 - 6.0 * f2 + 3.0 * f3) / 6.0;
vec2 w2 = (1.0 + 3.0 * f + 3.0 * f2 - 3.0 * f3) / 6.0;
vec2 w3 = f3 / 6.0;
vec2 g0 = w0 + w1;
vec2 g1 = w2 + w3;
vec2 h0 = (base - 0.5 + w1 / g0) / size;
vec2 h1 = (base + 1.5 + w3 / g1) / size;
return g0.y * (g0.x * textureLod(fog_volume, vec3(h0.x, h0.y, slice), 0.0) + g1.x * textureLod(fog_volume, vec3(h1.x, h0.y, slice), 0.0))
+ g1.y * (g0.x * textureLod(fog_volume, vec3(h0.x, h1.y, slice), 0.0) + g1.x * textureLod(fog_volume, vec3(h1.x, h1.y, slice), 0.0));
}
// rgb: light the fog adds, a: what it lets through
vec4 fog_along_view(vec3 view, vec2 fragcoord)
{
@@ -17,16 +40,16 @@ vec4 fog_along_view(vec3 view, vec2 fragcoord)
vec3 direction = mat3(inv_view) * (view / span_distance);
if (fog_volume_params.x < 0.5)
return fog_analytic(direction, 0.0, span_distance, false);
// slices are spaced exponentially in the view depth; slice k holds the fog up to its far side
// slices are spaced exponentially in the view depth plus FOG_VOLUME_BIAS; slice k holds the fog up to its far side
float depth = max(-view.z, 1e-4);
float slices = fog_volume_params.w;
float slice = log(max(depth / fog_volume_params.y, 1e-6)) * fog_volume_params.z;
float slice = log(max((depth + FOG_VOLUME_BIAS) / (fog_volume_params.y + FOG_VOLUME_BIAS), 1e-6)) * fog_volume_params.z;
vec2 coords = fragcoord * fog_volume_target.xy;
if (slice < 1.0)
return mix(vec4(0.0, 0.0, 0.0, 1.0), textureLod(fog_volume, vec3(coords, 0.5 / slices), 0.0), max(slice, 0.0));
return mix(vec4(0.0, 0.0, 0.0, 1.0), fog_volume_at(coords, 0.5 / slices), max(slice, 0.0));
if (slice < slices)
return textureLod(fog_volume, vec3(coords, (slice - 0.5) / slices), 0.0);
vec4 volume = textureLod(fog_volume, vec3(coords, (slices - 0.5) / slices), 0.0);
return fog_volume_at(coords, (slice - 0.5) / slices);
vec4 volume = fog_volume_at(coords, (slices - 0.5) / slices);
vec4 rest = fog_analytic(direction, fog_volume_target.z * span_distance / depth, span_distance, false);
return vec4(volume.rgb + volume.a * rest.rgb, volume.a * rest.a);
}
@@ -40,7 +63,7 @@ vec4 fog_sky(vec3 viewdirection, vec2 fragcoord)
if (fog_volume_params.x < 0.5 || view.z > -1e-3)
return fog_analytic(direction, 0.0, 200000.0, true);
float slices = fog_volume_params.w;
vec4 volume = textureLod(fog_volume, vec3(fragcoord * fog_volume_target.xy, (slices - 0.5) / slices), 0.0);
vec4 volume = fog_volume_at(fragcoord * fog_volume_target.xy, (slices - 0.5) / slices);
vec4 rest = fog_analytic(direction, fog_volume_target.z / -view.z, 200000.0, true);
return vec4(volume.rgb + volume.a * rest.rgb, volume.a * rest.a);
}