Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 648
Brian Avatar asked Dec 04 '10 19:12

Brian


3 Answers

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.

like image 117
Daniel Avatar answered Nov 14 '22 22:11

Daniel


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.

like image 43
fzwo Avatar answered Nov 14 '22 23:11

fzwo


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.

like image 20
Rekin Avatar answered Nov 14 '22 23:11

Rekin