From 31e16c5915e44c6699f15ee72fe97e58bc287a09 Mon Sep 17 00:00:00 2001 From: jdgleaver Date: Thu, 6 Dec 2018 17:17:27 +0000 Subject: [PATCH] Add frame mixing shaders --- motionblur/mix_frames.slangp | 6 ++ motionblur/mix_frames_smart.slangp | 6 ++ motionblur/shaders/mix_frames.slang | 57 +++++++++++ motionblur/shaders/mix_frames_smart.slang | 111 ++++++++++++++++++++++ 4 files changed, 180 insertions(+) create mode 100644 motionblur/mix_frames.slangp create mode 100644 motionblur/mix_frames_smart.slangp create mode 100644 motionblur/shaders/mix_frames.slang create mode 100644 motionblur/shaders/mix_frames_smart.slang diff --git a/motionblur/mix_frames.slangp b/motionblur/mix_frames.slangp new file mode 100644 index 0000000..c53e6a0 --- /dev/null +++ b/motionblur/mix_frames.slangp @@ -0,0 +1,6 @@ +shaders = "1" + +shader0 = "shaders/mix_frames.slang" + +filter_linear0 = "false" +scale_type_0 = "source" diff --git a/motionblur/mix_frames_smart.slangp b/motionblur/mix_frames_smart.slangp new file mode 100644 index 0000000..e1330d1 --- /dev/null +++ b/motionblur/mix_frames_smart.slangp @@ -0,0 +1,6 @@ +shaders = "1" + +shader0 = "shaders/mix_frames_smart.slang" + +filter_linear0 = "false" +scale_type_0 = "source" diff --git a/motionblur/shaders/mix_frames.slang b/motionblur/shaders/mix_frames.slang new file mode 100644 index 0000000..ddb8229 --- /dev/null +++ b/motionblur/shaders/mix_frames.slang @@ -0,0 +1,57 @@ +#version 450 + +/* + mix_frames - performs 50:50 blending between the current and previous + frames. + + Author: jdgleaver + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. +*/ + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; +} global; + +#pragma stage vertex + +layout(location = 0) in vec4 Position; +layout(location = 1) in vec2 TexCoord; +layout(location = 0) out vec2 vTexCoord; + +/* + VERTEX_SHADER +*/ +void main() +{ + gl_Position = global.MVP * Position; + vTexCoord = TexCoord; +} + +#pragma stage fragment + +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D Source; +layout(set = 0, binding = 3) uniform sampler2D OriginalHistory1; + +/* + FRAGMENT SHADER +*/ +void main() +{ + // Get colour of current pixel + vec3 colour = texture(Source, vTexCoord.xy).rgb; + + // Get colour of previous pixel + vec3 colourPrev = texture(OriginalHistory1, vTexCoord.xy).rgb; + + // Mix colours + colour.rgb = mix(colour.rgb, colourPrev.rgb, 0.5); + + FragColor = vec4(colour.rgb, 1.0); +} diff --git a/motionblur/shaders/mix_frames_smart.slang b/motionblur/shaders/mix_frames_smart.slang new file mode 100644 index 0000000..369e22d --- /dev/null +++ b/motionblur/shaders/mix_frames_smart.slang @@ -0,0 +1,111 @@ +#version 450 + +/* + mix_frames_smart - performs 50:50 blending between the current and + previous frames, but only if pixels repeatedly switch state on alternate + frames (i.e. prevents flicker on games that use LCD ghosting for transparency, + without blurring the entire screen). This is not 100% effective, but 'good + enough' in many cases (e.g. it fixes map rendering issues in F-Zero GP on the GBA). + Works best when flickering objects are in a fixed location. + + Author: jdgleaver + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. +*/ + +// User-specified fudge factor. Increasing this value loosens up the +// detection of repeated 'flicker' frames. This is required for +// games like Boktai on the GBA, where the character shadow flickers +// on and off between frames, but is sometimes overlaid with a screen +// shading effect (so checking for pixel RGB equality fails - need to +// check whether pixels are 'almost' equal) +#pragma parameter DEFLICKER_EMPHASIS "Deflicker Emphasis" 0.0 0.0 1.0 0.01 + +layout(push_constant) uniform Push +{ + float DEFLICKER_EMPHASIS; +} registers; + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; +} global; + +#pragma stage vertex + +layout(location = 0) in vec4 Position; +layout(location = 1) in vec2 TexCoord; +layout(location = 0) out vec2 vTexCoord; + +/* + VERTEX_SHADER +*/ +void main() +{ + gl_Position = global.MVP * Position; + vTexCoord = TexCoord; +} + +#pragma stage fragment + +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D Source; +layout(set = 0, binding = 3) uniform sampler2D OriginalHistory1; +layout(set = 0, binding = 4) uniform sampler2D OriginalHistory2; +layout(set = 0, binding = 5) uniform sampler2D OriginalHistory3; +layout(set = 0, binding = 6) uniform sampler2D OriginalHistory4; +layout(set = 0, binding = 7) uniform sampler2D OriginalHistory5; + +#define EPSILON 0.000001 + +float is_equal(vec3 x, vec3 y) +{ + vec3 result = 1.0 - abs(sign(x - y)); + return min(min(result.r, result.g), result.b); +} + +float is_approx_equal(vec3 x, vec3 y) +{ + vec3 result = 1.0 - step(EPSILON + registers.DEFLICKER_EMPHASIS, abs(x - y)); + return min(min(result.r, result.g), result.b); +} + +/* + FRAGMENT SHADER +*/ +void main() +{ + // Get pixel colours of current + last 5 frames + // NB: Using fewer frames results in too many false positives + vec3 colour0 = texture(Source, vTexCoord.xy).rgb; + vec3 colour1 = texture(OriginalHistory1, vTexCoord.xy).rgb; + vec3 colour2 = texture(OriginalHistory2, vTexCoord.xy).rgb; + vec3 colour3 = texture(OriginalHistory3, vTexCoord.xy).rgb; + vec3 colour4 = texture(OriginalHistory4, vTexCoord.xy).rgb; + vec3 colour5 = texture(OriginalHistory5, vTexCoord.xy).rgb; + + // Determine whether mixing should occur + // i.e. whether alternate frames have the same pixel colour, but + // adjacent frames do not (don't need to check colour0 != colour1, + // since if this is true the mixing will do nothing) + float doMix = (1.0 - is_equal(colour0, colour3)) + * (1.0 - is_equal(colour0, colour5)) + * (1.0 - is_equal(colour1, colour2)) + * (1.0 - is_equal(colour1, colour4)) + * (1.0 - is_equal(colour2, colour3)) + * (1.0 - is_equal(colour2, colour5)) + * min( + (is_approx_equal(colour0, colour2) * is_approx_equal(colour2, colour4)) + + (is_approx_equal(colour1, colour3) * is_approx_equal(colour3, colour5)), + 1.0 + ); + + // Mix colours + colour0.rgb = mix(colour0.rgb, colour1.rgb, doMix * 0.5); + + FragColor = vec4(colour0.rgb, 1.0); +}