//========== Copyright (c) Valve Corporation, All rights reserved. ==========// #include "common_ps_fxc.h" // DYNAMIC: "AO_MODE" "0..2" sampler FrontNDBuffer : register( s0 ); sampler RandomReflectionSampler : register( s1 ); #define NUM_SSAO_SAMPLES 9 struct PS_INPUT { float2 vScreenPos : VPOS; float2 tc : TEXCOORD0; float3 vEyeRay : TEXCOORD1; }; const float4 cNoiseOffset : register( c1 ); const float2 g_vInvScreenExtents : register( c2 ); const float3 g_vEyePt : register( c3 ); // vEyePos in world space const float3 g_vEyeDir : register( c4 ); // vForward / zFar const matrix g_mViewProj : register( c5 ); // 5, 6, 7, 8 const float4 g_vRandSampleScale : register( c9 ); // xy is rand sample scale, zw is half-texel offset shift + 0.5 const float4 g_vSampleRadiusNBias : register( c10 ); // x is world rad, y is depth to world space scale, z is far plane, w is bias const float4 g_vSphereSamples[NUM_SSAO_SAMPLES] : register( c11 ); float ToonEdges( float2 tcCenter ) { float4 vND[9]; // 3x3 neighborhood offsets float2 v3x3Offsets[9] = { float2( -0.00078125f, -0.001388f ), float2( 0, -0.001388f ), float2( 0.00078125f, -0.001388f ), float2( -0.00078125f, 0 ), float2( 0, 0 ), float2( 0.00078125f, 0 ), float2( -0.00078125f, 0.001388f ), float2( 0, 0.001388f ), float2( 0.00078125f, 0.001388f ) }; // Look up 3x3 neighborhood [unroll] for( int i=0; i<9; i++ ) { vND[i] = tex2D( FrontNDBuffer, tcCenter + v3x3Offsets[i].xy ); vND[i].xyz = normalize( vND[i].xyz ); vND[i].w = vND[i].w; } // Take some local dot products float4 vNormalDots; vNormalDots.x = dot( vND[4].xyz, vND[1].xyz ); vNormalDots.y = dot( vND[4].xyz, vND[3].xyz ); vNormalDots.z = dot( vND[4].xyz, vND[5].xyz ); vNormalDots.w = dot( vND[4].xyz, vND[7].xyz ); // Threshold the four dots and combine float normalEdges = smoothstep( 0.7, 0.9, dot( vNormalDots > 0.8f, float4( 0.25, 0.25, 0.25, 0.25 ) ) ); float flSobelX = 1.0f - ( abs( -vND[0].w - vND[3].w * 2.0f - vND[6].w + vND[2].w + vND[5].w * 2.0f + vND[8].w ) > 0.004 ); float flSobelY = 1.0f - ( abs( -vND[0].w - vND[1].w * 2.0f - vND[2].w + vND[6].w + vND[7].w * 2.0f + vND[8].w ) > 0.004 ); return normalEdges * flSobelX * flSobelY; } float3 RecoverWorldPos( float flDepth, float3 vEyeRay ) { return g_vEyePt + vEyeRay * flDepth; } float4 main( PS_INPUT Input ) : COLOR0 { float2 vShiftedScreenPos = Input.vScreenPos.xy + float2( 0.5, 0.5 ); float2 vScreenCoord = ( vShiftedScreenPos ) * ( g_vInvScreenExtents.xy ); float2 vRandSampleCoord = ( Input.vScreenPos.xy ) * ( g_vRandSampleScale.xy ); // Sample from ND buffer float4 vFrontND = tex2D( FrontNDBuffer, vScreenCoord ); float3 vNormal = vFrontND.xyz; float flDepth = vFrontND.a; float3 vReflectPlane = tex2D( RandomReflectionSampler, vRandSampleCoord + cNoiseOffset.xy) * 2 - 1; float3 vEyePos = RecoverWorldPos( flDepth, Input.vEyeRay ); vEyePos += vNormal * flDepth * g_vSampleRadiusNBias.w; // Loop over samples float flTotalOcc = 0.0f; [unroll] for ( int s=0; s