I am trying to implement the simple variant of Crytek's screen space ambient occlusion algorithm.
The algorithm as far as I understand it;
That should basically be all there is to it. If the sampled point's depth is higher (it lies beyond the geometry) it does not occluded the current pixel (p).
float z = gl_FragCoord.z; // depth-buffer value for the current pixel
int occluding_points = 0;
vec4 fPosition = model_transformation * vec4(position, 1.0f); // Really from vertex shader
#ifdef CRYTEK_AO
const int NUM_SAMPLES = 10;
float R = 3.0f;
const float[10] steps = float[](0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f);
for (int sample_id = 0; sample_id < NUM_SAMPLES; sample_id++) {
// 1. Generate sample point in world space.
float ang = steps[sample_id];
vec4 sample_point = vec4(R * cos(2 * M_PI * ang) * sin(M_PI * ang) + fPosition.x,
R * sin(2 * M_PI * ang) * sin(M_PI * ang) + fPosition.y,
R * sin(M_PI * ang) + fPosition.z,
1.0f);
// 2. Transform sample point from view space to screen space to get its depth value.
sample_point = projection * camera_view * sample_point; // Clip space
sample_point = sample_point / sample_point.w; // Perspective division - Normalized device coordinate
float sample_depth = 0.5f * (sample_point.z + 1.0f); // Viewport transform for z - window space
// 3. Check whether sample_point is behind current pixel depth.
if (sample_depth > z) { occluding_points++; }
}
occlusion_factor = occluding_points / float(NUM_SAMPLES);
// Diffuse, specular components removed
total_light += vec3(ambient_intensity) * (1.0f - occlusion_factor); // Ambient factor
outColor = total_light;
#endif
Below is a screenshot how it looks. For some reason the artifacts only appear when looking down the z-axis so there can be something fishy with the transformations, although the work fine when rendering objects and the camera and so on.
When looking at basically any other angle it looks like you would expect from setting the occlusion factor to 0.5 (which will get you grey in all the color channels).
Result after accidental integer division fixed to floating point number division.
Added a vidoe here of the flickering.
Any clues?
EDIT: One problem was detected with rounding. EDIT: Added a video link to the artifact when moving along the z-axis.
There are two fishy things with your code.
occlusion_factor = occluding_points / NUM_SAMPLES;
Just change type of occluding_points to float and you should be fine.
vec4 sample_point = vec4(R * cos(2 * M_PI * ang) * sin(M_PI * ang) + fPosition.x,
R * sin(2 * M_PI * ang) * sin(M_PI * ang) + fPosition.y,
R * sin(M_PI * ang) + fPosition.z,
1.0f);
This gives you samples from the same spiral in world coordinates each time, so with right surface you'll get artifacts depending on the viewing angle. That's what I think it's happening when you are looking down the z-axis, when paired with rounding error above.