//========== Copyright (c) Valve Corporation, All rights reserved. ==========// // DYNAMIC: "QUALITY" "0..3" // Includes ======================================================================================= #include "common_ps_fxc.h" // Texture Samplers =============================================================================== sampler g_tFullFB : register( s0 ); sampler g_tSmallFB : register( s1 ); // Shaders Constants and Globals ================================================================== float4 g_vDists : register( c0 ); #define g_flNearBlurDist g_vDists.x #define g_flNearFocusDist g_vDists.y #define g_flFarFocusDist g_vDists.z #define g_flFarBlurDist g_vDists.w float3 g_vBlurAmounts : register( c1 ); #define g_flMaxBlurRadius g_vBlurAmounts.x #define g_flNearBlurStrength g_vBlurAmounts.y #define g_flFarBlurStrength g_vBlurAmounts.z float3 g_vNearFarDists : register( c2 ); #define g_flNearPlaneDist g_vNearFarDists.x #define g_flFarPlaneDist g_vNearFarDists.y #define g_flDepthConv g_vNearFarDists.z float4 g_vMagicConsts : register( c3 ); #if ( QUALITY == 0 ) #define NUM_SAMPLES 8 // These must match the C code #elif ( QUALITY == 1 ) #define NUM_SAMPLES 16 #elif ( QUALITY == 2 ) #define NUM_SAMPLES 16 #elif ( QUALITY == 3 ) #define NUM_SAMPLES 32 #endif float4 g_vPoisson[ NUM_SAMPLES/2 ] : register( c4 ); // Interpolated values ============================================================================ struct PS_INPUT { float2 vUv0 : TEXCOORD0; }; float DestAlphaDepthToViewSpaceDepth( float flDepth ) { return g_flDepthConv * flDepth + g_flNearPlaneDist; } // returns blur radius from depth as a fraction of max_blur. float BlurAmountFromDepth( float flDestAlphaDepth ) { /* dist = DestAlphaDepthToViewSpaceDepth( flDestAlphaDepth ); float flBlur = max( g_flNearBlurStrength * saturate( (flDestAlphaDepth - g_flNearFocusDist) / ( g_flNearBlurDist - g_flNearFocusDist ) ), g_flFarBlurStrength * saturate( (flDestAlphaDepth - g_flFarFocusDist) / ( g_flFarBlurDist - g_flFarFocusDist ) ) ); */ // A more optimized version that concatenates the math above and the one in DestAlphaDepthToViewSpaceDepth to a single muladd float flBlur = max( g_flNearBlurStrength * saturate( g_vMagicConsts.x * flDestAlphaDepth + g_vMagicConsts.y ), g_flFarBlurStrength * saturate( g_vMagicConsts.z * flDestAlphaDepth + g_vMagicConsts.w ) ); return flBlur; } float BlurRadiusFromDepth( float flDepth ) { return g_flMaxBlurRadius * BlurAmountFromDepth( flDepth ); } float4 ComputeTap( float flCenterDepth, float flCenterBlurRadius, float2 vUV, float2 vPoisson ) { float4 cTapSmall; float4 cTap; float2 vPoissonUV = vUV.xy + flCenterBlurRadius * vPoisson.xy; cTapSmall = tex2D( g_tSmallFB, vPoissonUV.xy ); cTap = tex2D( g_tFullFB, vPoissonUV.xy ); float flTapBlur = BlurAmountFromDepth( cTap.a ); // Maybe 50/50 mix between low and high here? cTap = lerp( cTap, cTapSmall, saturate( 2.2 * flTapBlur ) ); // TODO: tweak blur amount. float flLerpedTapBlur = BlurAmountFromDepth( cTap.a ); float weight = ( cTap.a >= flCenterDepth ) ? 1.0 : ( flLerpedTapBlur*flLerpedTapBlur ); return float4( cTap.rgb, 1 ) * weight; } float4 ComputeTapHQ( float flCenterDepth, float flCenterBlurRadius, float2 vUV, float2 vPoisson ) { float4 cTap; cTap = tex2D( g_tFullFB, vUV.xy + flCenterBlurRadius * vPoisson.xy ); float flTapBlur = BlurAmountFromDepth( cTap.a ); float weight = ( cTap.a >= flCenterDepth ) ? 1.0 : ( flTapBlur * flTapBlur ); return float4( cTap.rgb, 1 ) * weight; } // Main =========================================================================================== float4 main( PS_INPUT i ) : COLOR { // TODO: BETTER DOWNSAMPLE THAT TAKES DEPTH INTO ACCOUNT? float4 cOut = { 0, 0, 0, 0 }; float4 cCenterTap = tex2D( g_tFullFB, i.vUv0 ); float flCenterBlurRadius = BlurRadiusFromDepth( cCenterTap.a ); // circle of confusion radius for current pixel cCenterTap.a -= 0.001; // z-bias to avoid strange banding artifact on almost orthogonal walls #if ( QUALITY < 2 ) // ATI's Ruby1-style algorithm for ( int t = 0; t < NUM_SAMPLES/2; t++ ) { cOut.rgba += ComputeTap( cCenterTap.a, flCenterBlurRadius, i.vUv0, g_vPoisson[t].xy ); cOut.rgba += ComputeTap( cCenterTap.a, flCenterBlurRadius, i.vUv0, g_vPoisson[t].wz ); } #else // Less fancy, with less fetches per tap and less math. Needs more samples to look smooth. cOut = cCenterTap; cOut.a = 1.0; // Use the center sample we just fetched for ( int t = 0; t < NUM_SAMPLES/2; t++ ) { cOut.rgba += ComputeTapHQ( cCenterTap.a, flCenterBlurRadius, i.vUv0, g_vPoisson[t].xy ); cOut.rgba += ComputeTapHQ( cCenterTap.a, flCenterBlurRadius, i.vUv0, g_vPoisson[t].wz ); } #endif //cOut.rgb = cOut.a / float(NUM_SAMPLES+1); //cOut = lerp( tex2D( g_tFullFB, i.vUv0 ), tex2D( g_tSmallFB, i.vUv0 ).aaaa, 0.5 ); if ( cOut.a > 0.0 ) cOut.rgba /= cOut.a; else cOut.rgba = cCenterTap.rgba; return cOut; }