Anti-aliasing shadow edges with PCF

One of the simplest and most common techniques for dealing with the aliasing of shadow edges is called percentage-closer filtering (PCF). The name comes from the concept of sampling the area around the fragment and determining the percentage of the area that is closer to the light source (in shadow). The percentage is then used to scale the amount of (diffuse and specular) shading that the fragment receives. The overall effect is a blurring of the shadow's edges.

The basic technique was first published by Reeves et al in a 1987 paper (SIGGRAPH Proceedings, Volume 21, Number 4, July 1987). The concept involved transforming the fragment's extents into shadow space, sampling several locations within that region, and computing the percent that is closer than the depth of the fragment. The result is then used to attenuate the shading. If the size of this filter region is increased, it can have the effect of blurring the shadow's edges.

A common variant of the PCF algorithm involves just sampling a constant number of nearby texels within the shadow map. The percent of those texels that are closer to the light is used to attenuate the shading. This has the effect of blurring the shadow's edges. While the result may not be physically accurate, the result is not objectionable to the eye.

The following figures show shadows rendered with PCF (right) and without PCF (left). Note that the shadows in the right-hand image have fuzzier edges and the aliasing is less visible.

Anti-aliasing shadow edges with PCF

In this recipe, we'll use the latter technique, and sample a constant number of texels around the fragment's position in the shadow map. We'll calculate an average of the resulting comparisons and use that result to scale the diffuse and specular components.

We'll make use of OpenGL's built-in support for PCF, by using linear filtering on the depth texture. When linear filtering is used with this kind of texture, the hardware can automatically sample four nearby texels (execute four depth comparisons) and average the results (the details of this are implementation dependent). Therefore, when linear filtering is enabled, the result of the textureProj function can be somewhere between 0.0 and 1.0.

We'll also make use of the built-in functions for texture accesses with offsets. OpenGL provides the texture access function textureProjOffset, which has a third parameter (the offset) that is added to the texel coordinates before the lookup/comparison.

Getting ready

Start with the shaders and FBO presented in the previous recipe, Rendering shadows with shadow maps. We'll just make a few minor changes to the code presented there.

How to do it...

To add the PCF technique to the shadow mapping algorithm, use the following steps:

  1. When setting up the FBO for the shadow map, make sure to use linear filtering on the depth texture. Replace the corresponding lines with the following code:
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, 
                    GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,     
                    GL_LINEAR);
  2. Use the following code for the shadeWithShadow function within the fragment shader:
    subroutine (RenderPassType)
    void shadeWithShadow()
    {
      vec3 ambient = vec3(0.2);
      vec3 diffSpec = diffAndSpec();
    
      // The sum of the comparisons with nearby texels
      float sum = 0;
    
      // Sum contributions from texels around ShadowCoord
      sum += textureProjOffset(ShadowMap, ShadowCoord, 
                               ivec2(-1,-1));
      sum += textureProjOffset(ShadowMap, ShadowCoord, 
                               ivec2(-1,1));
      sum += textureProjOffset(ShadowMap, ShadowCoord, 
                               ivec2(1,1));
      sum += textureProjOffset(ShadowMap, ShadowCoord, 
                               ivec2(1,-1));
      float shadow = sum * 0.25;
    
    
      FragColor = vec4(ambient + diffSpec * shadow,1.0);
    }

How it works...

The first step enables linear filtering on the shadow map texture. When this is enabled, the OpenGL driver can repeat the depth comparison on the four nearby texels within the texture. The results of the four comparisons will be averaged and returned.

Within the fragment shader, we use the textureProjOffset function to sample the four texels (diagonally) surrounding the texel nearest to ShadowCoord. The third argument is the offset. It is added to the texel's coordinates (not the texture coordinates) before the lookup takes place.

As linear filtering is enabled, each lookup will sample an additional four texels, for a total of 16 texels. The results are then averaged together and stored within the variable shadow.

As before, the value of shadow is used to attenuate the diffuse and specular components of the lighting model.

There's more...

An excellent survey of the PCF technique was written by Fabio Pellacini of Pixar, and can be found in Chapter 11, Shadow Map Anti-aliasing of GPU Gems, edited by Randima Fernando, Addison-Wesley Professional, 2004. If more details are desired, I highly recommend reading this short, but informative, chapter.

Because of its simplicity and efficiency, the PCF technique is an extremely common method for anti-aliasing the edges of shadows produced by shadow mapping. Since it has the effect of blurring the edges, it can also be used to simulate soft shadows. However, the number of samples must be increased with the size of the blurred edge (the penumbra) to avoid certain artifacts. This can, of course, be a computational roadblock. In the next recipe, we'll look at a technique for producing soft shadows by randomly sampling a larger region.

Note

The penumbra is the region of a shadow where only a portion of the light source is obscured.

See also

  • The Rendering shadows with shadow maps recipe
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.88.249