**DirectX Raytracing, Tutorial 8** **Depth-of-field using a simple thin-lens camera** In [Tutorial 7](../Tutor7/tutorial07.md.html), we added random jitter to the camera position to allow antialiasing by accumulating samples temporally over multiple frames. This tutorial modifies our *`RayTracedGBufferPass`* from [Tutorial 4](../Tutor4/tutorial04.md.html) to use a simple [thin lens camera model](https://en.wikipedia.org/wiki/Thin_lens). Each pixel randomly selects the camera origin somewhere on the lens of this camera. Temporally accumulating these random camera origins over multiple frames allows us to model dynamic [depth of field](https://en.wikipedia.org/wiki/Depth_of_field). Our Antialised Rendering Pipeline ================================================================================= If you open up *`Tutor08-ThinLensCamera.cpp`*, you will find our new pipeline combines a new *`ThinLensGBufferPass`*, the *`AmbientOcclusionPass`* from [Tutorial 5](../Tutor5/tutorial05.md.html), and the *`SimpleAccumulationPass`* from [Tutorial 6](../Tutor6/tutorial06.md.html). ~~~~~~~~~~~~~~~~~~~~ C // Create our rendering pipeline RenderingPipeline *pipeline = new RenderingPipeline(); pipeline->setPass(0, ThinLensGBufferPass::create()); pipeline->setPass(1, AmbientOcclusionPass::create()); pipeline->setPass(2, SimpleAccumulationPass::create(ResourceManager::kOutputChannel)); ~~~~~~~~~~~~~~~~~~~~ This *`ThinLensGBufferPass`* builds on the *`RayTracedGBufferPass`* from [Tutorial 4](../Tutor4/tutorial04.md.html) but adds our new per-pixel random camera origin. Essentially, camera jitter from [Tutorial 7](../Tutor7/tutorial07.md.html) perturbs the camera ray *_direction_*; a thin lens model also perturbs the camera *_origin_*. In order to combine antialiasing, our thin lens camera model, and to allow the user to toggle them both on and off, the *`ThinLensGBufferPass`* also reuses the random jittering code from [Tutorial 7](../Tutor7/tutorial07.md.html). Setting up Our Thin Lens ================================================================================= Continue by looking in *`ThinLensGBufferPass.h`*. The key changes are the introduction of a number of variables related to lens parameters: ~~~~~~~~~~~~~~~~~~~~C bool mUseThinLens = false; // Currently using thin lens? (Or pinhole?) float mFNumber = 32.0f; // The f-number of our thin lens float mFocalLength = 1.0f; // The distance to our focal plane float mLensRadius; // The camera aperature. (Computed) ~~~~~~~~~~~~~~~~~~~~ *`mUseThinLens`* is a user-controllable variable in the GUI that allows toggling camera jitter. *`mFNumber`* and *`mFocalLength`* are the user-controllable parameters for the thin lens. *`mFNumber`* controls the virtual [f-number](https://en.wikipedia.org/wiki/F-number). *`mFocalLength`* controls our camera's [focal length](https://en.wikipedia.org/wiki/Focal_length), which is the distance from the camera where all rays contributing to a pixel converge. Since we define default values for all our thin lens parameters in the header file, there are no camera-specific additions to our *`ThinLensGBufferPass::initialize()`* method, so we move on to the changes required in *`ThinLensGBufferPass::execute()`* ~~~~~~~~~~~~~~~~~~~~ C void ThinLensGBufferPass::execute(RenderContext::SharedPtr pRenderContext) { ... // Compute lens radius based on our user-exposed controls mLensRadius = mFocalLength / (2.0f * mFNumber); // Specify our HLSL variables for our thin lens auto rayGenVars = mpRays->getRayGenVars(); rayGenVars["RayGenCB"]["gLensRadius"] = mUseThinLens ? mLensRadius : 0.0f; rayGenVars["RayGenCB"]["gFocalLen"] = mFocalLength; // Compute our camera jitter float xJitter = mUseJitter ? mRngDist(mRng) : 0.0f; float yJitter = mUseJitter ? mRngDist(mRng) : 0.0f; rayGenVars["RayGenCB"]["gPixelJitter"] = vec2(xOff, yOff); ... } ~~~~~~~~~~~~~~~~~~~~ The first addition computes *`mLensRadius`* based on our user-specified [focal length and f-number](https://en.wikipedia.org/wiki/F-number). We then pass down our thin lens parameters to our DirectX ray generation shader, where we'll use it to determine what rays to shoot from our camera. Note: A thin lens camera model degenerates to a pinhole camera if the lens radius is set to zero, so if the user chooses a pinhole camera the logic need not change. We also pass down a random camera jitter to antialias geometry that is focus. This is slightly different that when rasterizing in [Tutorial 7](../Tutor7/tutorial07.md.html), where Falcor utilities handled everything to jitter the camera. Here we pass the pixel jitter down to our ray generation shader, where we'll take also this jitter into account when tracing our rays. DirectX Ray Generation for Jittered, Thin-Lens Camera Rays =============================================================================== The final step in this tutorial is updating our G-buffer's ray generation to perturb our camera origin and ray directions: ~~~~~~~~~~~~~~~~~~~~C [shader("raygeneration")] void GBufferRayGen() { // Get our pixel's position on the screen uint2 rayIdx = DispatchRaysIndex(); uint2 rayDim = DispatchRaysDimensions(); // Convert our ray index into a jittered ray direction. float2 pixelCenter = (rayIdx + gPixelJitter) / rayDim; float2 ndc = float2(2, -2) * pixelCenter + float2(-1, 1); float3 rayDir = ndc.x * gCamera.cameraU + ndc.y * gCamera.cameraV + gCamera.cameraW; ... ~~~~~~~~~~~~~~~~~~~~ To start off, it looks very similar to our previous ray traced G-buffer. However, instead of using a fixed *`0.5f`* offset to shoot our ray through the center of each pixel, we'll use our computed camera jitter *`gPixelJitter`* as our sub-pixel offset. This gives us antialiasing as in [Tutorial 7](../Tutor7/tutorial07.md.html). ~~~~~~~~~~~~~~~~~~~~C ... // Find the focal point for this pixel. rayDir /= length(gCamera.cameraW); float3 focalPoint = gCamera.posW + gFocalLen * rayDir; ... ~~~~~~~~~~~~~~~~~~~~ Next, we want to find the *_focal point_* for this pixel. All rays in the pixel pass through this focal point, so a ray from the camera center goes through it. Compute the right point by moving along this ray an appropriate distance. The division ensures this focal plane is planar (i.e., always the same distance from the camera along the viewing vector *`cameraW`*). ~~~~~~~~~~~~~~~~~~~~C ... // Initialize a random number generator uint randSeed = initRand(rayIdx.x + rayIdx.y * rayDim.x, gFrameCount); // Get point on lens (in polar coords then convert to Cartesian) float2 rnd = float2( nextRand(randSeed) * M_2PI, nextRand(randSeed) * gLensRadius ); float2 uv = float2( cos(rnd.x) * rnd.y, sin(rnd.x) * rnd.y ); ... ~~~~~~~~~~~~~~~~~~~~ Now we need to compute a random ray origin on our lens. To do this, we first initialize a random number generator and pick a random point on a canonical lens of the selected raidus (in polar coordinates), then convert this to a Cartesian location on the lens. ~~~~~~~~~~~~~~~~~~~~C ... // Use uv coordinate to compute a random origin on the camera lens float3 randomOrig = gCamera.posW + uv.x * normalize(gCamera.cameraU) + uv.y * normalize(gCamera.cameraV); // Initialize a random thin lens camera ray RayDesc ray; ray.Origin = randomOrig; ray.Direction = normalize(focalPoint - randomOrig); ray.TMin = 0.0f; ray.TMax = 1e+38f; ... } ~~~~~~~~~~~~~~~~~~~~ Finally, we compute the random thin lens camera ray to use. We convert our random sample into an actual world space position on the camera (using the origin and the camera's *`u`* and *`v`* vectors). Once we have the random camera origin, we can compute our random ray direction by shooting from the random origin *_through_* this pixel's focal point (computed earlier). The rest of our G-buffer pass is identical to *`RayTracedGBufferPass`* from [Tutorial 4](../Tutor4/tutorial04.md.html). What Does it Look Like? =============================================================================== That covers the important points of this tutorial. When running, you get the following result: ![Result of running Tutorial 8, after loading the scene "pink_room.fscene"](Tutor08-Output.png) Hopefully, this tutorial demonstrated how to add add a simple thin lens model by randomly selecting ray origin and direction based on standard thin lens camera parameters. When you are ready, continue on to [Tutorial 9](../Tutor9/tutorial09.md.html), which swaps out our simplistic ambient occlusion shading for a slightly more complex [Lambertian](https://en.wikipedia.org/wiki/Lambertian_reflectance) material model using ray traced shadows.