Soft-Edged Shadows
Step 2: Rendering the shadowed scene into a bufferNext, 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.
|
|