Algorithm
Prior to actually
rendering
a visible set,
shadow maps are generated
for all
shadow-projecting lights in
the set. A
shadow map for variance shadow
mapping, for a light
k, is a two-component
red/green image of all of the
shadow casters
associated with
k in the visible set.
The image is produced by rendering the instances from the point of view of
k. The red
channel of each pixel in the image represents the
logarithmic depth
of the closest surface at that pixel, and the green channel represents
the depth squared (literally
depth * depth).
For example:
Then, when actually applying lighting during rendering of the
scene, a given
eye space
position
p is transformed to
light-clip space
and then mapped to the range
[(0, 0, 0), (1, 1, 1)] in order
to sample the
depth and
depth squared values
(d, ds)
from the shadow map (as with sampling from a projected texture
with projective lighting).
As stated previously, the intent of variance
shadow mapping is to essentially calculate the
probability that a given point is in shadow,
rather than the binary
is/is not of
basic shadow mapping.
A
one-tailed variant of
Chebyshev's inequality
is used to calculate the upper bound
u
on the probability that, given
(d, ds), a given point with depth
t is in shadow:
One of the improvements suggested to the original variance shadow
algorithm is to clamp the minimum variance to some small value
(the io7m-r1 package uses 0.00002
by default, but this is configurable on a per-shadow basis). The
equation above becomes:
The above is sufficient to give shadows that are roughly equivalent
in visual quality to
basic shadow mapping
with the added benefit of being generally better behaved and with
far fewer artifacts. However, the algorithm can suffer from
light bleeding, where the penumbrae
of overlapping shadows can be unexpectedly bright despite the fact
that the entire area should be in shadow. One of the suggested
improvements to reduce light bleeding is to modify the upper bound
u such that all values below
a configurable threshold are mapped to zero, and values above the
threshold are rescaled to map them to the range
[0, 1]. The original article
suggests a linear step function applied to
u:
The amount of light bleed reduction is adjustable on a per-shadow
basis.
To reduce problems involving numeric inaccuracy, the
original article suggests the use of 32-bit floating point textures
in depth variance maps. The io7m-r1
package allows 16-bit or 32-bit textures, configurable on a per-shadow basis.
Finally, as mentioned previously, the io7m-r1
package allows both optional box blurring and mipmap generation for shadow
maps. Both blurring and mipmapping can reduce aliasing artifacts,
with the former also allowing the edges of shadows to be significantly
softened as a visual effect: