Tutorial link:
https://github.com/ssloy/tinyrenderer/wiki/Lesson-8-Ambient-occlusion
My Code:
https://github.com/cdgiessen/TinyRenderer
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.
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:
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
SSAO output
For fun: Inverse of SSAO