After rendering the scene with deferred rendering pipeline, I started with Screen Space Ambient Occlusion for the 6th week of my project.
In reality, when the light is projected on an object, it scatters in all directions with varying intensities, so the indirectly lit parts of a scene should also have varying intensities. One type of indirect lighting approximation is called ambient occlusion that tries to approximate indirect lighting by darkening creases, holes and surfaces that are close to each other. These areas are largely occluded by surrounding geometry and thus light rays have fewer places to escape, hence the areas appear darker.
Ambient occlusion techniques are expensive as they have to take surrounding geometry into account. In 2007 Crytek published a technique called screen-space ambient occlusion (SSAO) for use in their title Crysis. This technique uses the scene’s depth in screen-space to determine the amount of occlusion instead of real geometrical data. This approach is incredibly fast compared to real ambient occlusion and gives plausible results.
I’m referring to John Chapman’s SSAO tutorial for my implementation.
The idea behind screen-space ambient occlusion is simple: for each fragment on a screen-filled quad we calculate an occlusion factor based on the fragment’s surrounding depth values. The occlusion factor is then used to reduce or nullify the fragment’s ambient lighting component. The occlusion factor is obtained by taking multiple depth samples in a sphere sample kernel surrounding the fragment position and compare each of the samples with the current fragment’s depth value. The number of samples that have a higher depth value than the fragment’s depth represents the occlusion factor.
It is clear that the quality of this technique is directly related to the number of surrounding samples we take. If the sample count is too low the quality/precision reduces drastically and we get an artifact called banding; if it is too high we lose performance. John Chapman says that we can reduce the number of samples we have to test by introducing some randomness into the sample kernel. By randomly rotating the sample kernel each fragment we can get high-quality results with a much smaller amount of samples. But the randomness also introduces a noticeable noise pattern. He says that we can fix it by blurring the results.
In Crysis, as the kernel used was a sphere, it caused flat walls to look gray as half of the kernel samples end up being in the surrounding geometry. For this reason, John Chapman suggests using a hemisphere instead of a sphere which can be oriented along the surface’s normal vector. By sampling around this normal-oriented hemisphere we do not consider the fragment’s underlying geometry as a contribution to the occlusion factor. This removes the gray-feel of ambient occlusion.
So for this week, I studied in detail about how to implement SSAO. I also had to learn about Gramm-Schmidt process which helps me in creating an orthogonal basis.
So why do we need an orthogonal basis? As it is difficult nor plausible to generate a sample kernel for each surface normal direction we can generate a sample kernel in tangent-space, with the normal vector pointing in the positive z direction. To then transform the samples from tangent-space to view-space, I need a TBN (tangent, bi-tangent, & normal) matrix. To create the TBN matrix, I need a normal, tangent and bi-tangent vector. All of these vectors are orthogonal to each other. I already have the normal vector that I get while loading a model. To calculate the tangent, I use a random vector and use the Gramm-Schmidt process to get the vector orthogonal to the normal. I can then cross-product with the normal and the tangent vector to get the bi-tangent vector. Using these 3 orthogonal vectors, I can then create a TBN matrix.
