EVEN MORE SHENANIGANS from the OPENGL Red Book. Depth of field is caused by off-angle light rays hitting the focal plane. This effect can be simulated in a rasterization architecture by jittering the camera on a plane perpendicular to the view vector, thereby simulating off-angle light rays. Note that the view frustum must be adjusted to keep the plane of perfect focus stable.
The exact pattern of jittering will affect the nature of the bokeh. The jitter distibution should approximate the shape of the camera aperture you are trying to simulate. Four candidate distributions are shown in Figure 2. The above applet uses the final implementation: jittered stratified pattern and weighted rejection sampling to emulate falloff on a disc shaped aperture.
|2c. Disc||2d. Weighted Disc|
|3c. Disc||3d. Weighted Disc|
Figure 3a shows obvious aliasing and banding effects. The sample jittering in Figure 3b significantly reduces the aliasing, but the box aperture shape is clearly distinguishable. In Figure 3c, the bokeh has a disc shape, which creates a more pleasing effect. Note that the image appears somewhat sharper and the aliasing effects are more pronounced than in the previous case. This is due to the removal of outlying samples, which reduces the aperture and increases the depth of field. Weighting the samples radially from the center as in Figure 3d creates an even sharper image since the remaining outliers are weighted even less. When sampled at a high enough frequency, this method creates the smoothest, highest quality blur.
This is a simple explanation and implementation of sampling and reconstruction. For a complete treatment, I highly suggest Physically Based Rendering by Pharr and Humphrey. The relevant chapter on sampling is available online (link on the home page), and I wholeheartedly recommend the book if you are interested in computer graphics & raytracing.
Source code for the applet is linked below, but none of the interesting stuff is there. This is mostly camera manipulation, and the code is in SGCamera, so I've attached it here. Jittering the camera position on the plane requires an orthonormal basis formed by the camera's up, left, and view vectors. Displacement is easy—scale the up and left vectors by the desired radius and add to the camera position. The view frustum is adjusted via the formulas provided in the Red Book documentation.
float top = near*PApplet.tan(fov/2.f);float bottom = -top;float left = aspect*bottom;float rt = aspect*top;float scx = (rt-left)/parent.width;float scy = (top-bottom)/parent.height;PVector displaceUp = up.get();displaceUp.mult(fy); PVector displaceRight = right.get();displaceRight.mult(fx);float fc = near/focus; parent.frustum(left + scx*dx - fx*fc,rt + scx*dx - fx*fc,bottom + scy*dy - fy*fc,top + scy*dy - fy*fc,near,far); //these vector functions are um… inlined?//we move the camera AND target location//do not rotate around the target, or you'll//get some weird kind of spherical plane of focusparent.camera(pos.x + displaceUp.x + displaceRight.x,pos.y + displaceUp.y + displaceRight.y,pos.z + displaceUp.z + displaceRight.z,target.x+ displaceUp.x + displaceRight.x,target.y + displaceUp.y + displaceRight.y,target.z+ displaceUp.z + displaceRight.z,up.x,up.y,up.z);