# GLSL - one-pass gaussian blur

It is possible to implement fragment shader to do one-pass gaussian blur? I have found lot of implementation of two-pass blur (gaussian and box blur):

and so on.

I have been thinking of implementing gaussian blur as convolution (in fact, it is the convolution, the examples above are just aproximations):

http://en.wikipedia.org/wiki/Gaussian_blur

Yes, you can implement Gaussian blur in one pass, by sampling all n^2 pixels in the kernel (for kernel width n). It's usually faster to run it on the rows and columns in two passes, since then you have O(n) pixels to sample rather than O(n^2). This is not an approximation, since Gaussian blur is mathematically separable.

The trick for rapid Gaussian blurring with GLSL is to take advantage of the fact that the GPU provides linear interpolation in hardware. Therefore, you can effectively sample four 2D pixels with a single prefetch or eight 3D voxels. By deciding where to sample you can weight the output. The definitive reference is Sigg and Hadwiger's "Fast Third-Order Texture Filtering" which you can find online.

For a readable explanation find the web page "Efficient Gaussian blur with linear sampling". As noted, since the Gaussian blur is separable with wide kernels it is most efficient to do one pass per dimension.

However, you can also use this trick to approximate a Gaussian with a tight kernel in a single pass. In the example below I emulate the 3D kernel with the top slice = [1 2 1; 2 4 2; 1 2 1]; middle slice = [2 4 2; 4 8 4; 2 4 2]; bottom slice = [1 2 1; 2 4 2; 1 2 1]. By sampling +/-0.5 voxels in each dimension you do this with just 8 texture fetches rather than 27. I am demonstrating this in GLSL as a MRIcroGL shader file - just drop save the script below as "a.txt" and place it into MRIcroGL's "Shader" folder. When you relaunch the program you will see your ray cast image blurred. Clicking the "doBlur" checkbox toggles the blurring on and off. Using my integrated Intel GPU in my laptop and the "chris_t1" image that comes with MRIcroGL I get 70fps without blurring (1 texture fetch) and 21fps with blurring (8 fetches). Most of the code is just a classic ray caster, the "doBlur" conditional encapsulates your question.

//-------a.txt file follows

//pref
doBlur|bool|true
//vert
void main() {
gl_TexCoord = gl_MultiTexCoord1;
gl_Position = ftransform();
}
//frag
uniform int loops;
uniform float stepSize, sliceSize, viewWidth, viewHeight;
uniform sampler3D intensityVol;
uniform sampler2D backFace;
uniform vec3 clearColor;
uniform bool doBlur;
void main() {
// get normalized pixel coordinate in view port (e.g. [0,1]x[0,1])
vec2 pixelCoord = gl_FragCoord.st;
pixelCoord.x /= viewWidth;
pixelCoord.y /= viewHeight;
// starting position of the ray is stored in the texture coordinate
vec3 start = gl_TexCoord.xyz;
vec3 backPosition = texture2D(backFace,pixelCoord).xyz;
vec3 dir = backPosition - start;
float len = length(dir);
dir = normalize(dir);
vec3 deltaDir = dir * stepSize;
vec4 colorSample,colAcc = vec4(0.0,0.0,0.0,0.0);
float lengthAcc = 0.0;
float opacityCorrection = stepSize/sliceSize;
//ray dithering http://marcusbannerman.co.uk/index.php/home/42-articles/97-vol-render-optimizations.html
vec3 samplePos = start.xyz + deltaDir* (fract(sin(gl_FragCoord.x * 12.9898 + gl_FragCoord.y * 78.233) * 43758.5453));
//offset to eight locations surround target: permute top/bottom, anterior/posterior, left/right
float dx = 0.5; //distance from target voxel
vec3 vTAR = vec3( dx, dx, dx)*sliceSize;
vec3 vTAL = vec3( dx, dx,-dx)*sliceSize;
vec3 vTPR = vec3( dx,-dx, dx)*sliceSize;
vec3 vTPL = vec3( dx,-dx,-dx)*sliceSize;
vec3 vBAR = vec3(-dx, dx, dx)*sliceSize;
vec3 vBAL = vec3(-dx, dx,-dx)*sliceSize;
vec3 vBPR = vec3(-dx,-dx, dx)*sliceSize;
vec3 vBPL = vec3(-dx,-dx,-dx)*sliceSize;
for(int i = 0; i < loops; i++) {
if (doBlur) {
colorSample = texture3D(intensityVol,samplePos+vTAR);
colorSample += texture3D(intensityVol,samplePos+vTAL);
colorSample += texture3D(intensityVol,samplePos+vTPR);
colorSample += texture3D(intensityVol,samplePos+vTPL);
colorSample += texture3D(intensityVol,samplePos+vBAR);
colorSample += texture3D(intensityVol,samplePos+vBAL);
colorSample += texture3D(intensityVol,samplePos+vBPR);
colorSample += texture3D(intensityVol,samplePos+vBPL);
colorSample *= 0.125; //average of 8 sample locations
} else
colorSample = texture3D(intensityVol,samplePos);
colorSample.a = 1.0-pow((1.0 - colorSample.a), opacityCorrection);
colorSample.rgb *= colorSample.a;
//accumulate color
colAcc = (1.0 - colAcc.a) * colorSample + colAcc;