Graphics Programming Virtual Meetup

Discord

Twitter

Tiny Renderer

Lesson 8

Ambient Occlusion

Tutorial link:
https://github.com/ssloy/tinyrenderer/wiki/Lesson-8-Ambient-occlusion


My Code:
https://github.com/cdgiessen/TinyRenderer

Ambient Lighting

 

Lighting which comes from 'everywhere' instead of a single, direct, light source

Classic phong lighting uses a 'constant' to approximate this ambient lighting

Problem: Ambient light isn't constant!

It various across a surface, based on how exposed or closed off a surface is.

The bottom of a trench receives far less light than the top of a tower.

Brute Force Strategy

Assume an object is being lit by a hemisphere (like the sky)

Pick 1000 random points on the hemisphere, and render a shadow map from that point.

Accumulate all the points into a secondary texture.
Then sample that when drawing the final image

Points on a hemisphere

One points 'ambiance' stored in UV map

Accumulate all points to get ambient map

Now we have a non constant 'ambient' term to use with our rendering

Limitations:

  • SLOW
    • 1000 shadow maps generated
  • Static hemisphere based
  • UV space isn't 1:1, some surfaces are mirrored

Screen Space Ambient Occlusion (SSAO)

Use the depth buffer to determine whether a pixel has 'local' occlusion instead of pre-computing it

First need to create the depth buffer

struct ZShader : public IShader {
    mat<4,3> varying_tri;

    virtual Vec4f vertex(int iface, int nthvert) {
        vec4f gl_Vertex = 
           Projection*ModelView*embed<4>(model.vert(iface, nthvert));
        varying_tri.set_col(nthvert, gl_Vertex);
        return gl_Vertex;
    }

    virtual bool fragment(vec3f gl_FragCoord, vec3f bar, TGAColor &color)
    {
        color = TGAColor(0, 0, 0);
        return false;
    }
};
ZShader zshader;
for (int i=0; i<model->nfaces(); i++) {
    for (int j=0; j<3; j++) {
        zshader.vertex(i, j);
    }
    triangle(zshader.varying_tri, zshader, frame, zbuffer);
}

for (int x=0; x<width; x++) {
    for (int y=0; y<height; y++) {
        if (zbuffer[x+y*width] < -1e5) continue;
        float total = 0;
        for (float a=0; a<M_PI*2-1e-4; a += M_PI/4) {
            total += M_PI/2 - max_elevation_angle(zbuffer, Vec2f(x, y), Vec2f(cos(a), sin(a)));
        }
        total /= (M_PI/2)*8;
        total = pow(total, 100.f);
        frame.set(x, y, TGAColor(total*255, total*255, total*255));
    }
}

"Screen Space" ambient occlusion shader

Algorithm for SSAO

  • For each pixel:
    • Emit rays outwards from it in UV space
      • Up/Down/Left/Right
    • For each pixel we see along a short path
      • Record the 'maximum slope' encountered
    • If the max slope is 0, area is in the open
    • Else if the slope is 90, the pixel is likely occluded
    • The ambient value is inversely proportional to how 'hilly' the local region is.
       

SSAO output

For fun: Inverse of SSAO

Ideas for things to implement

  • Hardware Acceleration
    • Parallelization on CPU (multithreading)
    • SIMD/Intrinsics/ispc
    • GPU acceleration with CUDA or SYCL
  • Lighting
    • Cubemaps/Skybox
    • Point shadows
    • Emissive textures
  • Particles
  • Post processing (bloom/color palletes)
  • Recreate in OpenGL to see how fast GPU's are
  • Recreate Quake :D

Graphics Programming Virtual Meetup

Tiny Renderer Lesson 8

By Charles Giessen

Tiny Renderer Lesson 8

Ambient Occlusion

  • 133