Strided blur and other tips for SSAO

If you’re new to SSAO, here are good overview blog posts: meshula.net and levelofdetail. Some tips and an idea on strided blur below.

Bits and pieces I found useful

  • SSAO can be generated at a smaller resolution than screen, with depth+normals aware upsample/blur step.
  • If random offset vector points away from surface normal, flip it. This makes random vectors be in the upper hemisphere, which reduces false occlusion on flat surfaces. Of course this requires having surface normals.
  • When generating random vectors for your AO kernel:
    • Generate vectors inside unit sphere (not on unit sphere).
    • Use energy minimization to distribute your samples better, especially at low sample counts. See malmer.ru blog post.
  • In your AO blurring/upsampling step: no need to sample each pixel for blur. Just skip some of them, i.e. make kernel offsets larger. See below.

Strided blur for AO

Normally you’d blur AO term using some sort of standard blur, for example separable Gaussian: horizontal blur, followed by vertical blur. How one can imagine horizontal blur kernel:
Horizontal Blur Kernel

Here’s how Rune taught me how to blur better:

Rune:
The other thing is the blur. I tried to make the blur 4 times stronger, and it looks much better IMO without any artifacts I could see. I could even use 4x downsampling with that blur amount and still get acceptable results.
Aras:
how did you make it 4x stronger? (I was going to say that blur step is already quite expensive, and I don’t want to add more samples to make it even more expensive, yadda yadda)
Rune:
m_SSAOMaterial.SetVector (“_TexelOffsetScale”, m_IsOpenGL ?
  new Vector4 (4,0,1.0f/m_Downsampling,0) :
  new Vector4 (4.0f/source.width,0,0,0));
And similar for vertical.
Aras:
hmm. that’s strange :)
Rune:
I have no idea what I’m doing of course but it looks good.
Aras:
so this way it does not do Gaussian on 9×9 pixels, but instead only takes each 4th pixel. Wider area, but… it should not work! :)
Rune:
It creates a very fine pattern at pixel level but it’s way more subtle than the noise you get otherwise.
Aras:
ok (hides in the corner and weeps)

So yeah. The blur kernel can be “spread” to skip some pixels, effectively resulting in a larger blur radius for the same sample count:
Blur with 2 pixel stride

Or even this:
Blur with 3 pixel stride

Yes, it’s not correct blur. But that’s okay, we’re not building nuclear reactors that depend on SSAO blur being accurate. If you are, SSAO is probably a wrong approach anyway, I’ve heard it’s not that useful for nuclear stuff.

I’m not sure how this blur should be called. Strided blur? Interleaved blur? Interlaced blur? Or maybe everyone is doing that already and it has a well established name? Let me know.

Some images of blur in action. Raw AO term (very low – 8 – sample count and increased contrast on purpose):
Raw AO at low sample count

Regular 9×9 blur (does not blur over depth+normals discontinuities):
Blurred AO

Blur that goes in 2 pixel stride (effectively 17×17):
Blurred AO with stride 2
It does create a fine interleaved pattern because it skips pixels. But you get wider blur!
Blurred AO with stride 2, magnified

Blur that goes in 3 pixel stride (effectively 25×25):
Blurred AO with stride 3
At 3 pixel stride the artifacts are becoming apparent. But hey, this is very
low AO sample count, increased contrast and no textures in the scene.
Blured AO with stride 3, magnified

For sake of completeness, the same raw AO term, but computed at 2×2 smaller resolution (still using low sample count etc.):
AO computed at lower resolution

Now, 2×2 smaller AO, blurred with 3 pixels stride:
AO at lower resolution, blurred with 3 pixel stride

Happy blurring!

11 Responses to 'Strided blur and other tips for SSAO'

  1. imbusy

    So that’s bilateral blur you’re using, not gaussian, right? Otherwise how do you get it to respect the boundaries?

  2. Aras Pranckevičius

    @imbusy: yeah, something like that. I’m not sure if it’s exactly “bilateral”, but basically it discards samples based on depth/normal difference.

  3. imbusy

    It shouldn’t be separable, but hey, you said it yourself, we’re not building nuclear reactors :)

  4. steve

    I discovered something similar when doing PCSS on a project last year – that although I should have needed to increase the number of PCF samples as the kernel widened, it was surprising what you could actually get away with in practice. And interestingly, the more detailed / non-uniform your diffuse textures are, the more you can get away with, because the diffuse channel makes it even harder to spot the ‘dithering’ patterns.

    Moral of the story, theory sucks, random experimentation FTW :)

  5. ReJ

    @Aras: I can’t believe you didn’t knew this “trick”… I’m pretty sure we’ve been doing it in late Interamotion days even.

  6. Real-Time Rendering » Blog Archive » Clearing the Queue

    [...] This is one more reason the Internet is great: an in-depth article on normal compression techniques, weighing the pros and cons of each. This sort of article would probably not see the light of day in traditional publications, even Game Developer – too long for them, but all the info presented here is worthwhile for a developer making this decision. Aras’ blog has other nice bits such as packing a float into RGBA and SSAO blurring. [...]

  7. Aras Pranckevičius

    @imbusy: well yeah, it’s not bilateral in fact. I’m just doing kinda-Gaussian, and discarding samples that don’t meet some criteria.

    @steve: word!

    @ReJ: I don’t remember us using it, actually. Or I have already forgot about it :)

  8. imbusy

    Using bilateral or just discarding – it’s still not separable.

  9. icastano

    Something that I’ve suggested a few times, but never tried myself, is to use some sort of importance sampling. Pixels closer to the center should be sampled at a higher frequency, since they have more important contributions, than pixels with lower contributions. Note that you would also have to readjust the weights. This is basically like the 2D poison disk distribution that ATI used in some of the old demos, but on one dimension only.

  10. Aras Pranckevičius

    @imbusy: ok ok. But it works! :)

    @icastano: yeah, makes sense.

  11. Timothy Farrar

    Would be interesting to try adjusting filter tap location by a function of VPOS to attempt to break up the interleave pattern (assuming TEX bound with free ALU capacity). Might play havoc on texture cache however.

    Might also be interesting to play with temporal feedback in the filtering (like Gears II) but in combination with Ignacio’s suggestion of importance sampling and changing filter position temporally.

Leave a Reply