**DirectX Raytracing, Tutorial 5**
**Ray traced ambient occlusion using a hybrid raster / ray tracing renderer**
In [Tutorial 4](../Tutor4/tutorial04.md.html), we walked through our first example of spawning
rays in DirectX and the new HLSL shader types needed to control them. This tutorial combines
the rasterized [G-Buffer](https://en.wikipedia.org/wiki/Deferred_shading) we created in
[Tutorial 3](../Tutor3/tutorial03.md.html) and then launches [ambient occlusion](https://en.wikipedia.org/wiki/Ambient_occlusion)
rays from the locations of pixels in that G-buffer.
Our Basic Ambient Occlusion Pipeline
=================================================================================
If you open up *`Tutor05-AmbientOcclusion.cpp`*, you will find our new pipeline combines the
*`SimpleGBufferPass`* from [Tutorial 3](../Tutor3/tutorial03.md.html) with the new
*`AmbientOcclusionPass`* we'll build below.
~~~~~~~~~~~~~~~~~~~~ C
// Create our rendering pipeline
RenderingPipeline *pipeline = new RenderingPipeline();
pipeline->setPass(0, SimpleGBufferPass::create());
pipeline->setPass(1, AmbientOcclusionPass::create());
~~~~~~~~~~~~~~~~~~~~
This pipeline is a hybrid rasterization and ray tracing pipeline, as it uses a standard rasterization pass
to defer rendering, saving our primary hit points in a G-buffer and shading them in a later pass. In this
example, our later pass will shade these rasterized points using ray traced ambient occlusion.
Launching DirectX Ambient Occlusion Rays From a G-Buffer
=================================================================================
Continue by looking in *`AmbientOcclusionPass.h`*. This should look familiar, as the boilerplate is
nearly identical to that from *`RayTracedGBufferPass`* in [Tutorial 4](../Tutor4/tutorial04.md.html).
The major difference is our member variables to control the ambient occlusion:
~~~~~~~~~~~~~~~~~~~~ C
float mAORadius = 0.0f; // Our ambient occlusion radius
uint32_t mFrameCount = 0; // Used for unique random seeds each frame
~~~~~~~~~~~~~~~~~~~~
Ambient occlusion gives an approximation of ambient lighting and shadowing due to nearby geometry
at each pixel. Usually, ambient occlusion is limited to nearby geometry by having a radius that
defines "nearby geometry." In our rendering, this is controlled by *`mAORadius`*.
Ambient occlusion is a [stochastic effect](https://en.wikipedia.org/wiki/Stochastic), so we'll need
a random number generator to select our rays. Our shader uses a
[pseudo random number generator](https://en.wikipedia.org/wiki/Pseudorandom_number_generator)
to generate random samples, and to avoid generating the same rays every frame we need to update our
random seeds each frame. We do this by hashing the pixel location and a frame count to initialize
our random generator.
Initializing our Ambient Occlusion Pass
------------------------------
Our *`AmbientOcclusionPass::initialize()`* is fairly straightforward:
~~~~~~~~~~~~~~~~~~~~ C
bool AmbientOcclusionPass::initialize(RenderContext::SharedPtr pRenderContext,
ResourceManager::SharedPtr pResManager)
{
// Stash a copy of our resource manager; request needed buffer resources
mpResManager = pResManager;
mpResManager->requestTextureResources( {"WorldPosition", "WorldNormal",
ResourceManager::kOutputChannel });
// Create our wrapper for our DirectX Raytracing launch.
mpRays = RayLaunch::create("aoTracing.hlsl", "AoRayGen");
mpRays->addMissShader("aoTracing.hlsl", "AoMiss");
mpRays->addHitShader("aoTracing.hlsl", "AoClosestHit", "AoAnyHit");
// Compile our shaders and pass in our scene
mpRays->compileRayProgram();
mpRays->setScene(mpScene);
return true;
}
~~~~~~~~~~~~~~~~~~~~
We first request our rendering resources. We need our G-buffer fields *`"WorldPosition"`* and *`"WorldNormal"`*
as inputs and we're outputting a displayable color to *`kOutputChannel`*.
Then we create a ray tracing wrapper *`mpRays`* that starts tracing rays at in the ray generation shader *`AoRayGen()`*
in the file *`aoTracing.hlsl`* and has one ray type with shaders *`AoMiss`*, *`AoAnyHit`*, and *`AoClosestHit`*.
Launching Ambient Occlusion Rays
------------------------------
Now that we initialized our rendering resources, we can shoot our ambient occlusion rays. Let's walk
through the *`AmbientOcclusionPass::execute`* pass below:
~~~~~~~~~~~~~~~~~~~~ C
void AmbientOcclusionPass::execute(RenderContext::SharedPtr pRenderContext)
{
// Get our output buffer; clear it to black.
auto dstTex = mpResManager->getClearedTexture(ResourceManager::kOutputChannel,
vec4(0.0f, 0.0f, 0.0f, 0.0f));
// Set our shader variables for our ambient occlusion shader
auto rayGenVars = mpRays->getRayGenVars();
rayGenVars["RayGenCB"]["gFrameCount"] = mFrameCount++;
rayGenVars["RayGenCB"]["gAORadius"] = mAORadius;
rayGenVars["RayGenCB"]["gMinT"] = mpResManager->getMinTDist();
rayGenVars["gPos"] = mpResManager->getTexture("WorldPosition");
rayGenVars["gNorm"] = mpResManager->getTexture("WorldNormal");
rayGenVars["gOutput"] = dstTex;
// Shoot our AO rays
mpRays->execute( pRenderContext, mpResManager->getScreenSize() );
}
~~~~~~~~~~~~~~~~~~~~
First, we grab our output buffer and clear it to black.
Then we pass our G-buffer inputs, output texture, and user-controllable variables down
to the ray generation shader. Unlike [Tutorial 4](../Tutor4/tutorial04.md.html),
we do not send variables to either the miss shader or closest hit shader. Instead, our
variables will only be visibile in the ray generation shader.
We pass down our *`mFrameCount`* to seed our per-pixel random number generator, our
user-controllable *`mAORadius`* to change the amount of occlusion seen, and a minimum _t_
value to use when shooting our rays. This *`gMinT`* variable will control how much bias
to add at the beginning of our rays to avoid self-intersection on surfaces.
We also pass down our input textures *`gPos`* and *`gNorm`* from our prior G-buffer pass
and our default pipeline output to *`gOutput`*.
DirectX Raytracing for Ambient Occlusion
==============================================================================
Let's start by reading our ambient occlusion shader's ray generation shader *`AoRayGen()`*:
~~~~~~~~~~~~~~~~~~~~~C
cbuffer RayGenCB {
float gAORadius, gMinT;
uint gFrameCount;
}
Texture2D