ssao feedback

David_Gallagher 102 Jul 07, 2012 at 01:21

hi i was just wondering if this looks correct, I’ve been struggling with this for 4 days and it finally seems like i have it, but would appreciate some feedback if it looks wrong.
it’s not blurred or anything yet just trying to get the ssao looking correct.


see in action here: video

and a pic…

10 Replies

Please log in or register to post a reply.

Reedbeta 167 Jul 07, 2012 at 03:09

Feels like the depth threshold needs to be tightened up - you’re getting shadowing between surfaces that are too far apart to shadow each other, such as from the monster’s left arm onto the spheres, or from his head onto his right shoulder.

David_Gallagher 102 Jul 07, 2012 at 06:29

Thanks Reedbeta, helps to have an other opinion, will have a good look at that tomorrow and hopefully be able to clean it up a bit more.

Vilem_Otte 117 Jul 07, 2012 at 18:37

Also, look at the top one sphere, it looks like it’s darker on top and lighter on bottom (maybe though it just *feels* like that and actually isn’t). Although maybe it’s just my opinion but shouldn’t SSAO be bright white where there is no occlusion (at least in my SSAO shader it does bright-white) … (Of course one can simply scale the values).

David_Gallagher 102 Jul 07, 2012 at 20:21

Thanks Vilem, yeah that slight darkness you say on top of sphere is dependant on the angle of view, if i look directly at the sphere the darkness adjusts. but this isn’t noticable with textures at least.
most of the examples i’ve serached through all seem to have a none pure white to them, but it does sound much more preferable to have that pure white you described so i will have a play with that too but the slight shading of the white does add something to the punch of the scene, but then again maybe i’ve been staring at it too long.

Vilem_Otte 117 Jul 08, 2012 at 01:20

I finally got to reply - so actually I found my SSAO code on HDD, I’ll give it few commentaries + upload (code + images) to show what SSAO shader I used in projects some 2 years ago - but not today - It’s 3 AM here and I seriously need some sleep

(Note: I hate this weather, 36 C during day (24 C right now outside), so one can do something only in the evening/during the night … even sleeping in that weather is impossible - but it’ll be awesome sweet wine (looking forward to making it) after this like summer :P ).

David_Gallagher 102 Jul 08, 2012 at 06:37

that would be great Vilem, would really appreciate that as it is hard piecing things together from many different sources, and might be a better implementation from what I have currently found. yeah go get some rest! :)

as for my current progress, turned out that my position buffer wasn’t correct, now it looks a lot better.

but one thing I don’t understand is why the hardfaces have now appeared on the surface (see pic below)…thanks for any advice in this area probably something simple I’ve overlooked in my zombie like state.


Reedbeta 167 Jul 08, 2012 at 07:13

Hmm, do you used smoothed normals in the algorithm or are you using screen-space derivatives to reconstruct the normals? If the latter, you’ll get hard normals corresponding to the actual polygonal geometry in the depth buffer.

Vilem_Otte 117 Jul 08, 2012 at 10:31


The whole code is here (ehm… how can one turn on syntax highlighting? :) … SOLVED - it turns on automatically after post :) ):

/** Constants **/
/* Scaling constants (can also be uniforms!) */
const float sample_scale = 5.0;
const vec2 random_scale = vec2(10.0, 5.25);
const int sample_count = 10;
const float depth_scale = 0.001;
const float jitter_scale = 0.1;

/** Some needed texture samples **/
/* Sample from random texture - noisy RGB normal map */
vec3 random = texture(tex_random, tex_coord * vec2(random_scale.x, random_scale.y)).xyz * 2.0 - 1.0;

/* Position map & Normal map */
vec3 position_map = texture(tex_position, tex_coord).xyz;   //< !!! Reconstruct from depth, I was too lazy to add it to old software
vec3 normal_map = texture(tex_normal, tex_coord).xyz * 2.0 - 1.0;

/** SSAO variables **/
float occlusion = 0.0;           //< value where everything will accumulate
float depth = texture(tex_depth, tex_coord);  //< depth value at pixel we're currently shading
float incx = sample_scale * 1.0 / win_width;  //< step size in x-direction
float incy = sample_scale * 1.0 / win_height;  //< step size in y-direction

/* Just variables for computing sample point */
float dx0 = incx;
float dy0 = incy;

float ang = 0.0;   //< Angle

/** Compute SSAO **/
/* We're circularly sampling around the point with some random noise, calculating the occlusion, note that it's absolutely
* necessary to use enough samples - 8 is fine, 16 is very good, 128 is damn too smooth (and damn too slow).
* Also it's good to mention that ambient occlusion is not affecting resulting image that much - e.g. a noise is hardly noticeable
* unless too strong. The strength of noise can be tuned through 'jitter_scale' variable, or in random-texture. (Can be optimized
* by just modifying texture and not multiplying here later - saves 2 MULs per loop cycle per pixel) */
for(int a = 0; a < sample_count; a++)
      /* Compute coordinates (note they're jittered!) */
      float dzx = (dx0 + jitter_scale * random.x) / depth;
      float dzy = (dy0 + jitter_scale * random.y) / depth;

      float angle = ang * 3.14 / 180.0;

      /* TODO: Do this faster + more precision might be needed (although it runs nice so far) */
      float dx = cos(angle) * dzx - sin(angle) * dzy;
      float dy = sin(angle) * dzx + cos(angle) * dzy;

      /* Sample position and normal texture */
      vec3 pos = texture(tex_position, tex_coord + vec2(dx, dy)).xyz - position_map;
      vec3 norm = texture(tex_data, tex_coord + vec2(dx, dy)).xyz;

      /* Normalize and scale */
      vec3 v = normalize(pos);
      float d = length(pos) * depth_scale;

      /* Heavy wizardy and deep magic */
      /* This code is actually computing amount of occlusion between two points - note that it's absolutely necessary to fine tune the
       * constant numbers here to get the best result! */
      occlusion += (1.0 - clamp(max(dot(norm, -v), 0.0) - 0.05, 0.0, 1.0)) * clamp(max(dot(normal_map, v), 0.0) - 0.05, 0.0, 1.0) * (1.0 - 1.0 / sqrt(0.2 / (d * d * 23.0) + 1.0));

      /* Continue sampling in a "sphere" around the point */
      dx0 += incx;
      dy0 += incy;
      ang += 360.0 / ((float)sample_count);

occlusion /= ((float)sample_count);  // The actual occlusion is average over samples

/** SSAO Result **/
/* We've got occlusion, where 1.0 means the point is fully occluded from environment and 0.0 that it's fully visible to environment
* but we need 1.0 when fully visible and 0.0 when fully occluded, also a bit of darkening through power function can be done (not
* necessary of course - fine-tune this to get the best result */
float ssao = pow(1.0 - clamp(occlusion, 0.0, 1.0), 2.0);  // The result (e.g. SSAO result)

It’s not the best one image (though I didn’t have better scane in my hands currently) - it might need some fine tuning of course (but well… I was too lazy to do that). Also it can be optimized a bit more without too much effort (reconstruct position from depth, save few operations in cycle by pre-computing them, etc.)… Thy almighty image:


Also note that it can be quite easily fine-tuned by const params (making it more local/more global occlusion, darkening/lightening it, etc.).

If you don’t understand any part of code (counts for anyone crawling through this topic) - feel free to ask. I’ll gladly explain principle behind it.

I’m also adding random texture:

David_Gallagher 102 Jul 08, 2012 at 20:56

Thanks Reedbeta will look at the normals. I am doing smoothed normals though I did also try reconstructing but it gave a slightly blurred version of the same above so i must not be doing my normals correctly, will dig into that next.

Thanks so much Vilem, that looks so much better than mine, really appreciate it. today might be the day I can finnish this (really sick of ssao right about now :) , but good to see a really nice example of it).

David_Gallagher 102 Jul 08, 2012 at 23:28

your code game me the idea to remove the depth buffer completly from the final output and that gave me the nice white like yours! Thanks VIlem.

as for the hard normals, i fiddled with my position buffer again and they cleaned up alot more, still a little noticeable on the hard edges, but i think once i add normal mapping to the scene it should clean the rest up.

thanks guys for the input really appreciate!

anyway here a pic…