my new ssao & some help

Started by
39 comments, last by broli 15 years, 3 months ago
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:

#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);
}













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]
Advertisement
Another idea to increase performance would be to reduce kernel size (along with radius, which already shrinks) when depth increases, i´ll try to add it to the code.
That seems like a very interesting implementation - thanks for sharing.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Can we get a bigger version of the full-color shot? Thanks. :)

[Hardware:] Falcon Northwest Tiki, Windows 7, Nvidia Geforce GTX 970

[Websites:] Development Blog | LinkedIn
[Unity3D :] Alloy Physical Shader Framework

Quote:Original post by n00body
Can we get a bigger version of the full-color shot? Thanks. :)

Yes, i had trimmed the size a bit in order to keep the post relatively small. Note that i´m multiplying the ao on top of the scene, so it can look weird in less illuminated areas. I will use in in the lighting equation once i complete the shader.
Here are another two screenshots:
with very local ao (radius = 2):

with more global ao (radius = 9). Contrast increased in photoshop:

Both images keep the kernel size as 3x3. More samples yield more quality. With this settings (k=3x3,redius=9) it runs at 190-210 fps in a 8800GT, in a moderately complex scene (terrain+skybox+sponza atrium+physics going on). Any ideas about the backface occlusion thing?

Thanks :)
I was messing around with the shader, and i tried to convert it to a color bleeding shader(global illumination is too big to speak about here :P)

The results are quite nifty for a convolution, i think:


Settings are the same as with global ssao. The changes made to the shader are minimal, I will post it tomorrow. Hopefully i will find a way to merge both shaders into one, trying to use common computations only once (different kernel sizes for ao & color bleeding are a problem).

Still thinking about a way to take invisible geometry into account...
Quite impressive. Sounds like it runs at a decent speed as well. I'm very interested in seeing it when you combine the two.
Good job!

I am developing similar technique (called ISR - 'image-space radiosity') for my PhD thesis - I hope I will able to share my work soon.

For back faces you can try the second pass with inverted culling - it should give you more precise results.

for the backgeometry, you'd need something like "deep shadow maps". order independent transparency needs that feature, too.

depth peeling, that was the name.

should help a bit to google around.

i really like your work. is it free to use? :)
If that's not the help you're after then you're going to have to explain the problem better than what you have. - joanusdmentia

My Page davepermen.net | My Music on Bandcamp and on Soundcloud

Quote:i really like your work. is it free to use? :)


Yes, it is free to use. Just share the results if you achieve something better :). The depth peeling idea sounds promising, i had heard about it before but never really implemented it, or found a practical use for it aside of transparency. I´ll google for info.

This topic is closed to new replies.

Advertisement