Gaussian blur with varying blur-radius?

Started by
4 comments, last by Viik 15 years, 4 months ago
Hi! I'm having a problem with a part of an algorithm i'm working on, which i thought would be simple (so i mistakenly and stupidly didn't give it much thought) but it turns out to not be as simple at all. :/ I'm currently doing a gaussian blur (2-pass). What i want is to make the blur radius vary across pixels instead of being the same for all pixels. The blur-radius could be stored in the alpha channel of each pixel. I would like each pixel (x,y) to blur its color out to the neighbor pixels according to its blur-radius. This would be very simple if fragment shaders allowed scatter writes (write to other pixels than its own), i could just vary the sample-spacing by an amount proportional to the blur radius (maybe the filter would no longer be separable, though). But GPUs only allows gathering (reading from other pixels than its own), not scattering. How would you do this? Any nice techniques or ideas? Any links to articles/websites discussing this? (i tried googling, but failed) Thanx!
Advertisement
If you want variable per-pixel blur then you can use a growable poission disc blur. There's more details here and I posted shader code here.
Quote:Original post by OrangyTang
If you want variable per-pixel blur then you can use a growable poission disc blur. There's more details here and I posted shader code here.
Thanks for your answer. I have read your code and the ATI-slides, you linked to.

Essentially you are suggesting that i use a 1-pass 2D-blur, but with much fewer (and more cleverly placed) samples than standard 1-pass 2D Gaussian blur, right?

But it doesn't seem like your method does exactly what i need.

What i would like is for each pixel to blur out over the neighboring pixels according to its blur-radius. In other words, when processing a pixel (let us call it the 'center pixel'), the other pixels which are sampled should blur out over the center pixel, based on their own blur-radius value, not based on the blur-radius value of the center-pixel.

In your growable poisson disc approach (both your code and the ATI code), it seems to me like the samples blur out over the center pixel, not based on their own blur-radius, but based on the blur-radius of the center-pixel.

This does not seem to the be same to me... Pixels with a high blur-radius should spread its color over a large area. They should not gather samples from a large area.

Or maybe it is the same, but i am just not seeing it?
I did actually try it, in case it perhaps was a good approximation, but as expected it did not give the desired result (i just used the normal slow 1-pass 2D-blur, not Poisson sampling).
If i am missing something clever, i hope some of you guys will tell me :)



I have tried making my own method which works, but might not be as elegant. The way i do it, when processing a pixel (the "center pixel"):
  • take n samples (always the same sample-spacing for all pixels)
    Each sample has its blur-radius in its alpha channel.

  • for each sample point, i use its blur-radius to lookup its gaussian-weight in a gaussian-texture (to get how much the sample-pixel should blur over the center pixel)

    (actually i calculate the gaussian-expression in the shader right now, instead of looking up in a texture, but i guess that is slower to do, as i have read that division and the 'exp' operation are slow in shaders (are they?))

  • After all samples have been taken, i divide the resulting color by the sum of the gaussian-values for all the samples which were taken, so that they sum to 1. (normalize the gaussian curve)

(although this isn't separable anymore, i have found that the artifacts from doing it in two passes as if it was separable, is good enough - the result is almost completely indistinguishable from doing a (slow) 1-pass 2D-blur)

Is this a bad solution? Do you know of better or other solutions? (i.e. faster solutions?)

I would really like to know if there is a better solution AND if the poisson solution suggested by OrangyTang does actually work, but it is just me who is not seeing it.
Thanx! [smile]



(i can post the shader code if some of you find it easier to understand code then my lame explanation above :) )
Hi again. Just to clearify, i would just like to hear about ANY other method that could work. I realize that it can be hard to know if a method you know or an idea you have is better than the method i outlined above, so just shoot with anything. [smile]
Also, if the poisson method should work, i would like to be told that too :)
Edit: saw that you already did this yourself, ignore this post.

Normally (as in in fake DOF for instance) you have a radius per pixel (based on Z for instance).
I take it that you'd like to go the other way around (more like how meta balls work), where each pixel doesn't store a blur radius, but rather a influence radius (better name needed), right?

Naive solution:
Render a point sprite for each pixel, with varying size (use the source texture in the vertex shader for radius).

Better solution?:
If your pixels have a limited influence radius, let's say 15 pixels at max, you'd need to process 31x31 pixels from the source at each destination point.
I.e:
For every pixel in the 31x31 grid, calculates it's influence on the current pixel, do something based on influence.

Poisson method is good for using variable kernel size. But for effect that you want to achive it's not needed. I'll explaine why:
What you want is not bluring but bleeding. Let's use grey scale image case for simplicity. For example you want pure white pixel to bleed over grey or black pixels. For this you use constant kernel radius, let it be 16x16 pixels. When you sample texels from texture you assign big weight value to pure white pixels and small weight value to grey or black. This way pure white pixels will contribute more than others to current pixel. As you want this effect to vary with radius you simply scale weighes according to distance between sampled texel and center texel.
As I understand in your case you will have some kind of mask, that shows which pixels should bleed more than other, you can use it directly as a weighes.
This stuff dosnt need to be only 16x16, you can choose any size that is good for your effect. You might even separate it into two passes.

This topic is closed to new replies.

Advertisement