GLSL "glow" effects
Using 2D image processing to emphasize the 3D points rendering
This really is a deNoise bilateral filter with variable sigma radius and threshold
This is typical 2D noise reduction algorithm, with settable/notFixed sigma, that I tried to optimize for GLSL :
// fX = exp( -(x*x) * invSigmaSqx2 ) * invSigmaxSqrt2PI;
// fY = exp( -(y*y) * invSigmaSqx2 ) * invSigmaxSqrt2PI;
// where...
// invSigmaSqx2 = 1.0 / (sigma^2 * 2.0)
// invSigmaxSqrt2PI = 1.0 / (sqrt(2 * PI) * sigma)
//
// now, fX*fY can be written in unique expression...
//
// e^(a*X) * e^(a*Y) * c*c
//
// where:
// a = invSigmaSqx2, X = (x*x), Y = (y*y), c = invSigmaxSqrt2PI
//
// -[(x*x) * 1/(2 * sigma^2)] -[(y*y) * 1/(2 * sigma^2)]
// e e
// fX = ------------------------------- fY = -------------------------------
// ________ ________
// \/ 2 * PI * sigma \/ 2 * PI * sigma
//
// now with...
// a = 1/(2 * sigma^2),
// X = (x*x)
// Y = (y*y) ________
// c = 1 / \/ 2 * PI * sigma
//
// we have...
// -[aX] -[aY]
// fX = e * c; fY = e * c;
//
// and...
// -[aX + aY] [2] -[a(X + Y)] [2]
// fX*fY = e * c = e * c
//
// well...
//
// -[(x*x + y*y) * 1/(2 * sigma^2)]
// e
// fX*fY = --------------------------------------
// [2]
// 2 * PI * sigma
//
// now with assigned constants...
//
// invSigmaQx2 = 1/(2 * sigma^2)
// invSigmaQx2PI = 1/(2 * PI * sigma^2) = invSigmaQx2 * INV_PI
//
// and the kernel vector
//
// k = vec2(x,y)
//
// we can write:
//
// fXY = exp( -dot(k,k) * invSigmaQx2) * invSigmaQx2PI
// reductFactor = 1.0 if used alone -> bilateral threshold
// reductFactor = 0.2 if used with gaussian blur - > gaussian + bilateral
////////////////////////////////////////////////////////////////////
vec4 bilateralSmartSmooth(float reductFactor)
{
float bsigma = threshold*reductFactor;
float radius = float(round(sigma.y*sigma.x-1.f));
float radQ = radius * radius;
float invSigma = 1.f/sigma.x;
float invSigmaQx2 = .5 * invSigma * invSigma; // 1.0 / (sigma^2 * 2.0)
float invSigmaQx2PI = INV_PI * invSigmaQx2; // 1.0 / (sqrt(PI) * sigma)
// float invSigmaxSqrt2PI = INV_SQRT_OF_2PI * invSigma; // 1.0 / (sqrt(PI) * sigma)
float invBSigma = 1.f/bsigma;
float invBSigmaSqx2 = .5 * invBSigma * invBSigma; // 1.0 / (sigma^2 * 2.0)
float invBSigmaxSqrt2PI = INV_SQRT_OF_2PI * invBSigma; // 1.0 / (sqrt(2*PI) * sigma)
vec4 centrPx = texelFetch(origTexture,ivec2(gl_FragCoord.xy),0);
float Zbuff = 0.0;
vec4 accumBuff = vec4(0.0);
vec2 d;
for (d.x=-radius; d.x <= radius; d.x++) {
float pt = sqrt(radQ-d.x*d.x);
for (d.y=-pt; d.y <= pt; d.y++) {
float blurFactor = exp( -dot(d , d) * invSigmaQx2 ) * invSigmaQx2;
vec4 walkPx = texelFetch(origTexture, ivec2(gl_FragCoord.xy+d),0 );
vec4 dC = walkPx-centrPx;
float deltaFactor = exp( -dot(dC, dC) * invBSigmaSqx2) * invBSigmaxSqrt2PI * blurFactor;
Zbuff += deltaFactor;
accumBuff += deltaFactor*walkPx;
}
}
return accumBuff/Zbuff;
}
Classic 2-pass gaussian blur algorithm, with settable/notFixed sigma, applied to a two-dimensional texture as two independent one-dimensional calculations, so for any pixel we processing 2*sigma fragments, instead of sigma*sigma
The algorithm is mixed with original texture to obtain the glow effect: the mixing parameters are settable.
vec4 gPass(sampler2D tex, vec2 direction)
{
//vec2 offset = wSize;
//compute the radius across the kernel
float radius = sigma.y*sigma.x-1.f;
float Zbuff = 0.0;
vec4 accumBuff = vec4(0.0);
//precompute factors used every iteration
float invSigma = 1.f/sigma.x;
float invSigmaSqx2 = .5 * invSigma * invSigma; // 1.0 / (sigma^2 * 2.0)
float invSigmaxSqrt2PI = INV_SQRT_OF_2PI * invSigma; // 1.0 / (sqrt(PI) * sigma)
// separable Gaussian
for (float r = -radius; r <= radius; r++) {
float factor = exp( -(r*r) * invSigmaSqx2 ) * invSigmaxSqrt2PI;
vec4 c = texelFetch(tex, ivec2(gl_FragCoord.xy + r * direction), 0);
accumBuff += factor * c;
}
return accumBuff;
}
// Pass1 Gauss Blur
////////////////////////////////////////////////////////////////////////////
vec4 radialPass1()
{
return gPass(origTexture, vec2(1.0, 0.0));
}
// Pass2 Gauss Blur
////////////////////////////////////////////////////////////////////////////
vec4 radialPass2()
{
vec4 original = texelFetch(origTexture,ivec2(gl_FragCoord.xy),0) * texControls.y; //origTex * intensity
vec4 blurred = gPass(pass1Texture, vec2(0.0, 1.0))* texControls.x; //blur * intensity
return mix(original,blurred,mixTexture); // return blurred texture, mixed with original
}
vec4 gPass(sampler2D tex, vec2 direction)
{
//vec2 offset = wSize;
//compute the radius across the kernel
float radius = sigma.y*sigma.x-1.f;
float Zbuff = 0.0;
vec4 accumBuff = vec4(0.0);
//precompute factors used every iteration
float invSigma = 1.f/sigma.x;
float invSigmaSqx2 = .5 * invSigma * invSigma; // 1.0 / (sigma^2 * 2.0)
float invSigmaxSqrt2PI = INV_SQRT_OF_2PI * invSigma; // 1.0 / (sqrt(PI) * sigma)
// separable Gaussian
for ( float r = -radius; r <= radius; r++) {
float factor = exp( -(r*r) * invSigmaSqx2 ) * invSigmaxSqrt2PI;
vec4 c = texelFetch(tex, ivec2(gl_FragCoord.xy + r * direction), 0);
accumBuff += factor * c;
}
return accumBuff;
}
// Pass1 Gauss Blur
////////////////////////////////////////////////////////////////////////////
vec4 radialPass1()
{
return gPass(origTexture, vec2(1.0, 0.0));
}
// Pass2 Gauss Blur + threshold -> reduced (1/4) bilateral
////////////////////////////////////////////////////////////////////////////
vec4 radialPass2withBilateral()
{
vec4 original = texelFetch(origTexture,ivec2(gl_FragCoord.xy),0) * texControls.y;
vec4 blurred = gPass(pass1Texture, vec2(0.0, 1.0))* texControls.x;
vec4 newBlur = bilateralSmartSmooth(.2) * texControls.z;
return newBlur;
}