In this reprinted #altdevblogaday opinion piece, game programmer Simon Yeung shares the results from his implementations of angle-based SSAO, or Screen Space Ambient Occlusion. SSAO (Screen space ambient occlusion) is a common post processing effect that approximates how much light is occluded in a given surface by the surrounding objects. In this year's SIGGRAPH, there were a few slides in "The Technology behind the Unreal Engine 4 Elemental Demo" about how Epic implements SSAO. Their technique can either use only the depth buffer or with the addition of per-pixel normal. I tried to implement both versions with a slight modification:
Using only the depth bufferThe definition of ambient occlusion is to calculate the visibility integral over the hemisphere of a given surface:
To approximate this in screen space, we design our sampling pattern as paired samples:
paired sample patternSo for each pair of samples, we can approximate how much the shading point is occluded in 2D instead of integrating over the hemisphere:
The AO term for each given pair of samples will be min( (θleft + θright)/π, 1). Then by averaging the AO terms of all the sample pairs (in my case, there are 6 pairs), we achieve the following result:
Dealing with large depth differencesAs seen in the above screen shot, there are dark halos around the knight. But the knight should not contribute AO to the castle, as he is too far away. So to deal with the large depth differences, I adopted the approach used in Toy Story 3. If one of the paired samples is too far away from the shading point, say the red point in the following figure, it will be replace by the pink point, which is on the same plane as the other valid paired sample:
So we can interpolate between the red point and the pink point for dealing with the large depth difference. Now the dark halo is gone:
Approximating arc-cos functionIn this approach, the AO is calculated by using the angle between the paired samples, which needs to evaluate the arc-cos function, which is a bit expensive. We can approximate acos(x) with a linear function: π(1-x)/2.
And the resulting AO looks much darker with this approximation:
This may affect the AO for the area of a curved surface with low tessellation. You might either need to increase the bias angle threshold or switch to a more accurate function. So my second attempt is to approximate it with a quadratic function: π(1- sign(x) * x * x)/2.
And this approximation shows a very similar result to the one using the arc-cos function.
Using per-pixel normalWe can enhance the details of AO by making use of the per-pixel normal. The per-pixel normal is used for further restricting the angle to compute the AO where the angle θleft, θright are clamped to the tangent plane:
And here is the final result: