obs-StreamFX/data/effects/blur/box-linear.effect
Michael Fabian 'Xaymar' Dirks 68a8fb9224 gfx/blur/box-linear: Refactor Linear Box Blur
Box Blur is a prime candidate for Linear optimizations, and as such reducing the total necessary samples by about half. However due to the reduction in samples, only Area and Directional Blur are supported.

Related: #45, #6
2019-04-02 03:50:01 +02:00

122 lines
3.3 KiB
Text

// Parameters:
/// OBS Default
uniform float4x4 ViewProj;
/// Texture
uniform texture2d pImage;
uniform float2 pImageTexel;
/// Blur
uniform float pSize;
uniform float pSizeInverseMul;
uniform float pAngle;
uniform float2 pCenter;
uniform float2 pStepScale;
#define MAX_BLUR_SIZE 128
// # Linear Optimization
// While the normal way is to sample every texel in the pSize, linear optimization
// takes advantage of the fact that most people, especially after compression,
// will not be able to tell the difference between a linear approximation and
// the actual thing.
//
// Instead of sampling every texel like this:
//
// |Tx|Tx|Tx|Tx|Tx|
// Tx|-2|-1| 0|+1|+2|
//
// Linear optimization will sample like this:
//
// |Tx|Tx|Tx|Tx|Tx|
// Tx| -1 | 0| +1 |
//
// This effectively removes half the necessary samples and looks identical when
// when used with box blur. However there is an edge case when the blur width
// is not a multiple of two, where two additional samples have to be spent on
// reading the outer edge:
//
// |Tx|Tx|Tx|Tx|Tx|Tx|Tx|
// Tx|-2| -1 | 0| +1 |+2|
//
// or this alternative pattern that uses two less samples:
//
// |Tx|Tx|Tx|Tx|Tx|Tx|Tx|
// Tx| 0 | +1 | +2 |+3|
//
// With careful planning this can even be used for other types of Blur, such as
// Gaussian Blur, which suffers a larger hit - however there are better and
// faster alternatives than linear sampling with Gaussian Blur, such as
// Dual Filtering ("Dual Kawase").
// Sampler
sampler_state linearSampler {
Filter = Linear;
AddressU = Clamp;
AddressV = Clamp;
MinLOD = 0;
MaxLOD = 0;
};
// Default Vertex Shader and Data
struct VertDataIn {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
};
struct VertDataOut {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
bool is_odd : TEXCOORD1;
};
VertDataOut VSDefault(VertDataIn vtx) {
VertDataOut vert_out;
vert_out.pos = mul(float4(vtx.pos.xyz, 1.0), ViewProj);
vert_out.uv = vtx.uv;
vert_out.is_odd = ((int(round(pSize)) % 2) == 1);
return vert_out;
}
// Blur 1 Dimensional
float4 PSBlur1D(VertDataOut vtx) : TARGET {
float4 final = pImage.Sample(linearSampler, vtx.uv);
// y = yes, s = skip, b = break
// Size-> | 1| 2| 3| 4| 5| 6| 7|
// -------+--+--+--+--+--+--+--+
// n=1 | b| y| y| y| y| y| y|
// n=2 | |bs| s| s| s| s| s|
// n=3 | | b| b| y| y| y| y|
// n=4 | | | |bs| s| s| s|
// n=5 | | | | b| b| y| y|
// n=6 | | | | | |bs| s|
// n=7 | | | | | | b| b|
// n=8 | | | | | | | |
// Loop unrolling is only possible with a fixed known maximum.
// Some compilers may unroll up to x iterations, but most will not.
for (int n = 1; n <= MAX_BLUR_SIZE; n+=2) {
// Different from normal box, early exit instead of late exit.
if (n >= pSize) {
break;
}
float2 nstep = (pImageTexel * pStepScale) * (n + 0.5);
final += pImage.Sample(linearSampler, vtx.uv + nstep) * 2.;
final += pImage.Sample(linearSampler, vtx.uv - nstep) * 2.;
}
if (vtx.is_odd) {
float2 nstep = (pImageTexel * pStepScale) * pSize;
final += pImage.Sample(linearSampler, vtx.uv + nstep);
final += pImage.Sample(linearSampler, vtx.uv - nstep);
}
final *= pSizeInverseMul;
return final;
}
technique Draw {
pass {
vertex_shader = VSDefault(vtx);
pixel_shader = PSBlur1D(vtx);
}
}