# Deconstructing Crysis' SSAO Shader

This topic is 3873 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

pixout_cl Deferred_SSAO_Pass_PS(vert2fragSSAO IN)
{
pixout_cl OUT;

// define kernel
const half step = 1.f - 1.f/8.f;
half n = 0;
const half fScale = 0.025f;
const half3 arrKernel[8] =
{
normalize(half3( 1, 1, 1))*fScale*(n+=step),
normalize(half3(-1,-1,-1))*fScale*(n+=step),
normalize(half3(-1,-1, 1))*fScale*(n+=step),
normalize(half3(-1, 1,-1))*fScale*(n+=step),
normalize(half3(-1, 1 ,1))*fScale*(n+=step),
normalize(half3( 1,-1,-1))*fScale*(n+=step),
normalize(half3( 1,-1, 1))*fScale*(n+=step),
normalize(half3( 1, 1,-1))*fScale*(n+=step),
};

// create random rot matrix
half3 rotSample = tex2D(sRotSampler4x4_16, IN.ScreenTC.zw).rgb;
rotSample = (2.0 * rotSample - 1.0);

half fSceneDepth = tex2D( sceneDepthSampler, IN.ScreenTC.xy ).r;

// range conversions
half fSceneDepthM = fSceneDepth * PS_NearFarClipDist.y;

half3 vSampleScale = SSAO_params.zzw
* saturate(fSceneDepthM / 5.3f) // make area smaller if distance less than 5 meters
* (1.f + fSceneDepthM / 8.f ); // make area bigger if distance more than 32 meters

float fDepthRangeScale = PS_NearFarClipDist.y / vSampleScale.z * 0.85f;

// convert from meters into SS units
vSampleScale.xy *= 1.0f / fSceneDepthM;
vSampleScale.z  *= 2.0f / PS_NearFarClipDist.y;

float fDepthTestSoftness = 64.f/vSampleScale.z;

// sample
half4 vSkyAccess = 0.f;
half4 arrSceneDepth2[2];
half3 vIrrSample;
half4 vDistance;
float4 fRangeIsInvalid;

const half bHQ = (GetShaderQuality()==QUALITY_HIGH);

float fHQScale = 0.5f;

for(int i=0; i<2; i++)
{
vIrrSample = mirror(arrKernel[i*4+0], rotSample) * vSampleScale;
arrSceneDepth2[0].x = tex2D( sceneDepthSamplerAO, IN.ScreenTC.xy + vIrrSample.xy ).r + vIrrSample.z;
if (bHQ)
{
vIrrSample.xyz *= fHQScale;
arrSceneDepth2[1].x = tex2D( sceneDepthSamplerAO, IN.ScreenTC.xy + vIrrSample.xy ).r + vIrrSample.z;
}

vIrrSample = mirror(arrKernel[i*4+1], rotSample) * vSampleScale;
arrSceneDepth2[0].y = tex2D( sceneDepthSamplerAO, IN.ScreenTC.xy + vIrrSample.xy ).r + vIrrSample.z;
if (bHQ)
{
vIrrSample.xyz *= fHQScale;
arrSceneDepth2[1].y = tex2D( sceneDepthSamplerAO, IN.ScreenTC.xy + vIrrSample.xy ).r + vIrrSample.z;
}

vIrrSample = mirror(arrKernel[i*4+2], rotSample) * vSampleScale;
arrSceneDepth2[0].z = tex2D( sceneDepthSamplerAO, IN.ScreenTC.xy + vIrrSample.xy ).r + vIrrSample.z;
if (bHQ)
{
vIrrSample.xyz *= fHQScale;
arrSceneDepth2[1].z = tex2D( sceneDepthSamplerAO, IN.ScreenTC.xy + vIrrSample.xy ).r + vIrrSample.z;
}

vIrrSample = mirror(arrKernel[i*4+3], rotSample) * vSampleScale;
arrSceneDepth2[0].w = tex2D( sceneDepthSamplerAO, IN.ScreenTC.xy + vIrrSample.xy ).r + vIrrSample.z;
if (bHQ)
{
vIrrSample.xyz *= fHQScale;
arrSceneDepth2[1].w = tex2D( sceneDepthSamplerAO, IN.ScreenTC.xy + vIrrSample.xy ).r + vIrrSample.z;
}

float fDefVal = 0.55f;

for(int s=0; s<(bHQ ? 2 : 1); s++)
{
vDistance = fSceneDepth - arrSceneDepth2[s];
float4 vDistanceScaled = vDistance * fDepthRangeScale;
fRangeIsInvalid = (saturate( abs(vDistanceScaled) ) + saturate( vDistanceScaled ))/2;
vSkyAccess += lerp(saturate((-vDistance)*fDepthTestSoftness), fDefVal, fRangeIsInvalid);
}
}

OUT.Color = dot( vSkyAccess, (bHQ ? 1/16.0f : 1/8.0f)*2.0 ) - SSAO_params.y; // 0.075f
OUT.Color = saturate(lerp( 0.9f, OUT.Color, SSAO_params.x ));

return OUT;
}


Yes I know about http://www.gamedev.net/community/forums/topic.asp?topic_id=474166&PageSize=25&WhichPage=1. I'm having a bit of trouble understanding the whole shader so I thought I'd post it here and see if anyone would mind explaining it line by line. EDIT: er way arrKErnel is a circle isn't it... when the "box corners" get reflected about a random normal. I see that arrKernel is a box centered at the origin that gets shrunk a couple times and normalized and then a random normal is read in for reflection. Then the depthbuffer output is read in and taken out of [0-1]. Past that I'm kind of lost. Do I have it right so far? [Edited by - jstroh on May 29, 2008 1:11:47 PM]

##### Share on other sites
Once you've sampled the depth buffer, you reconstruct a view-space or world-space position from that depth value. Starting from this position, you then use your random vectors (what you have stored in arrKernel) reflected off the random normal to "visit" a few locations in relatively close proximity to the original position. For each of these new locations, you figure out the screen-space position and use that to once again sample the depth buffer. With this depth value the *actual* position at this pixel is determined and compared to the original sample position.

In basic terms what you're doing is for each pixel you're reaching our blindly and saying "is there anything nearby?". If many samples are close by you darken that pixel, since it's more likely to be occluded. If many samples far away, you don't darken the pixel.

##### Share on other sites
Quote:
 Original post by MJPOnce you've sampled the depth buffer, you reconstruct a view-space or world-space position from that depth value. Starting from this position, you then use your random vectors (what you have stored in arrKernel) reflected off the random normal to "visit" a few locations in relatively close proximity to the original position. For each of these new locations, you figure out the screen-space position and use that to once again sample the depth buffer. With this depth value the *actual* position at this pixel is determined and compared to the original sample position. In basic terms what you're doing is for each pixel you're reaching our blindly and saying "is there anything nearby?". If many samples are close by you darken that pixel, since it's more likely to be occluded. If many samples far away, you don't darken the pixel.

That's the clearest explanation I've heard so far, thankyou ;o

Ok now for some specifics of this shader:

(do you know why they do this?)

rotSample = (2.0 * rotSample - 1.0);

(what about here, this makes little sense to me :P)

	half3 vSampleScale = SSAO_params.zzw		* saturate(fSceneDepthM / 5.3f) // make area smaller if distance less than 5 meters    * (1.f + fSceneDepthM / 8.f ); // make area bigger if distance more than 32 meters  float fDepthRangeScale = PS_NearFarClipDist.y / vSampleScale.z * 0.85f;		// convert from meters into SS units	vSampleScale.xy *= 1.0f / fSceneDepthM;	vSampleScale.z  *= 2.0f / PS_NearFarClipDist.y;  float fDepthTestSoftness = 64.f/vSampleScale.z;

##### Share on other sites
Quote:
 Original post by jstroh(do you know why they do this?)rotSample = (2.0 * rotSample - 1.0);
This one is easy [smile]

As it is a texture sample, the values of the half3 will all lie in the 0 to 1 range. They are simply remapping that range to -1 to 1

##### Share on other sites
Quote:
Original post by ViLiO
Quote:
 Original post by jstroh(do you know why they do this?)rotSample = (2.0 * rotSample - 1.0);
This one is easy [smile]

As it is a texture sample, the values of the half3 will all lie in the 0 to 1 range. They are simply remapping that range to -1 to 1

I'm not retarded I swear! Oo

##### Share on other sites
but but but, no one wants to examine it? :(

##### Share on other sites
I was looking at their shader again today and noticed:
// original depth targetsampler2D sceneDepthSampler = sampler_state{ Texture = $ZTarget; MinFilter = POINT; MagFilter = POINT; MipFilter = POINT; AddressU = Clamp; AddressV = Clamp;};// downscaled depth targetsampler2D sceneDepthSamplerAO = sampler_state{ Texture =$ZTargetScaled; MinFilter = POINT; MagFilter = POINT; MipFilter = POINT; AddressU = Clamp; AddressV = Clamp;};

So they actually accumulate from a downsampled depthbuffer and reconstruct points from the original depth buffer.

vert2fragSSAO Deferred_SSAO_Pass_VS(app2vertShadow IN){	vert2fragSSAO OUT;#ifndef OPENGL  	OUT = (vert2fragSSAO)0; #endif	OUT.HPosition = mul(CompMatrix, IN.Position);	OUT.ScreenTC.xy = IN.baseTC.xy;	OUT.ScreenTC.zw = IN.baseTC.xy*g_VS_ScreenSize.xy/4;	OUT.WS_ViewVect = IN.viewDir;	return OUT;}

[Edited by - jstroh on June 9, 2008 5:05:41 PM]

• ### What is your GameDev Story?

In 2019 we are celebrating 20 years of GameDev.net! Share your GameDev Story with us.

(You must login to your GameDev.net account.)

• 15
• 14
• 46
• 22
• 27
• ### Forum Statistics

• Total Topics
634048
• Total Posts
3015236
×