Sign in to follow this  
Downie

SSAO - Again!

Recommended Posts

I know there are hundreds of docs and posts about screen space AO (believe me I've been through a lot) but I never got round to using this technique when everyone was doing it a year ago so I thought I'd try it now. The problem is that my SSAO looks waaaay too much like an edge detection algorithm, and if the camera goes too close to the model the light and dark bits swap and flicker I am not sure if my space transforms are wrong or what. If anyone has time could they look at the HLSL code and give me some hints. Thanks.
//-----------------------------------------
// Vertex Shader Input
//-----------------------------------------
struct VS_IN
{
	float3 PosL : POSITION;
	float2 Tex  : TEXCOORD;
};
//-----------------------------------------
// Vertex Shader Output/Pixel Shader Input
//-----------------------------------------
struct VS_OUT
{
	float4 PosH : SV_POSITION;
	float3 PosW	: POSITION;
    float2 Tex  : TEXCOORD;
    float3 vView: TEXCOORD1;
};
//========================================================================================================================
// Vertex Shader
//========================================================================================================================
VS_OUT VS(VS_IN vIn)
{
	VS_OUT vOut;
	
	//Leave as NDC
	vOut.PosH = float4(vIn.PosL, 1.0f);
	
	//Transform to world space
	vOut.PosW = mul(float4(vIn.PosL, 1.0f), mWorld);
	
	//Transform to view space
	vOut.Tex  = vIn.Tex;//(float2(vIn.Tex.x, -vIn.Tex.y) + float2(1.0f, 1.0f)) * 0.5f;
	
	//Calculate the View Ray
	vOut.vView = normalize(mul(float4((vCamPos - vOut.PosW), 1.0f), mView));
	//float fFarY = tan(3.14159f/3.0f/2.0f) * 100.0f;
    //float fFarX = fFarY *(gvScreenDims.x/gvScreenDims.y);
	//vOut.vView = normalize(mul(float4(float3(-fFarX * vIn.PosL.x, fFarY * vIn.PosL.y, 100.0f), 1.0f), mView));
	
	return vOut;
}
//=======================================================================================================================
// Pixel Shader
//=======================================================================================================================
float4 PS(VS_OUT pIn) : SV_Target
{
	//Sample the linear depth at this pixel
	float fDepth = gNormalMap.Sample(gTriLinearSam, pIn.Tex).a;
	//Get the normal for this pixel
	float3 vNorm = gNormalMap.Sample(gTriLinearSam, pIn.Tex).rgb;
	
	//Determine pixel dimensions use this to sample surrounding pixels
	float2 vPixDims = float2(1.0f/gvScreenDims.x, 1.0f/gvScreenDims.y); 
	
	float3 vEye  = pIn.vView * fDepth/pIn.vView.z;
	
 	//Comparison Sample Pixels
 	float4 vSamples[12] =
	{
		float4(-0.87866, 	0.157139, 	-0.115167,	0.0 ),
		float4(0.140679, 	-0.475516, 	-0.0639818,	0.0 ),
		float4(-0.0796121, 	0.158842, 	-0.677075,	0.0 ),
		float4(-0.0759516, 	-0.101676, 	-0.483625,	0.0 ),
		float4(0.12493, 	-0.0223423,	-0.483625,	0.0 ),
		float4(-0.0720074, 	0.243395, 	-0.967251,	0.0 ),
		float4(-0.207641, 	0.414286, 	0.187755,	0.0 ),
		float4(-0.277332, 	-0.371262, 	0.187755,	0.0 ),
		float4(0.63864, 	-0.114214, 	0.262857,	0.0 ),
		float4(-0.184051, 	0.622119, 	0.262857,	0.0 ),
		float4(0.110007, 	-0.219486, 	0.435574,	0.0 ),
		float4(0.235085, 	0.314707, 	0.696918,	0.0 )
	};

	//Generate a random normal
	float3 vRandNorm = normalize(gRandomMap.Sample(gTriLinearSam, pIn.Tex.yx));

	//The final occlusion factor
	float fOcclusion = 0.0f;
	
	for(int i=0; i<gnSamples; ++i)
	{
		//Reflect samples with a random normal to remove banding effects.
		//Make sure it is projected within the sphere
		float3 vRay = vEye + reflect(vSamples[i], vRandNorm) * gfRadius;
		
		//Transform to screen space
		float4 vSample = float4(vRay, 1.0f);
		vSample = mul(vSample, mProj);
		
		//Convert from NDC to texture space
		float2 vSampleTex = 0.5f * vSample.xy/vSample.w + 0.5;
		//Scale up the Tex Coords to give a blurry effect
		vSampleTex.x += 1.0/gvScreenDims.x * 2.0f;
		vSampleTex.y += 1.0/gvScreenDims.y * 2.0f;
		
		//Sample the depth at the offset pixels
		float4 vSampleDepth = gNormalMap.Sample(gTriLinearSam, vSampleTex);
		
		//Use the difference in normals as a weighting
		float fDeltaNorm = (1.0 - dot(vSampleDepth.rgb, vNorm));
		float fDeltaDepth = max(fDepth - vSampleDepth.a, 0.0f);

		//Calculate fade-off (1/ZD^2)
		float fAttenuation = 1.0f/(1.0f + fDeltaDepth * fDeltaDepth);	
		//Accumulate the occlusion
		fOcclusion += fAttenuation * fDeltaNorm;
	}
	
	//The final output colour: Average the occlusion factor and scale to get desired contrast	
	float fAO = (1 - (fOcclusion/gnSamples)) * gfContrast; 
	
	//Pack the normal back in to use in final pass and for blurring
	return float4(vNorm, fAO);
}

Share this post


Link to post
Share on other sites
Quote:
Original post by Downie
The problem is that my SSAO looks waaaay too much like an edge detection algorithm
Not to be pedantic or anything, but SSAO *is* an edge detection algorithm - albeit only concave edges.
Quote:
I am not sure if my space transforms are wrong or what. If anyone has time could they look at the HLSL code and give me some hints.
I don't see anything wrong in your code from a brief inspection. I do however find it is a little simpler if one performs all calculations in view space - might be worth considering.
Quote:
Hey dont know how to embed images.
Use the HTML <img src="http://www.example.com" /> tag.

Share this post


Link to post
Share on other sites
Quote:
Not to be pedantic or anything, but SSAO *is* an edge detection algorithm - albeit only concave edges.


I know it is similar but even with a blur pass the edges are too well defined, and it looks nothing like other SSAO screenshots. See what you think from the pics

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this