SSAO blur

Started by
5 comments, last by IntegralKing 12 years, 9 months ago
Hello, I'm implementing SSAO and I'm having some troubles with the blur.
What I do is:
1) Render ssao to half-res buffer (I use full-res normals/depth buffer)
2) Blur ssao to full-res buffer, first horizontally then vertically

And this is my output:
halfres_blur.jpg

It looks like it's blurring more in the y axis. Besides it's still kind of noisy, although it has 32 samples for ssao and a big blur size.
Increasing the size of the blur even more doesn't make any difference.
Should I upsample the ssao buffer before blurring? Or do you think this is good enough?
I've read that some people use a downsampled normal/depth buffer when rendering ssao to a lower resolution. Would doing this help?

Here is the ssao buffer without the blur and in full-res:
fullres_noblur.jpg
Advertisement
2) Blur ssao to full-res buffer, first horizontally then vertically

Do you actually mean that you blur the SSAO horizontally to an intermediate buffer, and then vertically blur that buffer onto your final render target?

First get this blur method working:

Blur horizontally onto intermediate buffer.
Blur vertically onto original SSAO buffer.
Copy original SSAO buffer onto final render target (using bilinear mag filter).

If that works, then you can skip the copy to the original SSAO buffer and copy directly onto your final render target while vertically blurring.

If you are already doing that, then let's see your blur shader and tell us the parameters set on the shader.
What I was doing in the first picture was:
1- SSAO onto half-res buffer
2- blur horizontally onto intermediate buffer (full-res)
3- blur vertically onto final render target

If I do the 2nd step to half-res, the 3rd step to the ssao buffer and then copy to the final render target using
bilinear mag filter I get this output:
halfres_blur2.jpg

This is the fragment shader. It is (or tries to be) a bilateral filter like the one explained here.
Parameters are: kernelSize = 11, positionThreshold = 0.5, normalThreshold = 0.15


float4 main(vertex2fragment IN, uniform sampler2D NormalBuffer, uniform sampler2D AOBuffer,
uniform float2 InvViewportSize, // (1/viewport.width, 1/viewport.height)
uniform int KernelSize, uniform float PositionThreshold, uniform float NormalThreshold) : COLOR
{
float2 texCoord = (IN.ScreenPos.xy / IN.ScreenPos.w) * 0.5f + 0.5f;
float4 normalDepthVS = tex2D (NormalBuffer, texCoord);
float3 pixelNormalVS = normalDepthVS.rgb * 2.0f - 1.0f;
float pixelDepth = normalDepthVS.a;

float weightedAO = 0.0f;
float AO = 0.0f;
float weightSum = 0.0f;

for (int i = -KernelSize; i <= KernelSize; i++)
{
float2 sampleTexCoord = texCoord + float2 (i * InvViewportSize.x, 0.0f);
float4 sampleDepthNormalVS = tex2D (NormalBuffer, sampleTexCoord);
float3 sampleNormalVS = sampleDepthNormalVS.rgb * 2.0f - 1.0f;
float sampleDepth = sampleDepthNormalVS.a;

float deltaZ = sampleDepth - pixelDepth;
float dotN = dot (pixelNormalVS, sampleNormalVS);
float4 sampleAO = tex2D (AOBuffer, sampleTexCoord);

if (deltaZ < PositionThreshold && dotN > 1.0f - NormalThreshold)
{
float totalWeight = pow (dotN, 32.0f) / (EPSILON + deltaZ); //EPSILON=0.01f
weightedAO += sampleAO.a * totalWeight;
weightSum += totalWeight;
}
AO += sampleAO.a;
}

if (weightSum > 0.0f)
{
AO = weightedAO / weightSum;
}
else
{
AO /= 2.0f * KernelSize + 1.0f;
}
return float4 (AO);
}

What I was doing in the first picture was:
1- SSAO onto half-res buffer
2- blur horizontally onto intermediate buffer (full-res)
3- blur vertically onto final render target


Isn't step 2 the problem then? (As you will get a different amount of horizontal blur when upscaling than when you vertically blur to the same scale.)

Did you try blurring as I suggested to see if that is in fact the problem?

I realize that the method that I suggested is not the most performant one, but it is easy to test, and the point is to see if this is the source of your blur issue.
You probably don't want to blur at full res. Blurring at full res requires a huge sampling radius to look good which can be very expensive, especially if you do it in a pixel shader without shared memory. Even at quarter res a wide-radius bilateral blur won't be cheap.
I think the idea of SSAO performed at low res is to do the blurring at the low res, and then bilateral upsample to full res. (Where bilateral upsampling is essentially bilinear upsampling with normal and depth values affecting edge weights).


Like MJP pointed out, you are trying to simultaneously blur and upsample to full res at the same time.


Am I right?
Ok thanks both for the answers.

My ssao has a flickering pattern in the shadowed areas. I know it's a common problem, and when performing ssao at full res
or even at half res it's not that noticeable, but at quarter resolution it gets really annoying. Is there anything I can do to improve
this?
My ssao has a flickering pattern in the shadowed areas. I know it's a common problem, and when performing ssao at full res
or even at half res it's not that noticeable, but at quarter resolution it gets really annoying. Is there anything I can do to improve
this?



Here are a few AO methods:
http://www.jshopf.com/blog/?p=226

The first one, volumetric obscurance, chooses samples that create less noise in both statically and temporally.




Also, inspecting the shader code you posted above, I see that your "bilateral filter" behaves very little like the one in the paper you mentioned.

The bilateral filter in that paper takes 8 samples (4 fine + 4 coarse) of the color, normal and depth, and applies a weighting to each set of the color/normal/depth samples. This filter is done in one pass.

Your shader appears to be doing two passes and 11 samples per pass.


If you fix that shader, the perf gain might leave you with similar performance to your current 1/4 res implementation, where there is less temporal noise?

This topic is closed to new replies.

Advertisement