Skip to main content
GameDev.net gamedev.net
🔒 Locked

SSAO woes

Started by Harry Hunt Mar 29, 2008 at 11:20 AM 14 replies 10.7k views
Original Post
Harry Hunt
Harry Hunt
I'm trying to implement Inigo Quilez' version of Screen Space Ambient Occlusion in ATI's RenderMonkey and I've run into a problem... Here's my code: Depth-out shader VS:
float4x4 matWorldViewProjection;

struct VS_INPUT 
{
   float4 Position: POSITION0;
};

struct VS_OUTPUT 
{
   float4 Position: POSITION0;
   float4 Pos : TEXCOORD0;
};

VS_OUTPUT vs_main(VS_INPUT Input)
{
   VS_OUTPUT Output;
   Output.Position = mul(Input.Position, matWorldViewProjection);
   Output.Pos = Output.Position;
   
   return Output;
}

PS:
struct PS_INPUT 
{
   float4 Pos : TEXCOORD0;  
};

float4 ps_main(PS_INPUT Input) : COLOR0
{
   float depth = Input.Pos.z / Input.Pos.w;
   return float4(depth, 1.0f, 1.0f, 1.0f);
}

SSAO-Shader VS:
struct VS_INPUT 
{
   float4 Position : POSITION0;
};

struct VS_OUTPUT 
{
   float4 Position : POSITION;   
   float2 TexCoord : TEXCOORD0;
   float4 Pos : TEXCOORD1; 
};

VS_OUTPUT vs_main(VS_INPUT Input)
{
   VS_OUTPUT Output;

   Output.Position = float4(sign(Input.Position.xy), 0.0, 1.0);
   Output.TexCoord.x = (Output.Position.x + 1.0) * 0.5;
   Output.TexCoord.y = 1.0 - ((Output.Position.y + 1.0) * 0.5);
   Output.Pos = Output.Position;
    
   return Output;
}

PS:
sampler2D DepthTexture;
sampler2D RandomTexture;

float4x4 matProjectionInverse;

float fRange;
int nNumSamples;
float2 fRandTextureSize;

struct PS_INPUT 
{
   float2 TexCoord : TEXCOORD0;
   float4 Pos : TEXCOORD1; 
};

float4 ps_main(PS_INPUT Input) : COLOR0
{   
   float4 samples[16] =
   {
      float4(0.355512, -0.709318, -0.102371, 0.0),
      float4(0.534186, 0.71511, -0.115167, 0.0),
      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),
      float4(-0.290012, 0.0518654, 0.522688, 0.0),
      float4(0.0975089, -0.329594, 0.609803, 0.0)
   };
           
   float depth = tex2D(DepthTexture, Input.TexCoord).r;
   float4 eyePos = mul(float4(Input.Pos.x, Input.Pos.y, depth, 1.0f), matProjectionInverse);
   eyePos.xyz = eyePos.xyz / eyePos.www;
   eyePos.w = 1.0f;   

   float4 randomPlane = tex2D(RandomTexture, Input.Pos.xy * fRandTextureSize.xy);
   randomPlane = randomPlane * 2.0 - float4(1.0, 1.0, 1.0, 1.0);

   float occlusion = 0.0;
   for (int i = 0; i < nNumSamples; i++)
   {
      float3 sample = eyePos + fRange * reflect(samples.xyz, randomPlane.xyz);
   
      float3 ss = sample.xyz * float3(0.75, 1.0, 1.0);
      float4 sz = tex2Dproj(DepthTexture, float4(ss * 0.5 + ss.z * float3(0.5, 0.5, 0.5), 1.0));

      float zDifference = 50.0 * max(sample.z - sz.x, 0.0);
      occlusion += 1.0 / (1.0 + zDifference * zDifference);
   }
   return float4(occlusion / nNumSamples, occlusion / nNumSamples, occlusion / nNumSamples, occlusion / nNumSamples);
}

Right now, all this gives me is a noisy image with all geometry rendered completely in black. I've tried to figure this out, but I don't see what I'm doing wrong. I'm suspecting that the way I calculate the Eye-Position might be incorrect, but then again I don't see why it would be. I didn't use Quilez' method because I couldn't get it to work in RenderMonkey, but I assumed that wouldn't be a problem. EDIT: Here's a screenshot: Free Image Hosting at www.ImageShack.us Any ideas? Thanks a lot in advance!
MJP
MJP
It doesn't look like you're properly reconstructing the pixel postion from the depth buffer in your SSAO pixel shader. I believe you have to some multiplying and diving by w to get the world-space position. You may want to have a look at this thread, where we discussed this issue.
Harry Hunt
Harry Hunt
Thanks for the reply!

I read through the thread and I really like your method for restoring the world space position. I'll try that one later.

I do believe however that the code I posted will give me the correct view-space-position which is what I need for SSAO (at least according to Quilez' article). I actually compared the computed view-space position with the "raw" view-space position and it appears to be correct.

I also tried computing the world-space-position but the results were pretty weird. So maybe it isn't the position after all? Any more ideas?

Thanks in advance!
Ysaneya
Ysaneya
Those 2 lines look particularly suspicious to me:

      float3 ss = sample.xyz * float3(0.75, 1.0, 1.0);      float4 sz = tex2Dproj(DepthTexture, float4(ss * 0.5 + ss.z * float3(0.5, 0.5, 0.5), 1.0));

Harry Hunt
Harry Hunt
I agree, but they're not mine. I took them from Quilez' website.
What those two lines are supposed to do is take a view-space-vector ("sample") and use it to sample from the depth map. I can't say I fully understand what the "magic numbers" in these two lines of code mean, but maybe somebody can put some light into this darkness.

Thanks a bunch!
Ysaneya
Ysaneya
I will ask him next monday as I know him IRL. The correct thing would be IMO:

vec4 sz = texture2D(tex0, (se.xy/se.z)*0.5 + 0.5);


I do not know where the multiplication with (0.75, 1.0) comes from, it does not really make sense to me.

Y.
Harry Hunt
Harry Hunt
Thanks so much!
I tried your code and my bunny is still completely black :-(
Maybe I'm still doing something else wrong, but I dunno what. The view-space pixel position seems to be right.

EDIT The RenderMonkey project file, just in case.

[Edited by - Harry Hunt on March 30, 2008 7:25:27 AM]
jamesw
jamesw
Quote:
I do not know where the multiplication with (0.75, 1.0) comes from, it does not really make sense to me.


I think that is taking the aspect ratio right out of the projection matrix.
Ysaneya
Ysaneya
Okay, I asked Inigo about your problem, here are a few hints:

- the multiplication by float3(0.75, 1.0, 1.0) shouldn't be needed, this was a custom projection/ratio fix as he was using the shader in his raytracer, in standard OpenGL/D3D clip space it is going from -1 to +1, so it's not needed.

- inside the loop, once you get your sample in eye space:
float3 sample = eyePos + fRange * reflect(samples.xyz, randomPlane.xyz);


and to be coherent with your eyePos, you should apply the projection matrix again, then sample the texture in 2D without the projection:

float4 ss = mul(float4(sample, 1.0), matProjection);ss.xyz = (ss.xyz / ss.www) * 0.5 + 0.5;float4 sz = tex2D(DepthTexture, ss.xy);


- you should also play and tweak the various parameters and settings until you get good results (like the *50 in zDifference).

Y.
Harry Hunt
Harry Hunt
Awesome! Thank you so much! I'll try that.
draktheas
draktheas
Harry, if you figure out what the problem is please let us know what you did to solve it. I have a similar problem with my implementation of SSAO.

Thanks,
Drak
MJP
MJP
I don't know if this well help you guys out at all but in that thread I linked I posted a "cleaned-up" version of Inigo's algorithm in HLSL. I used some more verbose variables names named a few of the parameters that you can tweak, which might make things easier to understand. I also mentioned in that thread what parameters I used to get the results for the screenshots I posted. Only catch is I didn't implement the "reflect using a random texture" part, but that's not too hard.

#define NUM_SAMPLE_POINTS	32float2 depthBufferDimensions;float4 sampleOffsets [NUM_SAMPLE_POINTS];float  sampleRadius;float  distanceScale;float4x4 projectionMatrix;sampler2D depthSampler : register( s2 );struct VS_INPUT{    float4 position : POSITION0;		//pre-transformed vertex positions     float2 texPos : TEXCOORD0;			//texture coordinates    float3 viewDirection : TEXCOORD1;	//location of corresponding furstum corner (vertex location in eye-space)};struct VS_OUTPUT{    float4 position : POSITION0;    float2 texPos : TEXCOORD0;    float3 viewDirection : TEXCOORD1;};struct PS_OUTPUT{    float4 color : COLOR0;};void vs (in VS_INPUT IN, out VS_OUTPUT OUT){    //just pass through the data from the vertex    OUT.position = IN.position;    OUT.texPos = IN.texPos;    OUT.viewDirection = IN.viewDirection;}void ps (in VS_OUTPUT IN, out PS_OUTPUT OUT){    //reconstruct eye-space position from the depth buffer    float pixelDepth = tex2D(depthSampler, IN.texPos).r;    float3 pixelPosEyeSpace = pixelDepth * IN.viewDirection;    //loop through our sample locations, accumulating the occlusion    float result = 0.0f;    for (int i = 0; i < NUM_SAMPLE_POINTS; i++)    {        //determine the eye-space and clip-space locations of our current sample point	float4 samplePointEyeSpace = float4(pixelPosEyeSpace + (sampleOffsets * sampleRadius), 1.0f);        float4 samplePointClipSpace = mul(samplePointEyeSpace, projectionMatrix);	//determine the texture coordinate of our current sample point	float2 sampleTexCoord = 0.5f * samplePointClipSpace.xy/samplePointClipSpace.w + float2(0.5f, 0.5f);	//Flip around the y-coordinate and offset by half a pixel	//This is for Direct3D9 only!!!!  Not necessary in OpenGL	sampleTexCoord.y = 1.0f - sampleTexCoord.y;	float2 offset = 0.5f / depthBufferDimensions;	sampleTexCoord -= offset;        	//read the depth of our sample point from the depth buffer        float sampleDepth = tex2D(depthSampler, sampleTexCoord);	//compute our occulusion factor        float occlusionFactor = distanceScale* max(pixelDepth - sampleDepth, 0.0f);        result += 1.0f / (1.0f + occlusionFactor * occlusionFactor);   }   OUT.color = float4(result/NUM_SAMPLE_POINTS, 1.0f, 1.0f, 1.0f);}
draktheas
draktheas
Ok, based on the other thread that MJP posted a link to (thanks MJP), I have attempted my own version of SSAO. I am getting very strange artifacts and I am sure it has to do with my far frustum corner calculation. Any help would be appreciated.

Here is the rendermonkey project:
http://www.2shared.com/file/3086783/5b84fc90/ssao.html

Thanks,
Drak


MJP
MJP
I don't have rendermonkey on this computer so I can't look at your project, but I can tell you how I reconstruct position from depth.

Like it says in the comments, viewDirection is the location of corresponding corner of the view frustum, in view space. This value is calculated from the parameters I use to create my perspective projection matrix, which are the vertical fov, aspect ratio, distance to the near clip plane, and distance to the far clip plane. The code looks something like this, with index 0 of the array being the top left corner and then going clockwise:

float farY = tan(fov / 2) * farZ;float farX = farY * aspectRatio;frustumCorners[0] = {-farX, farY, farZ};frustumCorners[1] = {farX, farY, farZ};frustumCorners[2] = {farX, -farY, farZ};frustumCorners[3] = {-farX, -farY, farZ};


My depth buffer is created by storing linearized view-space Z, which is calculated like this:

// Vertex shaderOUT.vPositionVS = mul( IN.vPositionOS, matWorldView );// Pixel shaderOUT.depth = IN.vPositionVS.z / fFrustumFarZ;
doronf
doronf
My tip is to avoid the transformation into viewspace (eyespace).
At first, I tried to use Inigo's method, but after understanding what the nature of the calculation is, I decided to just do it in projected space. My best explenation for this choice is: you won't gain any additional information in viewspace, so there is no point in the additional transformation. By avoiding the projection to viewspace the amound of code is reduced (which is always a good thing).
You can see my implementation in the image of the day section and decide for yourself if the implementation in projectedspace is good enough or not.
MJP
MJP
Quote:
Original post by doronf
My tip is to avoid the transformation into viewspace (eyespace).
At first, I tried to use Inigo's method, but after understanding what the nature of the calculation is, I decided to just do it in projected space. My best explenation for this choice is: you won't gain any additional information in viewspace, so there is no point in the additional transformation. By avoiding the projection to viewspace the amound of code is reduced (which is always a good thing).
You can see my implementation in the image of the day section and decide for yourself if the implementation in projected space is good enough or not.


Not performing your calculations in linear space means that the parameters you use will produce different results depending on the size of the projection volume and where you're sampling within your volume. IMO it's not worth saving the shader math, especially for complex scenes.


Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.