OpenGL ES 2.0 fragment shader to blur is slow and low quality

14,247

Solution 1

That shader needs to run twice for it to work, what you call blurSize should be a vec2 and the value of that should be vec2(0, 1.0/height) for vertical blur and vec2(1.0/width, 0) for horizontal blur.

See http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=240334

The idea behind doing a two pass blur is that it will dramatically reduce the number of texture lookups and hopefully increase the speed. A two pass blur with kernel size of 7x7 will require 14 texture lookups but if it done in a nested loop you'll need to do 49 texture lookups.

Solution 2

Doing two or more box blur passes improves quality to very near gaussian blur while keeping performance relatively high. And box blurs can be sped up relatively easily as well. Have a look at http://web.archive.org/web/20060718054020/http://www.acm.uiuc.edu/siggraph/workshops/wjarosz_convolution_2001.pdf

In any case, in most moving scenes you can get away with lower-fidelity rendering than might seem obvious from screenshots.

Solution 3

It's not clear what exactly your code is doing. You are using texture2D which suggests a 2D filter. Yet your convolution matrix has one dimension and you loop only once. I may be wrong, but it seems that you're applying the blur diagonally. If it's meant to be 2D filter than you'd need two (nested) loops for x and y respectively to cover a 2D area.

And about the variable blurSize - it's name is a bit misleading. The size of the blur depends on your convolution matrix. Your's is 7 pixels wide. That determines size. The variable is more like a "strength" of blur which can only fade the effect of convolution matrix. If given too high value, artifacts will arise.

I'm not an expert and haven't written any pixel shader besides "hello world" mandelbrot one. If I'm not wrong, than the blur shader is one of the worst to speed up. Most real-time blurs I saw were box-blurs. Try porting some code from here: gameDev thread.

Share:
14,247
Brian
Author by

Brian

Mostly Cocoa, Core Graphics, Core Bluetooth, Bernstein crypto (ED and Curve25519, ChaChaPoly1305) and a bit of Metal Shader programing. I'd love to play with Tensor Flow sometime.

Updated on June 04, 2022

Comments

  • Brian
    Brian almost 2 years

    I am trying to write a blur shader for the iPad. I have it working but I am not very happy with the results. I get very choppy frame rates and the blur looks like crap when blur amount is high.

    Any ideas on how to improve things?

    Some sample output:

    alt text

    uniform sampler2D texture;
    varying mediump vec2 fragTexCoord;
    varying mediump vec3 eyespaceNormal;
    
    varying highp float blurAmount;
    
    void main(void)
    {
        highp vec2 gaussFilter[7];
        gaussFilter[0] = vec2(-3.0, 0.015625);
        gaussFilter[1] = vec2(-2.0, 0.09375);
        gaussFilter[2] = vec2(-1.0, 0.234375);
        gaussFilter[3] = vec2(0.0, 0.3125);
        gaussFilter[4] = vec2(1.0, 0.234375);
        gaussFilter[5] = vec2(2.0, 0.09375);
        gaussFilter[6] = vec2(3.0, 0.015625);
    
        highp float blurSize = blurAmount * 1.0;
    
        /////////////////////////////////////////////////
        // 7x1 gaussian blur fragment shader
        /////////////////////////////////////////////////
    
        highp vec4 color = vec4(0,0,0,1);
    
        for( int i = 0; i < 7; i++ )
        {
            color += texture2D( texture, vec2( fragTexCoord.x+gaussFilter[i].x*blurSize, fragTexCoord.y+gaussFilter[i].x*blurSize ) )*gaussFilter[i].y;
        }
    
        gl_FragColor = color;
    }
    

    Edit: A box blur may be the way to go. Here is a box blur version of the shader:

    highp vec4 color = vec4(0,0,0,1);
    
    color += texture2D(texture, vec2(fragTexCoord.x, fragTexCoord.y - 4.0*blurAmount)) * 0.05;
    color += texture2D(texture, vec2(fragTexCoord.x, fragTexCoord.y - 3.0*blurAmount)) * 0.09;
    color += texture2D(texture, vec2(fragTexCoord.x, fragTexCoord.y - 2.0*blurAmount)) * 0.12;
    color += texture2D(texture, vec2(fragTexCoord.x, fragTexCoord.y - blurAmount)) * 0.15;
    color += texture2D(texture, vec2(fragTexCoord.x, fragTexCoord.y)) * 0.16;
    color += texture2D(texture, vec2(fragTexCoord.x, fragTexCoord.y + blurAmount)) * 0.15;
    color += texture2D(texture, vec2(fragTexCoord.x, fragTexCoord.y + 2.0*blurAmount)) * 0.12;
    color += texture2D(texture, vec2(fragTexCoord.x, fragTexCoord.y + 3.0*blurAmount)) * 0.09;
    color += texture2D(texture, vec2(fragTexCoord.x, fragTexCoord.y + 4.0*blurAmount)) * 0.05;
    
    gl_FragColor = color;
    

    Here is the box blur output(note it's only a horizontal blur, but it might be enough for what i want) : alt text

  • Brian
    Brian over 13 years
    I decided to try out a box blur. Seems to work pretty well but I haven't tried it out on my iPad yet to see if it's fast enough, but looks pretty good. I'll post the code in my answer above. Thanks
  • Brian
    Brian over 13 years
    Yes I think that was my screwup in adapting the code. Thanks. I think that I don't really need a full gaussian for my purposes, so the simple box blur might work well enough and be faster.