17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-29 22:07:28 +01:00
Files
maszyna/shaders/vk14/taa_resolve.glsl

185 lines
8.5 KiB
GLSL

// the temporal anti-aliasing (gfx.antialiasing taa, gfx::temporal_aa): the frame, drawn with the projection moved by a part of a
// pixel, blended into the history of the last frames. the same in ogl46/ and vk14/; the stage file gives the images and
// TAA_HISTORY_VALID the history holds the last frame (false: the frame alone is taken)
// TAA_REPROJECTION the clip space of this frame (with the depth) to the one of the last frame, for the world standing still
// TAA_NEAR the near plane of the projection: a pixel is that much over its depth away (reverse z)
// the history: the colour (linear, hdr) and in alpha the lock of the thin things.
//
// the thin things (rails and wires far away, thinner than a pixel) are hit by some of the offsets only; a pixel of a frame which
// misses them has the colours of the ground or the sky all around, so the usual check of the history against the colours around
// the pixel would wipe them out of the history and they would flicker. a pixel where a frame shows a line across (brighter or
// darker than the neighbours on both sides, and not along) is locked: for a few frames its history is kept as it is, as long as the
// pixel belongs to the world standing still (where the place in the last frame is sure), moves slowly over the screen and is far
// enough not to be a part of the cab (the needles of the gauges, the screens) or of things passing close by
// the share of the frame in the result
const float TAA_CURRENT_SHARE = 0.1;
// the history kept within this many standard deviations of the colours around the pixel: wide for a pixel standing still on the
// screen, narrow for one moving across it this many pixels a frame
const float TAA_VARIANCE_WIDE = 1.5;
const float TAA_VARIANCE_NARROW = 1.0;
const float TAA_SPEED_NARROW = 8.0;
// the velocity drawn agrees with the world standing still within this many pixels (the velocity is a half float)
const float TAA_STILL_PIXELS = 0.25;
// the lock of a thin thing lasts as many frames as the offsets go round (gfx::temporal_aa), and holds while the pixel moves slower
// than this many pixels a frame
const float TAA_LOCK_DECAY = 1.0 / 8.0;
const float TAA_LOCK_SPEED_MIN = 2.0;
const float TAA_LOCK_SPEED_MAX = 6.0;
const float TAA_LOCK_DISTANCE_MIN = 20.0;
const float TAA_LOCK_DISTANCE_FULL = 40.0;
// a line across: brighter or darker than both neighbours by this share of the brightest of the three, and the neighbours along
// it closer to the pixel than half of that
const float TAA_THIN_CONTRAST = 0.1;
const float TAA_THIN_MINIMUM = 0.01;
// the largest value of a half float: the colour drawn kept within it, without the nan and the infinity which would stay in the
// history for good
const float TAA_HALF_MAX = 65504.0;
vec3 taa_clean(vec3 c)
{
return (any(isnan(c)) || any(isinf(c))) ? vec3(0.0) : clamp(c, vec3(0.0), vec3(TAA_HALF_MAX));
}
// the colour squeezed into 0..1 by its largest channel (a weight against the bright pixels), and back
vec3 taa_compress(vec3 c)
{
return c / (1.0 + max(c.r, max(c.g, c.b)));
}
vec3 taa_decompress(vec3 c)
{
return c / max(1.0 - max(c.r, max(c.g, c.b)), 1.0 / TAA_HALF_MAX);
}
vec3 taa_ycocg(vec3 c)
{
return vec3(0.25 * c.r + 0.5 * c.g + 0.25 * c.b, 0.5 * c.r - 0.5 * c.b, -0.25 * c.r + 0.5 * c.g - 0.25 * c.b);
}
vec3 taa_rgb(vec3 c)
{
return vec3(c.x + c.y - c.z, c.x + c.z, c.x - c.y - c.z);
}
// the history at the place, sharp: a catmull-rom spline through the texels, in five bilinear lookups (the corners left out)
vec3 taa_history_colour(vec2 uv, vec2 size)
{
vec2 position = uv * size;
vec2 texel1 = floor(position - 0.5) + 0.5;
vec2 f = position - texel1;
vec2 w0 = f * (-0.5 + f * (1.0 - 0.5 * f));
vec2 w1 = 1.0 + f * f * (-2.5 + 1.5 * f);
vec2 w2 = f * (0.5 + f * (2.0 - 1.5 * f));
vec2 w3 = f * f * (-0.5 + 0.5 * f);
vec2 w12 = w1 + w2;
vec2 uv0 = (texel1 - 1.0) / size;
vec2 uv3 = (texel1 + 2.0) / size;
vec2 uv12 = (texel1 + w2 / w12) / size;
vec3 result = textureLod(history_tex, vec2(uv12.x, uv0.y), 0.0).rgb * (w12.x * w0.y);
result += textureLod(history_tex, vec2(uv0.x, uv12.y), 0.0).rgb * (w0.x * w12.y);
result += textureLod(history_tex, uv12, 0.0).rgb * (w12.x * w12.y);
result += textureLod(history_tex, vec2(uv3.x, uv12.y), 0.0).rgb * (w3.x * w12.y);
result += textureLod(history_tex, vec2(uv12.x, uv3.y), 0.0).rgb * (w12.x * w3.y);
float weight = w12.x * w0.y + w0.x * w12.y + w12.x * w12.y + w3.x * w12.y + w12.x * w3.y;
return taa_clean(result / weight);
}
// the history brought towards the middle of the box of the colours around the pixel until it's inside it
vec3 taa_clip(vec3 history, vec3 centre, vec3 extent)
{
vec3 offset = history - centre;
vec3 units = abs(offset / max(extent, vec3(1.0 / 65504.0)));
float largest = max(units.x, max(units.y, units.z));
return (largest > 1.0) ? centre + offset / largest : history;
}
// the pixel against its neighbours on both sides (A, B) and along (C, D): a line across it
bool taa_line(float centre, float a, float b, float c, float d)
{
float da = a - centre;
float db = b - centre;
float across = min(abs(da), abs(db));
float contrast = max(TAA_THIN_MINIMUM, TAA_THIN_CONTRAST * max(centre, max(a, b)));
return (da * db > 0.0) && (across > contrast) && (max(abs(c - centre), abs(d - centre)) < 0.5 * across);
}
void main()
{
vec2 size = vec2(textureSize(color_tex, 0));
ivec2 pixel = ivec2(f_coords * size);
ivec2 last = ivec2(size) - 1;
// the colours around the pixel (squeezed, ycocg): their moments and brightness, and the nearest depth among them
vec3 m1 = vec3(0.0);
vec3 m2 = vec3(0.0);
float luma[9];
float nearest = -1.0;
ivec2 nearestpixel = pixel;
for (int y = -1; y <= 1; ++y)
{
for (int x = -1; x <= 1; ++x)
{
ivec2 p = clamp(pixel + ivec2(x, y), ivec2(0), last);
vec3 c = taa_ycocg(taa_compress(taa_clean(texelFetch(color_tex, p, 0).rgb)));
m1 += c;
m2 += c * c;
luma[(y + 1) * 3 + (x + 1)] = c.x;
// reverse z: the nearer, the greater
float d = texelFetch(depth_tex, p, 0).r;
if (d > nearest)
{
nearest = d;
nearestpixel = p;
}
}
}
vec3 frame = taa_clean(texelFetch(color_tex, pixel, 0).rgb);
if (false == TAA_HISTORY_VALID)
{
out_color = vec4(frame, 0.0);
return;
}
vec3 current = taa_ycocg(taa_compress(frame));
// the place of the pixel in the last frame: by the velocity drawn (of the nearest pixel around, so the edges go with what's in
// front), or for the world standing still by the depth. the sky has no velocity drawn and stands still
float depth = texelFetch(depth_tex, pixel, 0).r;
vec2 moved = f_coords + texelFetch(velocity_tex, nearestpixel, 0).xy;
vec4 world = TAA_REPROJECTION * vec4(f_coords * 2.0 - 1.0, depth, 1.0);
vec2 worlduv = (world.w > 0.0) ? world.xy / world.w * 0.5 + 0.5 : vec2(-1.0);
float difference = length((moved - worlduv) * size);
bool still = (world.w > 0.0) && ((depth <= 0.0) || (difference < max(TAA_STILL_PIXELS, 0.01 * length((moved - f_coords) * size))));
vec2 uv = still ? worlduv : moved;
if (any(lessThan(uv, vec2(0.0))) || any(greaterThan(uv, vec2(1.0))))
{
// out of the last frame: the frame alone
out_color = vec4(frame, 0.0);
return;
}
float speed = length((uv - f_coords) * size);
vec3 history = taa_ycocg(taa_compress(taa_history_colour(uv, size)));
float lock = textureLod(history_tex, uv, 0.0).a;
lock = isnan(lock) ? 0.0 : clamp(lock, 0.0, 1.0);
// a line across the pixel in this frame locks it; the lock wears off, and holds only where the place in the last frame is sure,
// the pixel moves slowly and is far away
bool line = taa_line(luma[4], luma[3], luma[5], luma[1], luma[7]) || taa_line(luma[4], luma[1], luma[7], luma[3], luma[5]) ||
taa_line(luma[4], luma[0], luma[8], luma[2], luma[6]) || taa_line(luma[4], luma[2], luma[6], luma[0], luma[8]);
lock = max(lock - TAA_LOCK_DECAY, line ? 1.0 : 0.0);
float distance = (depth > 0.0) ? TAA_NEAR / depth : TAA_LOCK_DISTANCE_FULL;
lock *= (still ? 1.0 : 0.0) * (1.0 - smoothstep(TAA_LOCK_SPEED_MIN, TAA_LOCK_SPEED_MAX, speed)) *
smoothstep(TAA_LOCK_DISTANCE_MIN, TAA_LOCK_DISTANCE_FULL, distance);
// the history within the colours around the pixel, but where it's locked
vec3 mean = m1 / 9.0;
vec3 deviation = sqrt(max(m2 / 9.0 - mean * mean, vec3(0.0)));
float gamma = mix(TAA_VARIANCE_WIDE, TAA_VARIANCE_NARROW, clamp(speed / TAA_SPEED_NARROW, 0.0, 1.0));
history = mix(taa_clip(history, mean, gamma * deviation), history, lock);
vec3 result = mix(history, current, TAA_CURRENT_SHARE);
out_color = vec4(taa_clean(taa_decompress(clamp(taa_rgb(result), vec3(0.0), vec3(1.0 - 1.0 / TAA_HALF_MAX)))), lock);
}