Original Post
Hello! I´m new here. I´ve recently been experimenting with screen space ambient occlusion but i was quite disappointed with the results, its very difficult to get it to look like in Crysis. So i tried to implement it myself without following the crytek method. I ended up creating this shader which implements a type of ssao i have dubbed coherent ssao (cssao for friends). It looks far better than crysis´s (or at least that´s what i think). Now i need help because i want to extend it to full ambient occlusion, (still in image space, but taking into account hidden geometry). I´ll post the shader and explain the method first. The method is quite simple: a convolution kernel which samples pixels from a depth buffer and a normal buffer, calculates occlusion for each pixel using mainly the angular difference between normals, and then modulates it using depth. Usually this would only produce a cartoon outline type shader because it would shade "outside" as well as "inside" creases, but then i apply a cosine difference check between normal and direction of sampling (simple dotproduct between normalized vectors, this is the "coherence" test), that returns >0 for inside creases and <0 for inside ones, so i only occlude concave creases. The resulting AO doesn´t need to be blurred to look good, doesn´t suffer from halos, can be used for local as well as global ao -local looks better ;)-, and is quite fast to compute. A 3x3 kernel is more than enough for a game. Now for the code (needs to be cleaned up): GLSL: Now the question: I have a vague idea of using a second camera to capture the back faces of the geometry, or using a second pass with front culling and a reverse normal buffer to calculate occlusion from the hidden parts of the image, but i´ve tried to imagine it and write it on paper and i have a feeling that it won´t work. Any ideas? thanks and sorry for my weird english PD: normals are supplied in camera space and the zbuffer is not linear. images with 3x3 filter and radius=7:
[Edited by - ArKano22 on December 8, 2008 5:51:31 PM]
#define NUM_SAMPLES 8
uniform sampler2D som; //the depth buffer
uniform sampler2D normal; //the normal buffer
//noise producing function to reduce banding (got it from someone else´s shader):
float rand(vec2 co){
return 0.5+(fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453))*0.5;
}
void main()
{
float sum = 0.0;
float zFar = 80.0;
float zNear = 0.5;
float prof = texture2D(som, gl_TexCoord[0].st).x;
prof = zFar * zNear / (prof * (zFar - zNear) - zFar); //linearize z sample
vec3 norm = normalize(vec3(texture2D(normal,gl_TexCoord[0].st).xyz)*2.0-vec3(1.0));
int hf = NUM_SAMPLES/2;
//calculate sampling rates:
float incx = (1.0/160.0)*8;//8 is the radius
float incy = (1.0/120.0)*8;
for(int i=-hf; i < hf; i++){
for(int j=-hf; j < hf; j++){
vec2 coords = vec2(i*incx,j*incy)/prof;
float prof2 = texture2D(som,gl_TexCoord[0].st+coords*rand(gl_TexCoord[0])).x;
prof2 = zFar * zNear / (prof2 * (zFar - zNear) - zFar); //linearize z sample
if (prof2>prof){
vec3 norm2 = normalize(vec3(texture2D(normal,gl_TexCoord[0].st+coords*rand(gl_TexCoord[0])).xyz)*2.0-vec3(1.0));
//calculate approximate pixel distance:
vec3 dist = vec3(coords,prof-prof2);
//calculate normal and sampling direction coherence:
float coherence = dot(normalize(-coords),normalize(vec2(norm2.xy)));
//if there is coherence, calculate occlusion:
if (coherence > 0){
//approximate form factor:
float pformfactor = 0.5*((1.0-dot(norm,norm2)))/(3.1416*pow(abs(length(dist*4)),2.0)+0.5);//4 is depthscale
sum += clamp(pformfactor*2,0.0,1.0);//2 is ao intensity;
}
}
}
}
float occlusion = 1.0-(sum/NUM_SAMPLES);
gl_FragColor = vec4(occlusion ,occlusion ,occlusion ,1.0);
}
[Edited by - ArKano22 on December 8, 2008 5:51:31 PM]



