Lately I’ve been really disappointed by the poor performances of my first depth of field implementation, thus I decided to do something about it…
The most natural step to do was to give a look to the second Direct3D example from the same paper I used for the first one, as I was sure it would have led to more satisfactory results.
I spent the two last nights converting, correcting and fine tuning it, but I was rewarded by the fact that I was right: even if it is a five passes algorithm which is using four different Frame Buffer Objects, it is about 2.5 times faster than my previous implementation!
I think the speed boost depends on the two following:
- image blurring is achieved by a gaussian filter which is calculated separating the X from the Y axis, it is an approximation of a standard 2D kernel but it also means that the convolution matrix calculation complexity decreases from a quadratic to a linear factor.
- this filter operates only on a downsampled (1/4th of the screen resolution actually) FBO
Another nice note about this new implementation is that there are only two focal parameters, focus depth and focus range, which really help to setup a correct scene.
Now let’s review the five passes in detail:
- Render the scene normally while calculating a blur amount per-vertex, then store the interpolated value per-pixel inside the alpha component of the fragment.
The calculation at the vertex shader is just:
Blur = clamp(abs(-PosWV.z - focalDistance) / focalRange, 0.0, 1.0);
- Downsample the scene rendered at the previous pass storing it in a smaller FBO
- Apply the gaussian filter along the X axis on the downsampled scene and store it in a new FBO
- Apply the gaussian filter along the Y axis on the already X blurred scene and store it in a new FBO
- Calculate a linear interpolation between the first full resolution FBO and the XY downsampled blurred one
This is performed in the fragment shader as:
gl_FragColor = Fullres + Fullres.a * (Blurred - Fullres);