The SSAO is finally working! It needs tweaking and optimization, but the important thing is it works.
I use an interpolated view ray to reconstruct the view-space position of each pixel from the depth buffer. To get the view ray, I pass in the four corners of the frustum’s far plane and let the hardware interpolate the ray in the fragment shader. It turns out that in calculating the geometric representation of my frustum, I forgot to convert my field of view from degrees to radians. I spent hours tweaking and debugging the shader code. Sorry, I mean days. The screenshot on the right is the type of stuff I’ve been looking at for the last week.
The position buffer you see in the left screenshot is temporary. I used it to test if the reconstructed view-space position was accurate.
Next, I plan to optimize / tweak the SSAO, add a blur pass, and re-organize / pack the G-buffer.