Index: GTP/trunk/App/Demos/Vis/FriendlyCulling/src/shaders/ssao.cg
===================================================================
--- GTP/trunk/App/Demos/Vis/FriendlyCulling/src/shaders/ssao.cg	(revision 3319)
+++ GTP/trunk/App/Demos/Vis/FriendlyCulling/src/shaders/ssao.cg	(revision 3320)
@@ -156,7 +156,12 @@
 
 
-/** The ssao shader returning the an intensity value between 0 and 1
-	This version of the ssao shader uses the dotproduct between pixel and 
-	sample normal as weight.
+/** The ssao shader returning the an intensity value between 0 and 1.
+	This version of the ssao shader uses the dotproduct between 
+	pixel-to-sample direction and sample normal as weight.
+
+    The algorithm works like the following:
+	1) Check in a circular area around the current position.
+	2) Shoot vectors to the positions there, and check the angle to these positions.
+	3) Summing up these angles gives an estimation of the occlusion at the current position.
 */
 float3 ssao2(fragment IN,
@@ -172,64 +177,109 @@
 			 float3 tr, 
 			 float3 viewDir,
-			 sampler2D normalTex,
-			 float sampleIntensity
+			 float convergence,
+			 float sampleIntensity,
+			 bool isMovingObject,
+			 sampler2D normalTex
 			 )
 {
 	float total_ao = .0f;
+	float validSamples = .0f;
 	float numSamples = .0f;
-	float validSamples = .0f;
 
 	for (int i = 0; i < NUM_SAMPLES; ++ i) 
 	{
-		const float2 offset = samples[i];
-
-#if 1
+		float2 offset;
+
 		////////////////////
-		//-- add random noise: reflect around random normal vector (rather slow!)
-
-		const float2 mynoise = tex2Dlod(noiseTex, float4(IN.texCoord * 4.0f, 0, 0)).xy;
-		const float2 offsetTransformed = myreflect(offset, mynoise);
-#else
-		const float2 offsetTransformed = offset;
-#endif
+		//-- add random noise: reflect around random normal vector 
+		//-- (affects performance for some reason!)
+
+		if (convergence < SSAO_CONVERGENCE_THRESHOLD)
+		{
+			float2 mynoise = tex2Dlod(noiseTex, float4(IN.texCoord * 4.0f, 0, 0)).xy;
+			//offset = myreflect(samples[i], mynoise);
+			offset = myrotate(samples[i], mynoise.x);
+		}
+		else
+		{
+			offset = samples[i];
+		}
+		
 		// weight with projected coordinate to reach similar kernel size for near and far
-		//const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor + jitter;
-		const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor;
-
-		//if ((texcoord.x <= 1.0f) && (texcoord.x >= 0.0f) && (texcoord.y <= 1.0f) && (texcoord.y >= 0.0f)) ++ numSamples;
-		float4 sampleColor = tex2Dlod(colors, float4(texcoord, 0, 0));
-
+		const float2 texcoord = IN.texCoord.xy + offset * scaleFactor;
+
+		const float4 sampleColor = tex2Dlod(colors, float4(texcoord, .0f, .0f));
 		const float3 samplePos = ReconstructSamplePos(sampleColor.w, texcoord, bl, br, tl, tr);
+		
+
+		////////////////
+		//-- compute contribution of sample using the direction and angle
+
+		float3 dirSample = samplePos - centerPosition;
+
+		//const float minDist = 9e-1f;
+		const float minDist = 1e-2f;
+
+		//const float sqrLen = max(SqrLen(dirSample), minDist);
+		//const float lengthToSample = sqrt(sqrLen);
+		const float lengthToSample =  max(length(dirSample), minDist);
+
+		dirSample /= lengthToSample; // normalize
+
+		// angle between current normal and direction to sample controls AO intensity.
+		float cosAngle = dot(dirSample, normal);
+
 		// the normal of the current sample
 		const float3 sampleNormal = tex2Dlod(normalTex, float4(texcoord, 0, 0)).xyz;
 
-
-		////////////////
-		//-- compute contribution of sample using the direction and angle
-
-		float3 dirSample = samplePos - centerPosition;
-
-		const float sqrLen = max(SqrLen(dirSample), 1e-2f);
-		const float lengthToSample = sqrt(sqrLen);
-		//const float lengthToSample = max(length(dirSample), 1e-6f);
-
-		dirSample /= lengthToSample; // normalize
-
 		// angle between current normal and direction to sample controls AO intensity.
-		float cosAngle = .5f + dot(sampleNormal, -normal) * 0.5f;
-		// use binary decision to cull samples that are behind current shading point
-		cosAngle *= step(0.0f, dot(dirSample, normal));
-	
-		const float aoContrib = sampleIntensity / sqrLen;
+		float cosAngle2 = .5f + dot(sampleNormal, -normal) * 0.5f;
+		
+		//const float aoContrib = sampleIntensity / sqrLen;
+		const float aoContrib = sampleIntensity / lengthToSample;
+
 		//const float aoContrib = (1.0f > lengthToSample) ? occlusionPower(9e-2f, DISTANCE_SCALE + lengthToSample): .0f;
-
-		total_ao += cosAngle * aoContrib;
+		total_ao += max(cosAngle, .0f) * aoContrib * cosAngle2;
+
+		++ numSamples;
 
 		// check if the samples have been valid in the last frame
-		validSamples += (1.0f - step(1.0f, lengthToSample)) * sampleColor.x;
-
-		++ numSamples;
-	}
-
+		// only mark sample as invalid if in the last / current frame
+		// they possibly have any influence on the ao
+
+		const float changeFactor = sampleColor.y;
+		const float pixelValid = sampleColor.x;
+
+		// hack:
+		// we check if the sample could have been near enough 
+		// to the current pixel or if the angle is small enough
+		// to have any influence in the current or last frame
+#if 1
+		const float tooFarAway = step(0.5f, lengthToSample - changeFactor);
+		if (pixelValid < 2.0f)
+			validSamples = max(validSamples, pixelValid * (1.0f - tooFarAway) * step(-0.1f, cosAngle));
+		else
+			validSamples = max(validSamples, pixelValid);
+#else
+		validSamples = max(validSamples, pixelValid);
+#endif
+
+#ifdef USE_GTX
+		// we can bail out early and use a minimal #samples)
+		// if some conditions are met as long as the hardware supports it
+		if (numSamples >= MIN_SAMPLES)
+		{
+			//break;
+			// if the pixel belongs to a static object and all the samples stay valid in the current frame
+			if (!isMovingObject && (validSamples < 1.0f) && (convergence > NUM_SAMPLES)) break;
+			// if the pixel belongs to a dynamic object but the #accumulated samples for this pixel is sufficiently high 
+			// (=> there was no discontinuity recently)
+			//else if (isMovingObject && (convergence > SSAO_CONVERGENCE_THRESHOLD)) break;
+			//else if (isMovingObject && (convergence > NUM_SAMPLES * 5)) break;
+		}
+#endif
+	}
+
+	// "normalize" ao contribution
 	total_ao /= numSamples;
 
@@ -238,9 +288,9 @@
 	// => compensate for this (on the other hand, projected sampling area could be larger!)
 	const float viewCorrection = 1.0f + VIEW_CORRECTION_SCALE * max(dot(viewDir, normal), 0.0f);
-	total_ao += cosAngle * aoContrib * viewCorrection;
-
+	total_ao *= viewCorrection;
 #endif
 
-	return float3(max(0.0f, 1.0f - total_ao), validSamples, numSamples);
+	//return float3(total_ao, validSamples, numSamples);
+	return float3(min(1.0f, total_ao), validSamples, numSamples);
 }
 
@@ -450,5 +500,6 @@
 	if (eyeSpaceDepth < DEPTH_THRESHOLD)
 	{
-		ao = ssao(IN, colors, noiseTex, samples, normal, eyeSpacePos.xyz, scaleFactor, bl, br, tl, tr, normalize(viewDir), oldWeight, sampleIntensity, isMovingObject);
+		//ao = ssao(IN, colors, noiseTex, samples, normal, eyeSpacePos.xyz, scaleFactor, bl, br, tl, tr, normalize(viewDir), oldWeight, sampleIntensity, isMovingObject);
+		ao = ssao2(IN, colors, noiseTex, samples, normal, eyeSpacePos.xyz, scaleFactor, bl, br, tl, tr, normalize(viewDir), oldWeight, sampleIntensity, isMovingObject, normals);
 		//ao = ssao2(IN, colors, noiseTex, samples, normal, eyeSpacePos.xyz, scaleFactor, bl, br, tl, tr, normalize(viewDir), normals, sampleIntensity);
 	}
