obs-StreamFX/data/effects/blur/gaussian.effect
Michael Fabian 'Xaymar' Dirks 8971bad218 gfx/blur/gaussian: Fix Gaussian Kernel generation
While the previous method worked, it matches no other implementation including a reference implementation. The new implementation almost perfectly matches the reference implementation and uses oversampling to achieve the goal. This has the downside of limiting the blur size to just 64, but it is necessary in order to achieve correct results.

Fixes #573
2021-06-09 03:46:14 +02:00

124 lines
4 KiB
Text

#include "common.effect"
//------------------------------------------------------------------------------
// Uniforms
//------------------------------------------------------------------------------
// This shader requires that pSize is the number of samples, not the size of the
// kernel. That way oversampling can be performed, which is much more accurate than
//------------------------------------------------------------------------------
// Defines
//------------------------------------------------------------------------------
#define MAX_SAMPLES 128
//------------------------------------------------------------------------------
// Technique: Directional / Area
//------------------------------------------------------------------------------
float4 PSBlur1D(VertexInformation vtx) : TARGET {
float2 uvstep = pImageTexel * pStepScale;
float weights = 0;
// Move to texel center.
vtx.uv.xy += pImageTexel.xy / 2.;
// Calculate the actual Gaussian Blur
// 1. Sample the center immediately.
float kernel = kernelAt(0);
weights += kernel;
float4 final = pImage.Sample(LinearClampSampler, vtx.uv) * kernel;
// 2. Then sample both + and - coordinates in one go to reduce code iterations.
for (uint step = 1; (step < pSize) && (step < MAX_SAMPLES); step++) {
float2 offset = uvstep * step;
kernel = kernelAt(step);
weights += kernel * 2;
final += pImage.Sample(LinearClampSampler, vtx.uv + offset) * kernel;
final += pImage.Sample(LinearClampSampler, vtx.uv - offset) * kernel;
}
// 3. Ensure we always have a total of 1.0, even if the kernel is bad.
final /= weights;
return final;
}
technique Draw {
pass {
vertex_shader = VSDefault(vtx);
pixel_shader = PSBlur1D(vtx);
}
}
//------------------------------------------------------------------------------
// Technique: Rotate
//------------------------------------------------------------------------------
float4 PSRotate(VertexInformation vtx) : TARGET {
float angstep = pAngle * pStepScale.x;
float weights = 0.;
// Move to texel center.
vtx.uv.xy += pImageTexel.xy / 2.;
// Calculate the actual Gaussian Blur
// 1. Sample the center immediately.
float kernel = kernelAt(0);
weights += kernel;
float4 final = pImage.Sample(LinearClampSampler, vtx.uv) * kernel;
// 2. Then sample both + and - coordinates in one go to reduce code iterations.
for (uint step = 1; (step < pSize) && (step < MAX_SAMPLES); step++) {
float offset = angstep * step;
kernel = kernelAt(step);
weights += kernel * 2;
final += pImage.Sample(LinearClampSampler, rotateAround(vtx.uv, pCenter, offset)) * kernel;
final += pImage.Sample(LinearClampSampler, rotateAround(vtx.uv, pCenter, -offset)) * kernel;
}
// 3. Ensure we always have a total of 1.0, even if the kernel is bad.
final /= weights;
return final;
}
technique Rotate {
pass {
vertex_shader = VSDefault(vtx);
pixel_shader = PSRotate(vtx);
}
}
//------------------------------------------------------------------------------
// Technique: Zoom
//------------------------------------------------------------------------------
float4 PSZoom(VertexInformation vtx) : TARGET {
float2 dir = normalize(vtx.uv - pCenter) * pStepScale * pImageTexel;
float dist = distance(vtx.uv, pCenter);
float weights = 0.;
// Move to texel center.
vtx.uv.xy += pImageTexel.xy / 2.;
// Calculate the actual Gaussian Blur
// 1. Sample the center immediately.
float kernel = kernelAt(0);
weights += kernel;
float4 final = pImage.Sample(LinearClampSampler, vtx.uv) * kernel;
// 2. Then sample both + and - coordinates in one go to reduce code iterations.
for (uint step = 1; (step < pSize) && (step < MAX_SAMPLES); step++) {
float2 offset = dir * step * dist;
kernel = kernelAt(step);
weights += kernel * 2;
final += pImage.Sample(LinearClampSampler, vtx.uv + offset) * kernel;
final += pImage.Sample(LinearClampSampler, vtx.uv - offset) * kernel;
}
// 3. Ensure we always have a total of 1.0, even if the kernel is bad.
final /= weights;
return final;
}
technique Zoom {
pass {
vertex_shader = VSDefault(vtx);
pixel_shader = PSZoom(vtx);
}
}