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 9x9 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 9x9 blur (does not blur over depth+normals discontinuities):

Blurred AO

Blur that goes in 2 pixel stride (effectively 17x17):

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 25x25):

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 2x2 smaller resolution (still using low sample count etc.):

AO computed at lower resolution

Now, 2x2 smaller AO, blurred with 3 pixels stride:

AO at lower resolution, blurred with 3 pixel stride

Happy blurring!