|
Features

Creating A Post-Processing Framework:
Tips & Tricks
Optimizations
Per-Vertex vs. Per-Pixel
For certain effects, per-pixel processing may be prohibitively
expensive, and when running on lower-end hardware the pixel shader
instruction sets may be too limited. In this case it is useful
to break the rendered target quad into multiple smaller ones, so
the heaviest computation can be moved either to the vertex shader
or the CPU. This allows the results to be interpolated between
the vertices, or the vertex coordinates to be modified for effects
such as image perturbation.

The image illustrates simple and highly tessellated target quads.
Using the Hardware Filtering
One way to make the most use of the hardware is to use the linear
and bilinear hardware filtering to your advantage. Typically especially
the lower end pixel shaders allow for a very few texture samples
to be taken per-pixel. The number of sampled texels can be increased
by enabling either linear or bilinear filtering of the source texture,
and taking the source samples between the texels. This way the
number of samples can be increased, for example, from 4 to up to
16 samples with very little additional cost.
This technique is illustrated in the following image:

The black dots represent the sampling locations,
and the surrounding circles represent the total area sampled.
Multi-Sampling
When multi-sampling is enabled, the automatically created back
and z-buffer are not the size requested by the user and have instead
been magnified by the hardware to support multi-sampling. However,
this doesn’t happen to the user created auxiliary buffer,
which creates a problem since the hardware requires the used render-target
and z-buffer to be of the same size. As a result the scene can’t
be rendered to the auxiliary buffer, and has to be rendered to
the back-buffer instead. The back-buffer has to be then copied
back into the auxiliary buffer for being used as a source texture
for the post-processing operation. This is an expensive operation
and if care is not taken the performance could easily be crippled
when multi-sampling is enabled. The goal should be to minimize
the number of these copies, which can be achieved by designing
the post-processing effects so that they can all be performed from
the same source buffer.
Using the Alpha Channel
In some cases, it is useful to use the target alpha channel for
storing a separate layer of information from the RGB channels.
For this, the scene could be rendered with only the desired attributes
by write masking it to the target buffer’s alpha.
The following HLSL command enables the alpha-only writing for
the destination buffer:
ColorWriteEnable = Alpha;
The alpha channel could then be extracted during the post-processing
pass as separate layer of information from the RGB channels, thus
packing an extra channel of information per-pixel.
Special Effects
Motion Blur
Post-processing motion blur can be achieved in a couple of different
ways. Two are examined here, with both having different pros and
cons.
One of the ways is to maintain a set of small auxiliary buffers
for storing the previously rendered frames, and then blend each
frame on top of the current one with decreasing weights.

The method is illustrated with additive blending mode,
with example blending weights shown inside of the auxiliary buffers.
This method has the disadvantage of a fairly limited blur effect
as it’s not practical to store a large number of previously
rendered frames. The previous frames are also typically stored
as ¼ or 1/16 sized buffers for memory considerations which
decreases their resolution. This is typically acceptable as the
blended frames are intended to look slightly blurred.
Another method is to store the current frame by blending it recursively
on top of the previous one, in which case no extra work buffers
are needed for the effect, and no resolution is lost for the previous
frames.

The image illustrates recursive blending,
with example blending weights shown inside of the buffers.
Note that in practice, the back-buffer doesn’t have to be
used as a source texture as the example image would indicate. Rather,
it’s more efficient to blend the auxiliary buffer directly
on top of the existing back-buffer by alpha blending by using the
following render states in the HLSL effect file.
AlphaBlendEnable = True;
SrcBlend = InvSrcAlpha;
DestBlend = SrcAlpha;
The pixel shader for the effect then becomes extremely simple.

The HLSL sample code contains a pixel shader for the blur effect.
This method has a particular disadvantage as well. Although it
is very easy to produce a heavy blur effect, large weights for
the previous frame can produce a ghost image that remains on the
screen without being blended out. This has to do with the precision
of the buffers, which are typically only 8-bits per channel.
Thinking in 8-bits, if the auxiliary buffer contains a value 0/255
for the currently rendered frame, and the back-buffer contains
a value 1/255 for the previous frame, the blending operation would
then result into: 0*0.2 + 1*0.8 = 0.8. The resulting value of 0.8
would then be rounded back to 1. This means that even if the current
frame is perfectly black, a ghost image from the previous frame
would remain on the screen.
|