Upcoming Events
VIEW Conference 2009
11/4 - 11/7 @ Turin, Italy

Project Horseshoe
11/5 - 11/8 @ Burnet, TX

Independent Game Conference West
11/5 - 11/6 @ Los Angeles, CA

IGDA Leadership Forum
11/12 - 11/13 @ San Francisco, CA

More events...


Quick Stats
6415 people currently visiting GDNet.
2337 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!



Link to us

Link to us

  search:   

Soft-Edged Shadows


Step 2: Rendering the shadowed scene into a buffer

Next, we need to render the shadowed portions of the scene to an offscreen buffer so that we can blur it and project it back onto the scene. To do that, we first render the shadowed portions of the scene into a screen-sized fixed point texture.

// Create the screen-sized buffer map
if( FAILED( g_pd3dDevice->CreateTexture( SCREEN_WIDTH,
            SCREEN_HEIGHT, 1, D3DUSAGE_RENDERTARGET,
            D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT,
            &g_pScreenMap, NULL ) ) )
{
   MessageBox( g_hWnd, "Unable to create screen map!",
               "Error", MB_OK | MB_ICONERROR );
   return E_FAIL;
}
// Grab the texture's surface
g_pScreenMap->GetSurfaceLevel( 0, & g_pScreenSurf );

To get the projective texture coordinates, we need a "texture" matrix that will map the position from projection space to texture space.

// Generate the texture matrix
float fTexOffs = 0.5 + (0.5 / (float)SHADOW_MAP_SIZE);
D3DXMATRIX matTexAdj( 0.5f,     0.0f,     0.0f, 0.0f,
                      0.0f,     -0.5f,    0.0f, 0.0f,
                      0.0f,     0.0f,     1.0f, 0.0f,
                      fTexOffs, fTexOffs, 0.0f, 1.0f );

matTexture = matLightViewProj * matTexAdj;

We get the shadow factor as usual by depth comparison, but instead of outputting the completely lit scene, we output only the shadow factor. Here are the vertex and pixel shaders that do the job.

// Shadow mapping vertex shader
struct VSOUTPUT_UNLIT
{
   float4 vPosition   : POSITION;
   float4 vTexCoord   : TEXCOORD0;
   float  fDepth      : TEXCOORD1;
};

VSOUTPUT_UNLIT VS_Unlit( float4 inPosition : POSITION )
{
   // Output struct
   VSOUTPUT_UNLIT OUT = (VSOUTPUT_UNLIT)0;

   // Output the transformed position
   OUT.vPosition = mul( inPosition, g_matWorldViewProj );

   // Output the projective texture coordinates
   OUT.vTexCoord = mul( inPosition, g_matTexture );

   // Output the scene depth
   OUT.fDepth = mul( inPosition, g_matLightViewProj ).z;

   return OUT;
}

We use percentage closer filtering (PCF) to smoothen out the jagged edges. To "do" PCF, we simply sample the 8 (we're using a 3x3 PCF kernel here) surrounding texels along with the center texel and take the average of all the depth comparisons.

// Shadow mapping pixel shader
float4  PS_Unlit( VSOUTPUT_UNLIT IN ) : COLOR0
{
   // Generate the 9 texture co-ordinates for a 3x3 PCF kernel
   float4 vTexCoords[9];
   // Texel size
   float fTexelSize = 1.0f / 1024.0f;

   // Generate the tecture co-ordinates for the specified depth-map size
   // 4 3 5
   // 1 0 2
   // 7 6 8
   vTexCoords[0] = IN.vTexCoord;
   vTexCoords[1] = IN.vTexCoord + float4( -fTexelSize, 0.0f, 0.0f, 0.0f );
   vTexCoords[2] = IN.vTexCoord + float4(  fTexelSize, 0.0f, 0.0f, 0.0f );
   vTexCoords[3] = IN.vTexCoord + float4( 0.0f, -fTexelSize, 0.0f, 0.0f );
   vTexCoords[6] = IN.vTexCoord + float4( 0.0f,  fTexelSize, 0.0f, 0.0f );
   vTexCoords[4] = IN.vTexCoord + float4( -fTexelSize, -fTexelSize, 0.0f, 0.0f );
   vTexCoords[5] = IN.vTexCoord + float4(  fTexelSize, -fTexelSize, 0.0f, 0.0f );
   vTexCoords[7] = IN.vTexCoord + float4( -fTexelSize,  fTexelSize, 0.0f, 0.0f );
   vTexCoords[8] = IN.vTexCoord + float4(  fTexelSize,  fTexelSize, 0.0f, 0.0f );
   // Sample each of them checking whether the pixel under test is shadowed or not
   float fShadowTerms[9];
   float fShadowTerm = 0.0f;
   for( int i = 0; i < 9; i++ )
   {
      float A = tex2Dproj( ShadowSampler, vTexCoords[i] ).r;
      float B = (IN.fDepth - 0.1f);

      // Texel is shadowed
      fShadowTerms[i] = A < B ? 0.0f : 1.0f;
      fShadowTerm     += fShadowTerms[i];
   }
   // Get the average
   fShadowTerm /= 9.0f;
   return fShadowTerm;
}

The screen buffer is good to go! Now all we need to do is blur this and project it back onto the scene in screen space.





Step 3


Contents
  Introduction
  Step 1
  Step 2
  Step 3
  Step 4

  Source code
  Printable version
  Discuss this article