Screen Space Ambient Occlusion Shader

From GameStudio Wiki

Jump to: navigation, search

All text and code samples (c) Michael Auerbach 2009
Do NOT distribute or use in commercial projects

Enlarge

Since 2000, graphics engines have been moving towards dynamic real-time lighting solutions. These lighting algorithms have some advantages and disadvantages over precomputed lighting. Until recently, programming effort was placed primarily on the most direct diffuse and specular terms of the lighting equations. With advances in GPU technology and shader models that allow for large numbers of operations, we can additionally simulate an approximation of global illumination.

Screen Space Ambient Occlusion, or SSAO, was first developed in 2007 by Crytek (Germany). Ambient occlusion is one of several properties of global illumination. Ambient occlusion accounts for objects that receive ambient light from a hemi-spherical direction that in certain cases can be occluded by surrounding objects and geometry. SSAO attempts to take advantage of the scene's depth buffer to reconstruct a vague interpretation of the actual scene visible by the camera. SSAO is far less accurate than precomputed or ray-traced ambient occlusion, however, it is fully dynamic, allowing for moving objects to correctly portray occlusion as visible in real life.

To get started, I have posted a SSAO fragment shader:

//SSAO//(c) Michael Auerbach 2008
float pos  = sqrt(tex2D(depth ,tex).r);
half occlusion = 0;
float sample;
float pPos;
half dist = (tex.x*1440)%8;
dist += (tex.y*900)%8;
dist += 1;
half3 pNorm;
float SSnorm;
float3 vec;
for(int j=1;j<3;j++)
{
	for(int i=0;i<180;i+=45)
	{
		pPos = sqrt(tex2D(depth,tex+float2(cos(i),sin(i))*j*dist*vecViewPort.zw).r);
		SSnorm = sqrt(tex2D(depth,tex-float2(cos(i),sin(i))*j*dist*vecViewPort.zw).r);
		//SSnorm -= pos;
		sample = (pos/pPos) - (SSnorm/pos);
		if((sample>0.001)&&(sample<0.05))
				occlusion+= (sample*30);
	}
}

This code runs a kernel that generates an occlusion value based on per-pixel depths as a post process.

The steps are simple to imagine but unfortunately difficult to implement:

  • First, we render the scene's depth value into a floating point buffer which will be sampled during the SSAO stage
  • Second, we render a post processing stage to run our kernel.
  • Inside the kernel, we sample several points 'n' around our target pixel to examine the pixel's surroundings.
  • If a pixel is further than the source pixel, it is unoccluded. Otherwise, it is blocked in the sampling direction.


One problem that many programmers have when implementing this, is noise. Because SSAO is view-dependent, we often receive output that is non-linear and noisy. To overcome this, I propose a simple screen space normal generation kernel that replaces the traditional depth texture lookup as a bi-directional depth ratio. Combining this with a threshold to eliminate normal edges and depth discontinuities proves to be an extremely fast algorithm that produces results equivalent to those presented by Crytek.

This method is almost 65% faster than the version presented by Crytek - even with a sampling count 'n' of twice that used by Crytek.

The reason why it is so much faster is because we spare several time consuming operations. First, we do not need to generate normals in a previous pass and we do not need to look them up for each sample. Instead, we combine the normal orientation stage with the actual depth comparison stage. There is one downside to this however. Objects will not receive occlusion when viewed from a parallel angle. I have an improved implementation that accounts for multi-directional occlusion models that attempt to reduce the algorithms screen position dependency. This, however, is even more complicated and beyond the scope of this introduction.

I hope you find this kernel useful

Michael Auerbach

Personal tools