//======================================================================================= string description = "Screenspace Ambient Occlusion"; //======================================================================================= //======================================================================================= #define SAMPLES 24 static const float2 samples[SAMPLES] = { {-0.326212f, -0.405805f}, {-0.840144f, -0.07358f}, {-0.695914f, 0.457137f}, {-0.203345f, 0.620716}, {0.96234f, -0.194983f}, {0.473434f, -0.480026f}, {0.519456, 0.767022f}, {0.185461f, -0.893124f}, {0.507431f, 0.064425f}, {0.89642f, 0.412458f}, {-0.32194f, -0.932615f}, {-0.791559f, -0.597705f}, {0.326212f, 0.405805f}, {0.840144f, 0.07358f}, {0.695914f, -0.457137f}, {0.203345f, -0.620716}, {-0.96234f, 0.194983f}, {-0.473434f, 0.480026f}, {-0.519456, -0.767022f}, {-0.185461f, 0.893124f}, {-0.507431f, -0.064425f}, {-0.89642f, -0.412458f}, {0.32194f, 0.932615f}, {0.791559f, 0.597705f} }; //======================================================================================= float4x4 WorldViewProj : WorldViewProjection; float4x4 WorldView : WorldView; float4x4 CameraViewProjection; shared float3 WorldCenterPosition; // Center positions around this point (usually eye point). shared float WorldScale; // Scale positions to maintain precision. float AreaSize = 1.0; float SampleIntensity = 0.1; //======================================================================================= shared texture positions; shared texture diffuse; shared texture normals; sampler PositionsSampler = sampler_state { texture = ; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; MINFILTER = POINT; MAGFILTER = POINT; MIPFILTER = NONE; }; sampler DiffuseSampler = sampler_state { texture = ; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; MINFILTER = POINT; MAGFILTER = POINT; MIPFILTER = NONE; }; sampler NormalsSampler = sampler_state { texture = ; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; MINFILTER = POINT; MAGFILTER = POINT; MIPFILTER = NONE; }; //======================================================================================= struct VertexInput { float4 position : POSITION; float2 texcoord : TEXCOORD0; }; struct VertexOutput { float4 position : POSITION; float2 texcoord : TEXCOORD0; }; struct PixelOutput { float4 color : COLOR0; }; //======================================================================================= VertexOutput vs_render(VertexInput IN) { VertexOutput OUT; OUT.position = float4(IN.position.xyz, 1.0); OUT.texcoord = IN.texcoord; return OUT; } //======================================================================================= PixelOutput ps_render(VertexOutput IN) { PixelOutput OUT; float4 positions = tex2D(PositionsSampler, IN.texcoord); float4 normals = tex2D(NormalsSampler, IN.texcoord); float3 current_position = positions.xyz; float3 current_normal = normals.xyz * 2.0 - 1.0; // Get postprojection Z value to adjust AO area size. float3 worldposition = (positions.xyz / WorldScale) + WorldCenterPosition; float4 projected_position = mul(float4(worldposition.xyz, 1.0), CameraViewProjection); projected_position.xyz /= projected_position.w; // Check in a circular area around the current position. // Shoot vectors to the positions there, and check the angle to these positions. // Summing up these angles gives an estimation of the occlusion at the current position. float total_ao = 0.0; for (float sample = 0.0; sample < SAMPLES; sample++) { float2 offset = samples[sample]; float2 texcoord = IN.texcoord + offset * AreaSize * (1.0 - projected_position.z); float3 sample_position = tex2D(PositionsSampler, texcoord).xyz; float3 vector_to_sample = sample_position - current_position; float length_to_sample = length(vector_to_sample); float3 direction_to_sample = vector_to_sample / length_to_sample; length_to_sample /= WorldScale; // Angle between current normal and direction to sample controls AO intensity. float angle = dot(direction_to_sample, current_normal); angle = max(angle, 0.0); // Distance between current position and sample position controls AO intensity. float distance_intensity = 1.0 - length_to_sample * 2.0; distance_intensity = max(distance_intensity, 0.0); total_ao += angle * SampleIntensity * distance_intensity; } OUT.color = float4(0.0, 0.0, 0.0, total_ao); return OUT; } //======================================================================================= technique render { pass p0 { VertexShader = compile vs_3_0 vs_render(); PixelShader = compile ps_3_0 ps_render(); } }