Depth of Field – Simplified

March 11th, 2008 by ReAn

What is depth of field? Depth of field is the simulation in 3D graphics of a lens-based camera. Conventional rendering techniques model a pinhole camera paradigm, this means that all aspects of the picture share the same level of focus (the entire picture is in focus). With a lens-based camera, the rendered image will have varying levels of focus based on the ‘focus’ of the lens. Since we’re not using a ray-tracing engine, it’s hard to approximate the effects of light bending through a lens in the projection phase of rendering so we can ‘fake’ Depth of Field by breaking the problem down.

The approach

To simulate depth of field we will need to employ two advanced rendering techniques, the first is Vertex/Pixel Shaders, and the second is Multi-Pass Rendering. Vertex/Pixel (or Fragment) Shaders are small programs that run directly on the gpu over a specific set of data. A vertex shader is a program that’s run for every vertex it’s applied to. A Pixel (Fragment) shader is a program that runs for every ‘fragment’ which is a visual body of pixels drawn to the buffer, this is usually interpolated accross every pixel. Multi-Pass Rendering is a technique in which we render the same scene multiple times per frame, with different effects and/or purposes and we merge the outputs in some fassion to provide the desired result.

First Pass

Firstly we need to render our scene, but this will be in pin-hole format, or completely in focus. We apply a Vertex/Fragment shader to all the objects in the scene, the purpose of this shader is to calculate the distance from the camera of each pixel and store the blurriness coefficient in the alpha channel of the scene.

The shader will need various inputs from the program, such as the camera location, the visual depth of the focus plane, the visual depth of the near focal plane, and the far focal plane. This will allow us to set up a gradient of blurriness between the focal plane and the far focal plane, likewise for the near focus plane.


let fp = focal plane
let ffp = far focal plane
let nfp = near focal plane
let d = distance from camera
let b = blurriness

if (d > ffp OR d < nfp) { b = 1.0; }
elseif (d > fp AND d < ffp)
{
b = (d - fp) / (ffp - fp);
}
elseif (d < fp AND d > nfp)
{
b = 1 - ( (d - nfp) / (fp - nfp) );
}
else
{
b = 0;
}

Render Pass 1 Render Pass 1 Alpha

After the pass is rendered, the framebuffer is copied into a texture while maintaining the alpha channel for use in pass 2.

Second Pass

The second pass we will render a single quad to the screen and apply the texture generated by the first pass with a second Vertex/Fragment Shader. We will only need to pass the texture and the max blurriness to this shader, the rest of the information is stored inside the texture.

For each pixel we construct a unit circle (1.0 pixels big) with a possion distrobution of a number of points. If you’re lazy you can use a fast-blur technique instead of gaussian by setting your unit points to:

(1, 0), (-1, 0), (0, 1), (0, -1), (0.4, 0.4), (-0.4, 0.4), (-0.4, -0.4), and (0.4, -0.4), this will give a good average 8-point sample.

We then take the texture coordinates of the pixel we’re looking at and for each sample in our list (possion or lazy) add sample-point * (blurriness * max_blurriness) to the texture coordinates and sample the texture @ the resulting pixel. The samples are all averaged and a resulting color pixel is generated and output at the original location.

This is a simple blurring technique. Where blurriness is 0, the sample unit circle is effectively multiplied by 0 and will yield the source pixel. Where blurriness is 1.0, the unit circle is multiplied by max_blurriness, and will expand the sample points to sample nearby pixels.

We run into one problem however, at the edge of a sharp foreground object, we will get bleed from the sharp object into the blurred background/foreground. To compete this, we test the blurriness of each sample point, if the blurriness difference between the source point and the sample point is greater than a set threshold, we discard the sample and replace it with a sample from the point we’re inspecting.

2nd Pass:

And there you have it, a 2-Pass Depth of Field technique.

Posted in Computers, Graphics | | 1,509 Comments

Leave a reply