From 28fb09f791b1394bb8717487a0b5bc7679459915 Mon Sep 17 00:00:00 2001 From: Hyllian Date: Mon, 1 Apr 2024 16:11:32 -0300 Subject: [PATCH] Add b-spline-4-taps and catmull-rom-5-taps Two standalone fast cubic shaders. --- interpolation/b-spline-4-taps.slangp | 6 ++ interpolation/catmull-rom-5-taps.slangp | 6 ++ interpolation/shaders/b-spline-4-taps.slang | 95 ++++++++++++++++ .../shaders/catmull-rom-5-taps.slang | 101 ++++++++++++++++++ 4 files changed, 208 insertions(+) create mode 100644 interpolation/b-spline-4-taps.slangp create mode 100644 interpolation/catmull-rom-5-taps.slangp create mode 100644 interpolation/shaders/b-spline-4-taps.slang create mode 100644 interpolation/shaders/catmull-rom-5-taps.slang diff --git a/interpolation/b-spline-4-taps.slangp b/interpolation/b-spline-4-taps.slangp new file mode 100644 index 0000000000..d0874752af --- /dev/null +++ b/interpolation/b-spline-4-taps.slangp @@ -0,0 +1,6 @@ +shaders = 1 + +shader0 = shaders/b-spline-4-taps.slang +wrap_mode0 = clamp_to_edge +scale_type0 = viewport +filter_linear0 = true diff --git a/interpolation/catmull-rom-5-taps.slangp b/interpolation/catmull-rom-5-taps.slangp new file mode 100644 index 0000000000..4f73a814ff --- /dev/null +++ b/interpolation/catmull-rom-5-taps.slangp @@ -0,0 +1,6 @@ +shaders = 1 + +shader0 = shaders/catmull-rom-5-taps.slang +wrap_mode0 = clamp_to_edge +scale_type0 = viewport +filter_linear0 = true diff --git a/interpolation/shaders/b-spline-4-taps.slang b/interpolation/shaders/b-spline-4-taps.slang new file mode 100644 index 0000000000..ea3b9ad2de --- /dev/null +++ b/interpolation/shaders/b-spline-4-taps.slang @@ -0,0 +1,95 @@ +#version 450 + +/* + Bicubic B-Spline 4-taps (Fast) - ported by Hyllian - 2024 + + The following code is licensed under the MIT license: https://gist.github.com/TheRealMJP/bc503b0b87b643d3505d41eab8b332ae + + Samples a texture with B-Spline filtering, using only 4 texture fetches instead of 16. + See http://vec3.ca/bicubic-filtering-in-fewer-taps/ for more details + Implementation: https://www.shadertoy.com/view/styXDh + + ATENTION: This code only work using LINEAR filter sampling set on Retroarch! + +*/ + +layout(push_constant) uniform Push +{ + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; +} params; + +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; + +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; + +void main() +{ + // We're going to sample a a 4x4 grid of texels surrounding the target UV coordinate. We'll do this by rounding + // down the sample location to get the exact center of our "starting" texel. The starting texel will be at + // location [1, 1] in the grid, where [0, 0] is the top left corner. + vec2 texSize = params.SourceSize.xy; + vec2 invTexSize = 1.0 / texSize; + vec2 iTc = vTexCoord * texSize; + + vec2 tc = floor(iTc - vec2(0.5)) + vec2(0.5); + + // Compute the fractional offset from our starting texel to our original sample location, which we'll + // feed into the B-Spline function to get our filter weights. + vec2 f = iTc - tc; + vec2 f2 = f * f; + vec2 f3 = f2 * f; + + vec2 of = vec2(1.)-f; + vec2 of2 = of*of; + vec2 of3 = of2*of; + + vec2 w0 = of3 / 6.0 ; + vec2 w1 = (vec2(4.) + 3.*f3 - 6.*f2) / 6.0; + vec2 w2 = (vec2(4.) + 3.*of3 - 6.*of2) / 6.0; + vec2 w3 = f3 / 6.0; + + vec2 Weight[2]; + vec2 Sample[2]; + + Weight[0] = w0 + w1; + Weight[1] = w2 + w3; + + Sample[0] = tc - (vec2(1.) - w1/Weight[0]); + Sample[1] = tc + vec2(1.) + w3/Weight[1]; + + Sample[0] *= invTexSize; + Sample[1] *= invTexSize; + + float sampleWeight[4]; + sampleWeight[0] = Weight[0].x * Weight[0].y; + sampleWeight[1] = Weight[1].x * Weight[0].y; + sampleWeight[2] = Weight[0].x * Weight[1].y; + sampleWeight[3] = Weight[1].x * Weight[1].y; + + vec3 Ctl = texture(Source, vec2(Sample[0].x, Sample[0].y)).rgb * sampleWeight[0]; + vec3 Ctr = texture(Source, vec2(Sample[1].x, Sample[0].y)).rgb * sampleWeight[1]; + vec3 Cbl = texture(Source, vec2(Sample[0].x, Sample[1].y)).rgb * sampleWeight[2]; + vec3 Cbr = texture(Source, vec2(Sample[1].x, Sample[1].y)).rgb * sampleWeight[3]; + + FragColor = vec4(Ctl+Ctr+Cbl+Cbr, 1.0); +} diff --git a/interpolation/shaders/catmull-rom-5-taps.slang b/interpolation/shaders/catmull-rom-5-taps.slang new file mode 100644 index 0000000000..6fa5c8aa64 --- /dev/null +++ b/interpolation/shaders/catmull-rom-5-taps.slang @@ -0,0 +1,101 @@ +#version 450 + +/* + Bicubic Catmull-Rom 5-taps (Fast) - ported by Hyllian - 2024 + + Samples a texture with B-Spline filtering, using only 4 texture fetches instead of 16. + See http://vec3.ca/bicubic-filtering-in-fewer-taps/ for more details + Source: https://www.shadertoy.com/view/styXDh + http://www.profhua.com/Sub/Article/BicubicFiltering/BicubicFiltering.html + + ATENTION: This code only work using LINEAR filter sampling set on Retroarch! + +*/ + +layout(push_constant) uniform Push +{ + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; +} params; + +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; + +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; + +void main() +{ + // We're going to sample a a 4x4 grid of texels surrounding the target UV coordinate. We'll do this by rounding + // down the sample location to get the exact center of our "starting" texel. The starting texel will be at + // location [1, 1] in the grid, where [0, 0] is the top left corner. + vec2 texSize = params.SourceSize.xy; + vec2 invTexSize = 1.0 / texSize; + vec2 iTc = vTexCoord * texSize; + + vec2 tc = floor(iTc - vec2(0.5)) + vec2(0.5); + + // Compute the fractional offset from our starting texel to our original sample location, which we'll + // feed into the B-Spline function to get our filter weights. + vec2 f = iTc - tc; + vec2 f2 = f * f; + vec2 f3 = f2 * f; + + vec2 of = vec2(1.)-f; + vec2 of2 = of*of; + vec2 of3 = of2*of; + + vec2 w0 = f2 - 0.5 * (f3 + f); + vec2 w1 = 1.5 * f3 - 2.5 * f2 + vec2(1.); + vec2 w3 = 0.5 * (f3 - f2); + vec2 w2 = vec2(1.) - w0 - w1 - w3; + + vec2 Weight[3]; + vec2 Sample[3]; + + Weight[0] = w0; + Weight[1] = w1 + w2; + Weight[2] = w3; + + Sample[0] = tc - vec2(1.); + Sample[1] = tc + w2 / Weight[1]; + Sample[2] = tc + vec2(2.); + + Sample[0] *= invTexSize; + Sample[1] *= invTexSize; + Sample[2] *= invTexSize; + + float sampleWeight[5]; + sampleWeight[0] = Weight[1].x * Weight[0].y; + sampleWeight[1] = Weight[0].x * Weight[1].y; + sampleWeight[2] = Weight[1].x * Weight[1].y; + sampleWeight[3] = Weight[2].x * Weight[1].y; + sampleWeight[4] = Weight[1].x * Weight[2].y; + + vec3 Ct = texture(Source, vec2(Sample[1].x, Sample[0].y)).rgb * sampleWeight[0]; + vec3 Cl = texture(Source, vec2(Sample[0].x, Sample[1].y)).rgb * sampleWeight[1]; + vec3 Cc = texture(Source, vec2(Sample[1].x, Sample[1].y)).rgb * sampleWeight[2]; + vec3 Cr = texture(Source, vec2(Sample[2].x, Sample[1].y)).rgb * sampleWeight[3]; + vec3 Cb = texture(Source, vec2(Sample[1].x, Sample[2].y)).rgb * sampleWeight[4]; + + float WeightMultiplier = 1./(sampleWeight[0]+sampleWeight[1]+sampleWeight[2]+sampleWeight[3]+sampleWeight[4]); + + FragColor = vec4((Ct+Cl+Cc+Cr+Cb)*WeightMultiplier, 1.0); +}