Graphics Programming Virtual Meetup


Discord

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.
- Emit rays outwards from it in UV space

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