//////////////////// // Screen Spaced Ambient Occlusion shader // mainly based on shader of Alexander Kusternig //#define NUM_SAMPLES 8 #define NUM_SAMPLES 16 // rule of thumb: approx 1 / NUM_SAMPLES #define SAMPLE_INTENSITY 0.2 //#define SAMPLE_INTENSITY 0.125f #define AREA_SIZE 9e-1f #define VIEW_CORRECTION_SCALE 0.3f #define DISTANCE_SCALE 1e-6f struct fragment { // normalized screen position float4 pos: WPOS; float4 texCoord: TEXCOORD0; float3 view: COLOR0; }; struct pixel { float4 color: COLOR0; }; float2 reflect(float2 pt, float2 n) { // distance to plane float d = dot(n, pt); // reflect around plane float2 rpt = pt - d * 2.0f * n; //return pt; return rpt; } float2 rotate(float2 pt, float2 n) { float2 ptTransformed; ptTransformed.x = n.r * pt.x - n.g * pt.y; ptTransformed.y = n.g * pt.x + n.r * pt.y; return ptTransformed; } float ssao(fragment IN, uniform sampler2D positions, uniform sampler2D noiseTexture, uniform float2 samples[NUM_SAMPLES], uniform float3 currentNormal, uniform float3 currentViewDir, uniform float noiseMultiplier, uniform float4 centerPosition ) { // the w coordinate from the persp. projection float w = centerPosition.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; const float areaSize = 5e-1f; for (int i = 0; i < NUM_SAMPLES; i ++) { float2 offset = samples[i]; //sample noisetex; r stores costheta, g stores sintheta float2 mynoise = tex2D(noiseTexture, IN.texCoord.xy * noiseMultiplier).xy * 2.0f - 1.0f; // rotation //float2 offsetTransformed = offset; float2 offsetTransformed = rotate(offset, mynoise); //float2 offsetTransformed = reflect(offset, noise); // weight with projected coordinate to reach similar kernel size for near and far float2 texcoord = IN.texCoord.xy + offsetTransformed * AREA_SIZE * w; float3 sample_position = tex2D(positions, texcoord).xyz; float3 vector_to_sample = sample_position - centerPosition.xyz; float length_to_sample = length(vector_to_sample); float3 direction_to_sample = vector_to_sample / length_to_sample; // Angle between current normal and direction to sample controls AO intensity. float cos_angle = dot(direction_to_sample, currentNormal); cos_angle = max(cos_angle, 0.0f); // distance between current position and sample position controls AO intensity. float distance_intensity = (SAMPLE_INTENSITY * DISTANCE_SCALE) / (DISTANCE_SCALE + length_to_sample * length_to_sample); // if normal perpenticular to view dir, only half of the samples count float view_correction = 1.0f + VIEW_CORRECTION_SCALE * (1.0f - dot(currentViewDir, currentNormal)); total_ao += cos_angle * distance_intensity * view_correction; } return (1.0f - total_ao); } /** Computes ambient occlusion + diffuse reflections */ float4 globIllum(fragment IN, uniform sampler2D colors, uniform sampler2D positions, uniform sampler2D noiseTexture, uniform float2 samples[NUM_SAMPLES], uniform float3 currentNormal, uniform float3 currentViewDir, uniform float noiseMultiplier, uniform float4 centerPosition ) { // the w coordinate from the persp. projection float w = centerPosition.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; float3 total_color = float3(0.0f); const float areaSize = 5e-1f; for (int i = 0; i < NUM_SAMPLES; i ++) { float2 offset = samples[i]; //sample noisetex; r stores costheta, g stores sintheta float2 mynoise = tex2D(noiseTexture, IN.texCoord.xy * noiseMultiplier).xy * 2.0f - 1.0f; // rotation float2 offsetTransformed = rotate(offset, mynoise); // weight with projected coordinate to reach similar kernel size for near and far float2 texcoord = IN.texCoord.xy + offsetTransformed * AREA_SIZE * w; float3 sample_position = tex2D(positions, texcoord).xyz; float3 sample_color = tex2D(colors, texcoord).xyz; float3 vector_to_sample = sample_position - centerPosition.xyz; float length_to_sample = length(vector_to_sample); float3 direction_to_sample = vector_to_sample / length_to_sample; // Angle between current normal and direction to sample controls AO intensity. float cos_angle = dot(direction_to_sample, currentNormal); cos_angle = max(cos_angle, 0.0f); // distance between current position and sample position controls AO intensity. float distance_intensity = (SAMPLE_INTENSITY * DISTANCE_SCALE) / (DISTANCE_SCALE + length_to_sample * length_to_sample); // if normal perpenticular to view dir, only half of the samples count float view_correction = 1.0f + VIEW_CORRECTION_SCALE * (1.0f - dot(currentViewDir, currentNormal)); total_ao += cos_angle * distance_intensity * view_correction; total_color += cos_angle * distance_intensity * view_correction * sample_color * 0.3f; } return float4(total_color, 1.0f - total_ao); } float4 shade(fragment IN, uniform sampler2D colors, uniform sampler2D positions, uniform float3 normal, uniform float amb) { float4 lightDir = float4(0.8f, -1.0f, 0.7f, 0.0f); float4 lightDir2 = float4(-0.5f, 0.5f, 0.4f, 0.0f); float4 color = tex2D(colors, IN.texCoord.xy); float4 position = tex2D(positions, IN.texCoord.xy); float4 ambient = 0.3f; // float3 L = normalize(lightPosition - position); float3 light = normalize(lightDir.xyz); float3 light2 = normalize(lightDir2.xyz); float diffuseLight = max(dot(normal, light), 0.0f); float diffuseLight2 = max(dot(normal, light2), 0.0f); float diffuse = diffuseLight + diffuseLight2; //float diffuse = diffuseLight; return (ambient + diffuse) * color * (1.0f - amb) + amb * color; } /** The mrt shader for screen space ambient occlusion */ pixel main_ssao(fragment IN, uniform sampler2D colors, uniform sampler2D positions, uniform sampler2D normals, uniform sampler2D noiseTexture, uniform float2 samples[NUM_SAMPLES], uniform float noiseMultiplier, uniform sampler2D oldTex, const uniform float4x4 oldModelViewProj, uniform float maxDepth, uniform float expFactor ) { pixel OUT; float4 normal = tex2D(normals, IN.texCoord.xy); float amb = normal.w; // expand normal normal = normalize(normal * 2.0f - 1.0f); float3 viewDir = normalize(IN.view * 2.0f - float3(1.0f)); // the current world position float4 centerPosition = tex2D(positions, IN.texCoord.xy); float4 col = shade(IN, colors, positions, normal.xyz, amb); float ao = ssao(IN, positions, noiseTexture, samples, normal.xyz, viewDir, noiseMultiplier, centerPosition); float4 attenuated_color = ao * col; //float4 new_col = globIllum(IN, colors, positions, noiseTexture, samples, normal.xyz, viewDir, noiseMultiplier, centerPosition); //float ao = new_col.w; //float4 attenuated_color = ao * col + new_col; const float x = expFactor; float4 dummy = centerPosition * maxDepth; dummy.w = 1.0f; float4 oldPos = mul(oldModelViewProj, dummy); float newDepth = oldPos.z / oldPos.w; float2 tex = (oldPos.xy / oldPos.w) * 0.5f + 0.5f; float4 col1 = tex2D(oldTex, tex); float oldDepth = col1.w; float depthDif = 1.0f - newDepth / oldDepth; if ((tex.x >= 0.0f) && (tex.x < 1.0f) && (tex.y >= 0.0f) && (tex.y < 1.0f) && (abs(depthDif) < 8e-5f)) { OUT.color = attenuated_color * expFactor + col1 * float4(1.0f - expFactor); } else { OUT.color = attenuated_color; } OUT.color.w = tex2D(colors, IN.texCoord.xy).w; return OUT; } /** The mrt shader for standard rendering */ pixel main(fragment IN, uniform sampler2D colors, uniform sampler2D positions, uniform sampler2D normals ) { pixel OUT; float4 normal = tex2D(normals, IN.texCoord.xy); float amb = normal.w; // expand normal normal = normalize(normal * 2.0f - float4(1.0f)); float4 col = shade(IN, colors, positions, normal.xyz, amb); OUT.color = col; return OUT; }