Soft-Edged Shadows
Step 3: Blurring the screen bufferWe use a seperable gaussian filter to blur the screen buffer, but one could also use a Poisson filter. The render targets this time are A8R8G8B8 textures accompanied by corresponding surfaces. We need 2 render targets, one for the horizontal pass and the other for the vertical pass. // Create the blur maps for( int i = 0; i < 2; i++ ) { if( FAILED( g_pd3dDevice->CreateTexture( SCREEN_WIDTH, SCREEN_HEIGHT, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &g_pBlurMap[i], NULL ) ) ) { MessageBox( g_hWnd, "Unable to create blur map!", "Error", MB_OK | MB_ICONERROR ); return E_FAIL; } // Grab the texture's surface g_pBlurMap[i]->GetSurfaceLevel( 0, & g_pBlurSurf[i] ); } We generate 15 Gaussian offsets and their corresponding weights using the following functions. float GetGaussianDistribution( float x, float y, float rho ) { float g = 1.0f / sqrt( 2.0f * 3.141592654f * rho * rho ); return g * exp( -(x * x + y * y) / (2 * rho * rho) ); } void GetGaussianOffsets( bool bHorizontal, D3DXVECTOR2 vViewportTexelSize, D3DXVECTOR2* vSampleOffsets, float* fSampleWeights ) { // Get the center texel offset and weight fSampleWeights[0] = 1.0f * GetGaussianDistribution( 0, 0, 2.0f ); vSampleOffsets[0] = D3DXVECTOR2( 0.0f, 0.0f ); // Get the offsets and weights for the remaining taps if( bHorizontal ) { for( int i = 1; i < 15; i += 2 ) { vSampleOffsets[i + 0] = D3DXVECTOR2( i * vViewportTexelSize.x, 0.0f ); vSampleOffsets[i + 1] = D3DXVECTOR2( -i * vViewportTexelSize.x, 0.0f ); fSampleWeights[i + 0] = 2.0f * GetGaussianDistribution( float(i + 0), 0.0f, 3.0f ); fSampleWeights[i + 1] = 2.0f * GetGaussianDistribution( float(i + 1), 0.0f, 3.0f ); } } else { for( int i = 1; i < 15; i += 2 ) { vSampleOffsets[i + 0] = D3DXVECTOR2( 0.0f, i * vViewportTexelSize.y ); vSampleOffsets[i + 1] = D3DXVECTOR2( 0.0f, -i * vViewportTexelSize.y ); fSampleWeights[i + 0] = 2.0f * GetGaussianDistribution( 0.0f, float(i + 0), 3.0f ); fSampleWeights[i + 1] = 2.0f * GetGaussianDistribution( 0.0f, float(i + 1), 3.0f ); } } } To blur the screen buffer, we set the blur map as the render target and render a screen sized quad with the following vertex and pixel shaders. // Gaussian filter vertex shader struct VSOUTPUT_BLUR { float4 vPosition : POSITION; float2 vTexCoord : TEXCOORD0; }; VSOUTPUT_BLUR VS_Blur( float4 inPosition : POSITION, float2 inTexCoord : TEXCOORD0 ) { // Output struct VSOUTPUT_BLUR OUT = (VSOUTPUT_BLUR)0; // Output the position OUT.vPosition = inPosition; // Output the texture coordinates OUT.vTexCoord = inTexCoord; return OUT; } // Horizontal blur pixel shader float4 PS_BlurH( VSOUTPUT_BLUR IN ): COLOR0 { // Accumulated color float4 vAccum = float4( 0.0f, 0.0f, 0.0f, 0.0f ); // Sample the taps (g_vSampleOffsets holds the texel offsets // and g_fSampleWeights holds the texel weights) for(int i = 0; i < 15; i++ ) { vAccum += tex2D( ScreenSampler, IN.vTexCoord + g_vSampleOffsets[i] ) * g_fSampleWeights[i]; } return vAccum; } // Vertical blur pixel shader float4 PS_BlurV( VSOUTPUT_BLUR IN ): COLOR0 { // Accumulated color float4 vAccum = float4( 0.0f, 0.0f, 0.0f, 0.0f ); // Sample the taps (g_vSampleOffsets holds the texel offsets and // g_fSampleWeights holds the texel weights) for( int i = 0; i < 15; i++ ) { vAccum += tex2D( BlurHSampler, IN.vTexCoord + g_vSampleOffsets[i] ) * g_fSampleWeights[i]; } return vAccum; } There, the blur maps are ready. To increase the blurriness of the shadows, increase the texel sampling distance. The last step, of course, is to project the blurred map back onto the scene in screen space.
|
|