Screen Space Ambient Occlusion

Screen Space Ambient Occlusion

Introduced by (Shanmugam and Arikan 2007)

Idea #

Generate 3d sample points inside some radius around rendered fragments and check how many of them lie infront or behing the depth image, by projecting them to the image plane. The percentage of samples behind the depth image is the ambient occlusion.

The hemisphere radius is constant in world space, so projected size is depth dependent.

Each sample is treated equally, Lambert’s cosine law is not repsected (surfaces at a high angle from the normal should not occlude the point screen_space_ambient_occlusion_1e07f5ea8054cbe9e33803f209e513c65026fcf2.svg the same as surfaces at 0 degrees). This changes the normalization factor to screen_space_ambient_occlusion_91a44b50e22e6e4216e2b07c7ba47453dc5b52e4.svg for one hemisphere (half of the surface area of a sphere), rather than screen_space_ambient_occlusion_c22e7c4fca520e25a9fa7012918ca2e096f3a851.svg if Lambert’s cosine law was respected, see Ambient Occlusion .

History #

Developed at Crytec in 2007, appeard first in Crysis (Mittring 2007)

Normal-Free SSAO #

f32 calc_ao (u32 x, u32 y, f32* depth_image, u32 num_samples) {
    f32 ka = 1;

    vec3 p_v = reconstruct_coords(x, y, depth_image[x, y]);
    f32 r = get_sapling_radius_for_depth(depth_image[x, y]);
    Array_List<vec3> samples = generate_samples(num_samples, p_v, r);

    for (auto sample : samples) {
        vec2 p_s = project_to_image_plane(sample);
        if (depth_image[p_s] < vec3_len(sample)) {
            ka -= 1.0 / num_samples;
        }
    }

    return ka;
}

Possible improvements #

Distance based weighting
sample points closer to the original point are weighted more

SSAO with normals #

Only consider samples infront of the tangent plane of the to-be-shaded fragment.

f32 calc_ao (u32 x, u32 y, f32* depth_image, vec3* normal_image, u32 num_samples) {
    f32 ka = 0;
    u32 counter = 0;

    vec3 p_v = reconstruct_coords(x, y, depth_image[x, y]);
    f32 r = get_sapling_radius_for_depth(depth_image[x, y]);
    Array_List<vec3> samples = generate_samples(num_samples, p_v, r);

    for (auto sample : samples) {
        vec2 p_s = project_to_image_plane(sample);

        if (!is_over_tangent_plane(normal_image[ps], p_c)) {
            continue;
        }

        counter++;

        if (depth_image[p_s] < vec3_len(sample)) {
            ka += 1;
        }
    }

    return ka / counter;
}

Checking if a point screen_space_ambient_occlusion_7d97ab9586715a7f6f3de4b4d47d5be75a6f0039.svg is over a plane screen_space_ambient_occlusion_45093609f75ed8c0a71eb030e7b9d21f86e5c815.svg: screen_space_ambient_occlusion_c8c31f9de9f1d8118bc1b66d3f89da093261d45e.svg

Calendar October 22, 2023