From 92c4b54177c59fd16e9b75b9ea5edb5726fa2f4a Mon Sep 17 00:00:00 2001 From: Michael Fabian 'Xaymar' Dirks Date: Sat, 22 Dec 2018 22:54:17 +0100 Subject: [PATCH] filter-blur: Add Linear Gaussian Blur Similar to Linear Box Blur, this version of Gaussian Blur reduces the total number of sample by up to n. This results in a total sample count (per pass) of O(n+1) for even radii and O(n+2) for odd radii. The quality sacrificed to do this is higher this time, though careful adjustment of the halfTexelDelta value can bring it much closer to normal Gaussian Blur. The current offset however had no noticable effects on visual quality. See Also: #21 Blur Quality --- data/effects/blur.effect | 62 +++++++++++++++++++++++++++++++++++++++- data/locale/en-US.ini | 3 +- source/filter-blur.cpp | 20 ++++++------- source/filter-blur.h | 3 +- 4 files changed, 73 insertions(+), 15 deletions(-) diff --git a/data/effects/blur.effect b/data/effects/blur.effect index a8cf157e..7b7e7d3b 100644 --- a/data/effects/blur.effect +++ b/data/effects/blur.effect @@ -78,7 +78,7 @@ technique Box /// Blur: Box (Linear Optimized) // By abusing Linear sampling we can reduce the necessary samples, halving the total samples. -float4 PSBoxBlurLinear(VertDataOut vtx) : TARGET { +float4 PSBoxBlurLinear(VertDataOut vtx) : TARGET { // Radius 4 (Even): // [-4, -3, -2, -1, 0, +1, +2, +3, +4] // ^-S-^ ^-S-^ S ^-S-^ ^-S-^ @@ -155,6 +155,66 @@ technique Gaussian } } +/// Blur: Gaussian Linear +float4 PSGaussianLinearBlur(VertDataOut vtx) : TARGET { + // Origin sample must always be sampled. + // Even, Odd must decide differently than Box Blur + + // Radius 5 (Odd): + // [-5, -4, -3, -2, -1, 0, +1, +2, +3, +4, +5] + // S ^-S-^ ^-S-^ S ^-S-^ ^-S-^ S + // S ^-S-^ ^-S-^ S ^-S-^ ^-S-^ S + // Total Samples: 7 (n+2) + + // Radius 4 (Even): + // [-4, -3, -2, -1, 0, +1, +2, +3, +4] + // ^-S-^ ^-S-^ S ^-S-^ ^-S-^ + // Total Samples: 5 (n+1) + + // Radius 3 (Odd): + // [-3, -2, -1, 0, +1, +2, +3] + // S ^-S-^ S ^-S-^ S + // Total Samples: 5 (n+2) + + // Radius 2 (Even): + // [-2, -1, 0, +1, +2] + // ^-S-^ S ^-S-^ + // Total Samples: 3 (n+1) + + float4 origin = u_image.SampleLevel(pointSampler, vtx.uv, 0); + float4 final = origin * kernel.SampleLevel(pointSampler, (float2(0, u_radius - 1) * kernelTexel), 0).r; + float2 halfTexelDelta = u_texelDelta / 2.0; + + for (int k = 1; k < u_radius; k+=2) { + float2 offset = k * u_texelDelta + halfTexelDelta; + float l_g0 = kernel.SampleLevel(pointSampler, (float2(k, u_radius - 1) * kernelTexel), 0).r; + float l_g1 = kernel.SampleLevel(pointSampler, (float2(k + 1, u_radius - 1) * kernelTexel), 0).r; + float4 l_p = u_image.SampleLevel(linearSampler, vtx.uv + offset, 0); + float4 l_n = u_image.SampleLevel(linearSampler, vtx.uv - offset, 0); + final += (l_p + l_n) * l_g0; + final += (l_p + l_n) * l_g1; + } + + if (u_radius % 2 == 1) { + // Odd numbers require treatment of ends. + float4 left = u_image.SampleLevel(pointSampler, vtx.uv + u_texelDelta * u_radius, 0); + float4 right = u_image.SampleLevel(pointSampler, vtx.uv - u_texelDelta * u_radius, 0); + float krn = kernel.SampleLevel(pointSampler, (float2(u_radius, u_radius - 1) * kernelTexel), 0).r; + final += (left + right) * krn; + } + + return final; +} + +technique GaussianLinear +{ + pass + { + vertex_shader = VSDefault(vtx); + pixel_shader = PSGaussianLinearBlur(vtx); + } +} + /// Blur: Bilateral float Bilateral(float x, float sigma) { return 0.39894 * exp(-0.5 * (x*x) / (sigma*sigma)) / sigma; diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index b0672ad3..02c9ef5a 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -28,10 +28,11 @@ CustomShader.Texture.Type.Source="Source" # Filter - Blur Filter.Blur="Blur" Filter.Blur.Type="Type" -Filter.Blur.Type.Description="The type of blur to apply:\n- 'Box' smoothes pixels equally.\n- 'Box Linear' uses linear sampling to reduce the GPU usage, sacrificing minimal quality.\n- 'Gaussian' applies a gaussian curve to the smoothed pixels.\n- 'Bilateral' is an edge detection variant of 'Gaussian'." +Filter.Blur.Type.Description="The type of blur to apply:\n- 'Box' smoothes pixels equally.\n- 'Box Linear' uses linear sampling to reduce the GPU usage, sacrificing minimal quality.\n- 'Gaussian' applies a gaussian curve to the smoothed pixels.\n- 'Gaussian Linear' again uses linear sampling to reduce the total samples, sacrificing more quality this time.\n- 'Bilateral' is an edge detection variant of 'Gaussian'." Filter.Blur.Type.Box="Box" Filter.Blur.Type.BoxLinear="Box Linear" Filter.Blur.Type.Gaussian="Gaussian" +Filter.Blur.Type.GaussianLinear="Gaussian Linear" Filter.Blur.Type.Bilateral="Bilateral" Filter.Blur.Size="Size (Pixel)" Filter.Blur.Size.Description="Area size of the blur, large sizes may cause:\n- Skipped frames\n- Frame loss or drops\n- Input lag\n- GPU overheating\n- or other issues." diff --git a/source/filter-blur.cpp b/source/filter-blur.cpp index e9b4e96e..70b6f8a2 100644 --- a/source/filter-blur.cpp +++ b/source/filter-blur.cpp @@ -41,6 +41,7 @@ extern "C" { #define P_TYPE_BOX "Filter.Blur.Type.Box" #define P_TYPE_BOXLINEAR "Filter.Blur.Type.BoxLinear" #define P_TYPE_GAUSSIAN "Filter.Blur.Type.Gaussian" +#define P_TYPE_GAUSSIANLINEAR "Filter.Blur.Type.GaussianLinear" #define P_TYPE_BILATERAL "Filter.Blur.Type.Bilateral" #define P_SIZE "Filter.Blur.Size" #define P_BILATERAL_SMOOTHING "Filter.Blur.Bilateral.Smoothing" @@ -125,8 +126,6 @@ bool filter::blur::blur_instance::apply_gaussian_param() if (blur_effect->has_parameter("kernel")) { blur_effect->get_parameter("kernel").set_texture(kernel); - } else { - return false; } if (blur_effect->has_parameter("kernelTexel")) { @@ -300,6 +299,7 @@ obs_properties_t* filter::blur::blur_instance::get_properties() obs_property_list_add_int(p, P_TRANSLATE(P_TYPE_BOX), filter::blur::type::Box); obs_property_list_add_int(p, P_TRANSLATE(P_TYPE_BOXLINEAR), filter::blur::type::BoxLinear); obs_property_list_add_int(p, P_TRANSLATE(P_TYPE_GAUSSIAN), filter::blur::type::Gaussian); + obs_property_list_add_int(p, P_TRANSLATE(P_TYPE_GAUSSIANLINEAR), filter::blur::type::GaussianLinear); obs_property_list_add_int(p, P_TRANSLATE(P_TYPE_BILATERAL), filter::blur::type::Bilateral); p = obs_properties_add_int_slider(pr, P_SIZE, P_TRANSLATE(P_SIZE), 1, 25, 1); @@ -630,14 +630,8 @@ void filter::blur::blur_instance::video_render(gs_effect_t* effect) if (!apply_shared_param(intermediate, xpel, ypel)) break; - switch (type) { - case Gaussian: - apply_gaussian_param(); - break; - case Bilateral: - apply_bilateral_param(); - break; - } + apply_gaussian_param(); + apply_bilateral_param(); gs_texrender_reset(rt); if (!gs_texrender_begin(rt, baseW, baseH)) { @@ -1025,12 +1019,14 @@ std::string filter::blur::blur_factory::get_technique(filter::blur::type type) switch (type) { case type::Box: return "Box"; - case type::BoxLinear: - return "BoxLinear"; case type::Gaussian: return "Gaussian"; case type::Bilateral: return "Bilateral"; + case type::BoxLinear: + return "BoxLinear"; + case type::GaussianLinear: + return "GaussianLinear"; } return ""; } diff --git a/source/filter-blur.h b/source/filter-blur.h index 28a36da6..0143582c 100644 --- a/source/filter-blur.h +++ b/source/filter-blur.h @@ -42,9 +42,10 @@ namespace filter { namespace blur { enum type : int64_t { Box, - BoxLinear, Gaussian, Bilateral, + BoxLinear, + GaussianLinear, }; enum mask_type : int64_t {