0
\$\begingroup\$

I want to integrate Ambient Occlusion into my rendering which is a forward renderer.
How do I apply Ambient Occlusion to the ambient term only and thus not as a "post process" over the entire image (i.e. frambuffercolor * ambient occlusion)?

\$\endgroup\$
4
  • \$\begingroup\$ It's difficult to imagine an answer to this question that doesn't sound tautological — "you multiply your ambient light colour by the ambient occlusion factor before you add the direct lighting term(s)". How have you tried to implement this so far, and where did you run into trouble? \$\endgroup\$ – DMGregory Jan 14 at 12:38
  • \$\begingroup\$ When I am in forward rendering, I render the lighting of the current fragment. How would I compute and access Ambient Occlusion? \$\endgroup\$ – Raildex Jan 14 at 14:19
  • \$\begingroup\$ Are you talking about screenspace ambient occlusion (SSAO)? \$\endgroup\$ – DMGregory Jan 14 at 14:21
  • \$\begingroup\$ Yes, I want to do Screen Space AO. \$\endgroup\$ – Raildex Jan 14 at 14:59
1
\$\begingroup\$

You can do this with a technique called "depth pre-pass"

First you render the opaque geometry in your scene, drawing only the depth. No colour, texture, lighting.

Now you can run your ambient occlusion pass over this scene depth information, creating a texture containing your occlusion factor. You can run additional filter passes on this to smooth/de-noise it.

Now you render your scene again, with colour output as usual, doing all your material and lighting calculations. When it comes time to apply the ambient light, you sample the ambient occlusion texture you prepared earlier in the frame, use it to modulate the ambient term.

eg.

float ambientAmount = tex2D(ambientOcclusionTexture, screenSpaceUV);

float4 diffuse = ambientColour * ambientAmount;

// TODO: add contribution from each light 
// into the diffuse lighting and specular terms.

float4 outputColour = albedo * diffuse + specular;

This might sound like a lot of extra work, but depth pre-pass is often applied as an optimization in forward renderers, as a way to get some of the benefits of deferred rendering without a full G-Buffer.

Because your first pass uses only depth, you can do it very quickly, in large batches with no material changes. Your later passes then benefit from early depth rejection, throwing out occluded fragments without doing expensive material and lighting calculations on them.

This helps especially if you have scenes with lots of overlapping/occluded geometry, expensive material effects, or lots of lights - using the cheap early depth pass to skip the costlier later passes on any occluded pixels.

\$\endgroup\$
3
  • \$\begingroup\$ Does that mean i also need to render the normals if i want to take them into account for AO? \$\endgroup\$ – Raildex Jan 14 at 16:09
  • 1
    \$\begingroup\$ If you don't infer them from depth, then yes. But if you're outputting both normals and depth, you're starting to get close to deferred rendering, and it might be worth making the jump all the way. \$\endgroup\$ – DMGregory Jan 14 at 16:11
  • \$\begingroup\$ that's what i am afraid of \$\endgroup\$ – Raildex Jan 14 at 16:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.