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):



2 Answers 2


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

void main() {
    gl_TexCoord[1] = gl_MultiTexCoord1;
    gl_Position = ftransform();
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[1].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;
        samplePos += deltaDir;
        lengthAcc += stepSize;
        // terminate if opacity > 95% or the ray is outside the volume
        if ( lengthAcc >= len || colAcc.a > 0.95 ) break;
    colAcc.rgb = mix(clearColor,colAcc.rgb,colAcc.a);
    gl_FragColor = colAcc;

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .