Jump to content
  • Advertisement
Sign in to follow this  
jstroh

Deconstructing Crysis' SSAO Shader

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

If you intended to correct an error in the post then please contact us.

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 this post


Link to post
Share on other sites
Advertisement
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 this post


Link to post
Share on other sites
Quote:
Original post by MJP
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.


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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
Share on other sites
I was looking at their shader again today and noticed:

// original depth target
sampler2D sceneDepthSampler = sampler_state
{
Texture = $ZTarget;
MinFilter = POINT;
MagFilter = POINT;
MipFilter = POINT;
AddressU = Clamp;
AddressV = Clamp;
};

// downscaled depth target
sampler2D 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]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!