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; }