SFML community forums

Help => General => Topic started by: Evan Bowman on April 15, 2016, 05:56:46 am

Title: Gaussian blur shader
Post by: Evan Bowman on April 15, 2016, 05:56:46 am
Hi! For a game I'm making with SFML, I am trying to blur parts of a window when opening up an in-game menu. So far I've tried using the blur shader in the examples folder in the SFML 2.0 repo, and it works pretty well, but only for small amounts of blurring. When I start passing in larger parameters, the effect begins to look less good, for example:

A tiny bit of blur:
(click to show/hide)

The amount of blurring that I intend to use:
(click to show/hide)

And the shader:
Code: [Select]
uniform sampler2D texture;
uniform float blur_radius;

void main()
{
    vec2 offx = vec2(blur_radius, 0.0);
    vec2 offy = vec2(0.0, blur_radius);

    vec4 pixel = texture2D(texture, gl_TexCoord[0].xy)               * 4.0 +
                 texture2D(texture, gl_TexCoord[0].xy - offx)        * 2.0 +
                 texture2D(texture, gl_TexCoord[0].xy + offx)        * 2.0 +
                 texture2D(texture, gl_TexCoord[0].xy - offy)        * 2.0 +
                 texture2D(texture, gl_TexCoord[0].xy + offy)        * 2.0 +
                 texture2D(texture, gl_TexCoord[0].xy - offx - offy) * 1.0 +
                 texture2D(texture, gl_TexCoord[0].xy - offx + offy) * 1.0 +
                 texture2D(texture, gl_TexCoord[0].xy + offx - offy) * 1.0 +
                 texture2D(texture, gl_TexCoord[0].xy + offx + offy) * 1.0;

    gl_FragColor =  gl_Color * (pixel / 16.0);
}

I was wondering if anyone could give me some suggestions for how I could make the effect look really smooth? What I'm going for is the amount of smoothness pictured here:
(click to show/hide)
Title: Re: Gaussian blur shader
Post by: fallahn on April 15, 2016, 10:59:41 am
It looks like you're not actually using a gaussian curve for your blur. Take a look at the shader included in the SFML Game Dev book (https://github.com/SFML/SFML-Game-Development-Book/blob/master/08_Graphics/Media/Shaders/GuassianBlur.frag) (note the multiplication of the small numbers which make up the curve), it has some pretty good results:

(http://djfallen.com/images/blur.png)
Title: Re: Gaussian blur shader
Post by: Evan Bowman on April 15, 2016, 02:18:10 pm
It looks like you're not actually using a gaussian curve for your blur. Take a look at the shader included in the SFML Game Dev book (https://github.com/SFML/SFML-Game-Development-Book/blob/master/08_Graphics/Media/Shaders/GuassianBlur.frag) (note the multiplication of the small numbers which make up the curve), it has some pretty good results:

(http://djfallen.com/images/blur.png)

Thanks for the link, I'll take a look!

Edit: is the linked file this a true Gaussian blur? Doesn't look like it's fully convolving the image, rather approximating the effect. Anyway I'll try it out.
Title: Re: Gaussian blur shader
Post by: Evan Bowman on April 15, 2016, 03:38:15 pm
So I used the new shader, ran it in multiple passes, and the result is slightly better.

It's still a little choppy, but maybe that's something I'm doing?

EDIT: By multiple passes I meant one in the x direction and one in the y direction. Would it help maybe to run another pass in both directions?

(http://i.imgur.com/CMoRBR0.png)
Title: Re: Gaussian blur shader
Post by: fallahn on April 15, 2016, 03:50:19 pm
As far as I can tell it approximates the blur with a pre-computed curve (as opposed to averaging the samples). It's not totally clear in the screen shot because I also have another post effect running which purposely adds noise :)

The blur animates nicely though via the offset parameter:
http://www.youtube.com/watch?v=4PeyLftCcNM


I forgot to mention that the blur passes run on a much downsampled render texture (https://github.com/fallahn/LunarMooner/blob/master/LunarMooner/src/LMPostBlur.cpp#L127) (1/4 - 1/8 resolution) which is then smoothed via the texture property, and then stretched to fill the screen.
Title: Re: Gaussian blur shader
Post by: Evan Bowman on April 15, 2016, 04:02:19 pm
Oh ok I am running it on a smaller texture and stretching it, but I never do any smoothing, maybe that's the problem.
Title: Re: Gaussian blur shader
Post by: Evan Bowman on April 15, 2016, 05:52:11 pm
Ok got it working, thanks! In the end the shader's a little finicky about what constants I pass in as blur amounts, it seems to like floating point values between 0 and 1. It's pretty smooth too:
(click to show/hide)
Title: Re: Gaussian blur shader
Post by: fallahn on April 15, 2016, 06:25:17 pm
Looks great! :D
Title: Re: Gaussian blur shader
Post by: Evan Bowman on April 16, 2016, 12:45:41 am
Thanks!

The shader does appear to only take input parameters for blur amount between 0 and 1 divided by the target texture width and height. I wanted a greater amount of blurring than the shader could supply by default, so I had to expand it a bit. Here’s a brief description of how to do it in case anyone’s interested:

The shader works by setting the value of each pixel to sum of a bunch of surrounding pixels, such that the amount that each neighboring pixel contributes to the final fragment color is the product of the value of its original color and number sampled from a normal (gaussian) distribution.

In order to get a greater amount of blurring, you can increase the number of neighboring pixels used in the computation of the final fragment color, and expand the standard deviation of the normal distribution that you are sampling. I didn’t feel like doing the math for that, so I used MATLAB to sample a normal probability density function with a wider spread than the values in the original shader.

Matlab Command Window
Code: [Select]
>> x = [-12:1:12];
>> norm = normpdf(x,0,2.0);
>> norm

norm =

  Columns 1 through 10

    0.0000    0.0002    0.0005    0.0015    0.0038    0.0087    0.0180    0.0332    0.0547    0.0807

  Columns 11 through 20

    0.1065    0.1258    0.1330    0.1258    0.1065    0.0807    0.0547    0.0332    0.0180    0.0087

  Columns 21 through 25

    0.0038    0.0015    0.0005    0.0002    0.0000

You can then adjust the shader like so:
Code: [Select]
uniform sampler2D texture;
uniform vec2 blur_radius;

void main() {
    vec2 textureCoordinates = gl_TexCoord[0].xy;
    vec4 color = vec4(0.0);
    color += texture2D(texture, textureCoordinates - 10.0 * blur_radius) * 0.0012;
    color += texture2D(texture, textureCoordinates - 9.0 * blur_radius) * 0.0015;
    color += texture2D(texture, textureCoordinates - 8.0 * blur_radius) * 0.0038;
    color += texture2D(texture, textureCoordinates - 7.0 * blur_radius) * 0.0087;
    color += texture2D(texture, textureCoordinates - 6.0 * blur_radius) * 0.0180;
    color += texture2D(texture, textureCoordinates - 5.0 * blur_radius) * 0.0332;
    color += texture2D(texture, textureCoordinates - 4.0 * blur_radius) * 0.0547;
    color += texture2D(texture, textureCoordinates - 3.0 * blur_radius) * 0.0807;
    color += texture2D(texture, textureCoordinates - 2.0 * blur_radius) * 0.1065;
    color += texture2D(texture, textureCoordinates - blur_radius) * 0.1258;
    color += texture2D(texture, textureCoordinates) * 0.1330;
    color += texture2D(texture, textureCoordinates + blur_radius) * 0.1258;
    color += texture2D(texture, textureCoordinates + 2.0 * blur_radius) * 0.1065;
    color += texture2D(texture, textureCoordinates + 3.0 * blur_radius) * 0.0807;
    color += texture2D(texture, textureCoordinates + 4.0 * blur_radius) * 0.0547;
    color += texture2D(texture, textureCoordinates + 5.0 * blur_radius) * 0.0332;
    color += texture2D(texture, textureCoordinates + 6.0 * blur_radius) * 0.0180;
    color += texture2D(texture, textureCoordinates - 7.0 * blur_radius) * 0.0087;
    color += texture2D(texture, textureCoordinates - 8.0 * blur_radius) * 0.0038;
    color += texture2D(texture, textureCoordinates - 9.0 * blur_radius) * 0.0015;
    color += texture2D(texture, textureCoordinates - 10.0 * blur_radius) * 0.0012;
    gl_FragColor = color;
}

Remember that all the samples from the gaussian distribution need to add up to one or it’ll darken the final image! Also, obviously if you do it this way a greater amount of blurring is more computationally intensive.

EDIT:
You might be able to get a greater blur with fewer samples by using a flatter distribution.

EDIT2:
Never mind, flattening a distribution also widens it :). If anyone knows how to speed this up let me know! The only way I know of evenly flattening a normal distribution would be convolving it with another normal distribution, which obviously makes it wider, but maybe there's something that I'm not thinking of...