Another SSAO thread.

Started by
39 comments, last by Cineska 15 years, 9 months ago
Hello, I'm having some serious trouble getting this to work. I know you all must be sick and tired of SSAO questions, I have searched the forum and read as many of the related threads as I could find. I've also googled, downloaded samples etc. After 5 days I'm still pretty much nowhere. All I seem to be able to produce is noisy depth. If somebody could be so kind as to run over my shaders and point out what a moron I'm being I would be most greatful. Thanks. Result of Depth: http://azazeldev.org/ssao/ssao_depth.jpg Result of SSAO : http://azazeldev.org/ssao/wrong_ssao.jpg Depth shader:


        float4x4 wViewProj: WORLDVIEWPROJ;
        
        struct a2v
        {
                float4 Position: POSITION0;
                float3 Normal  : NORMAL;
        };

        struct v2f
        {
                float4 Position: POSITION0;
                float3 Test    : TEXCOORD0;
                float3 Normal  : TEXCOORD1;
        };
        
        struct f2s
        {
                float4 Colour  : COLOR0;
        };
        
        void vp(in a2v IN, out v2f OUT)
        {
                OUT.Position = mul(IN.Position, wViewProj);
                OUT.Test     = OUT.Position;
                OUT.Normal   = IN.Normal;
        }
        
        void fp(in v2f IN, out f2s OUT)
        {
                float  Deep = IN.Test.z / 50;
                float3 N    = normalize(IN.Normal);
                OUT.Colour = float4(Deep, 0, 0, 1);
        }
        
        technique
        {
                pass Pass0
                {
                        VertexShader = compile vs_1_1 vp();
                        PixelShader  = compile ps_2_0 fp();
                }
        }

SSAO shader:


//Direction variable in shader is a result of:

	cTV_3DVECTOR CalculateCorner()
	{
		float FarY = tanf(45 / 2) * 5000;
		float FarX = FarY * (1024 / 768);
		return Vector3(FarX, FarY, 5000);
	}

//----------------------------
        float4x4 Projection: PROJECTION;

        texture  Deepa;
        texture  Rand;
        float    SampleRadius  = 2.5;
        float    DistanceScale = 500;
        float3   Direction;

        sampler2D DeepSample = sampler_state
        {
	       Texture       = (Deepa);
        };

        sampler2D RandSample = sampler_state
        {
	       Texture       = (Rand);
        };

        struct app2vp
        {
                float4 Position: POSITION;
                float2 UV      : TEXCOORD0;
        };

        struct vp2fp
        {
                float4 Position: POSITION;
                float2 UV      : TEXCOORD0;
        };

        struct fp2s
        {
	       float4 Colour  : COLOR0;
        };


        void vp( in app2vp IN, out vp2fp OUT )
        {
                OUT.Position    = IN.Position;
                OUT.UV          = IN.UV;
        }

        void fp( in vp2fp IN, out fp2s OUT )
        {
                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  fColour     = 0.0f;
                
                float  Deep        = tex2D(DeepSample, IN.UV).r;
                float3 Norm        = tex2D(RandSample, IN.UV * 200).rgb;
                
                float3 vDir        = normalize(Direction);
                float3 SE          = Deep * vDir;//float3(Deep, Deep, Deep);
                
                for(int i = 0; i < 16; i++)
                {
                        float3 Ray    = reflect(Samples.xyz, Norm) * SampleRadius;
                        
                        float4 Sample = float4(SE + Ray, 1);
                        float4 SS     = mul(Sample, Projection);
                        
                        float2 sUV    = 0.5 * (SS.xy/SS.w) + float2(0.5, 0.5);
                        sUV.x        += 1/1024;
                        sUV.y        += 1/768;
                        
                        float sDeep   = tex2D(DeepSample, sUV).r;

                        //This was originally max(sDeep - Deep, 0);
                        float Occlude = DistanceScale * min(sDeep - Deep, 0);
                        fColour      += 1 / (1 + Occlude * Occlude * 0.1);
                }

//                fColour           = fColour/16;
                OUT.Colour        = float4(fColour, fColour, fColour, 1);
        }

        technique null
        {
                pass Pass0
                {
                        VertexShader = compile vs_3_0 vp();
                        PixelShader = compile ps_3_0 fp();
                }
        }

-...-Squeeged third eye.
Advertisement
In your depth shader, you're not calculating view-space depth correctly. You're calculating the positing and clip-space and taking the z-component (which is in non-linear space) and outputting that. You want this:

float4x4 worldView : WORLDVIEW:float4x4 wViewProj: WORLDVIEWPROJ;                        void vp(in a2v IN, out v2f OUT)        {                OUT.Position = mul(IN.Position, wViewProj);                OUT.Test     = mul(IN.Position, worldView);                OUT.Normal   = IN.Normal;        }


You're also not using the frustum corner correctly. What you want is in the vertex shader of each quad, you want to pass the position of the corresponding frustum corner (top left, bottom right, whatever) on to the pixel shader so th at it gets interpolated. Something like this should do the trick:

        struct app2vp        {                float4 Position: POSITION;                float2 UV      : TEXCOORD0;        };        struct vp2fp        {                float4 Position: POSITION;                float2 UV      : TEXCOORD0;                float3 frustumCorner : TEXCOORD1;        };        void vp( in app2vp IN, out vp2fp OUT )        {                OUT.Position    = IN.Position;                OUT.UV          = IN.UV;                float2 screenPos = IN.UV;                      screenPos.Y = 1.0f - screenPos.Y;                screenPos = screenPos * 2 - 1;                frustumCorner = float3(screenPos, 1.0f) * Direction;        }        void fp( in vp2fp IN, out fp2s OUT )        {                float  Deep        = tex2D(DeepSample, IN.UV).r;                               float3 SE          = Deep * IN.frustumCorner;                        ...        }


Also...you're using a value of "5000" as the distance to your far-clip plane in your code demonstrating how you calculate the frustum corner, but dividing the depth by "50" in your depth shader. Make sure you use the same value for both.
Thanks MJP. I've done what you said and my results are still not correct at all. I'm not sure what's going on here really. Everything I've read says it should work. After adding the new code I've tried to tweak it in to working but with no luck. I changed the depth shader and that seems to be working correctly. Is there anything else here you can see that looks wrong?

Here is the shader in it's current incarnation:

//Not sure about this. Should it be the projection, view projection, world view projection?//I've tried changing it around, but it's niggling me because I'm unsure.        float4x4 Projection: PROJECTION;        texture  Deepa;        texture  Rand;        float    SampleRadius  = 2.5;        float    DistanceScale = 500;        float3   Direction;        sampler2D DeepSample = sampler_state        {	       Texture       = (Deepa);        };        sampler2D RandSample = sampler_state        {	       Texture       = (Rand);        };        struct app2vp        {                float4 Position: POSITION;                float2 UV      : TEXCOORD0;        };        struct vp2fp        {                float4 Position: POSITION;                float2 UV      : TEXCOORD0;                float3 Corner  : TEXCOORD1;        };        struct fp2s        {	       float4 Colour  : COLOR0;        };        void vp( in app2vp IN, out vp2fp OUT )        {                OUT.Position     = IN.Position;                OUT.UV           = IN.UV;                                float2 ScreenPos = IN.UV;                ScreenPos.y      = 1.0f - ScreenPos.y;                ScreenPos        = ScreenPos * 2 - 1;                OUT.Corner       = float3(ScreenPos, 1.0f) * Direction;        }        void fp( in vp2fp IN, out fp2s OUT )        {                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  fColour     = 0.0f;                                float  Deep        = tex2D(DeepSample, IN.UV).r;                float3 Norm        = tex2D(RandSample, IN.UV * 200).rgb;                float3 SE          = Deep * IN.Corner;                                for(int i = 0; i < 16; i++)                {                        float3 Ray    = reflect(Samples.xyz, Norm) * SampleRadius;                                                float4 Sample = float4(Ray + SE, 1);                        float4 SS     = mul(Sample, Projection);                                                float2 sUV    = 0.5 * (SS.xy/SS.w) + float2(0.5, 0.5);                                                float sDeep   = tex2D(DeepSample, sUV).r;                        float Occlude = DistanceScale * max(sDeep - Deep, 0);                        fColour      += 1 / (1 + (Occlude * Occlude) * 0.1);                }                fColour           = fColour/16;                OUT.Colour        = float4(fColour, fColour, fColour, 1);        }        technique null        {                pass Pass0                {                        VertexShader = compile vs_3_0 vp();                        PixelShader = compile ps_3_0 fp();                }        }
-...-Squeeged third eye.
Output your view-space position ("SE", I think you call it ) as your final SSAO, what does this look like? Now normalize it and output it again, I want to see that, too. (screenshots, please)

Either that, or your projection is wrong. I don't see any obvious problems otherwise. Projection matrix should be purely projection, you're already in eye space.
SE: http://azazeldev.org/ssao/se_out.jpg
SE Normalized: http://azazeldev.org/ssao/se_out_normalized.jpg

It just seems to dump blue if it is normalized.
-...-Squeeged third eye.
Yup, your view-space position is being constructed incorrectly. Let me get you a screen shot of what the view-space position should look like:
view-space position
(ignore the cross-hair [grin])

Seems like the blue is caused by the depth, and your actual frustum corners are incorrect (or the XY portion of the view-space depth, for some odd reason). The reason I wanted the normalized position is because that'll remove the depth-factor showing above, showing if your frustum corners are constructed correctly (if they are, you'll get flat top-left green, flat top-right yellow, flat bottom-right red, and flat bottom-left black).

There's that, and there's your SSAO calculations - you're comparing normalized depth values constructed from the eye-space Z coordinate. What you want for SSAO is non-normalized depth values construct from the eye-space vector length (distance to the camera), not the Z coordinate itself.

Try storying length(viewSpacePosition) as your depth (don't divide by anything), that'll greatly simplify many things. Then just multiply this by the normalized direction to the interpolated frustum corner. This way you have the correct depth out of the box, and there is no messing with the far frustum values, or XY coordinates, or anything:
outputDepth = length(viewSpacePosition.xyz);viewSpacePosition = normalize(directionToFrustumCorners) * inputDepth;
Wow, 6 days now. Well, it turned out I had a few things wrong - but the major thing was the corner. Thanks Agi, once we had that breakthrough everyone on IRC rallied together and started throwing out idea. The other things wrong were, in my calculation I was using tanf - but with degrees!( dumb thing I know, didn't even give it a thought ) and also the sample V co-ord needed a 1- :).

The result is a little odd, I need to flip the colouring I think. But I think there is definite occlusion here: http://azazeldev.org/ssao/new.jpg

Thanks guys, I was going out of my mind trying to get this to work. :)

Edit: fixed the colouration, see: http://azazeldev.org/ssao/yay_yay_yay.jpg

Thanks again guys.
-...-Squeeged third eye.
Quote:Original post by AriusMyst
Wow, 6 days now. Well, it turned out I had a few things wrong - but the major thing was the corner. Thanks Agi, once we had that breakthrough everyone on IRC rallied together and started throwing out idea. The other things wrong were, in my calculation I was using tanf - but with degrees!( dumb thing I know, didn't even give it a thought ) and also the sample V co-ord needed a 1- :).

Hah, I noticed that but didn't pick it up as an error [grin]. I'm a bit overly protected by my libraries, I don't do any other internal math, just call Camera::getFrustumCorners() [lol] (so I had no idea if your code was correct either way)
Quote:
The result is a little odd, I need to flip the colouring I think. But I think there is definite occlusion here: http://azazeldev.org/ssao/new.jpg

Thanks guys, I was going out of my mind trying to get this to work. :)

Edit: fixed the colouration, see: http://azazeldev.org/ssao/yay_yay_yay.jpg

Thanks again guys.

Excellent, have fun and good luck. Just a few tips:
- use a quadratic falloff for the SSAO, it tends to limit the bad ghosting a lot more compared to the linear one you seem to be using right now (never mind, read your code incorrectly)
- bias the random direction by the current point's normal - this moves the sampling to areas less likely to be occluded, meaning little (or no) self-occlusion (which is what you're getting on those walls and cubes, the flat and curved surfaces are getting ghost occlusions of themselves) [you don't need a normal texture, just reconstruct the normals from your position by crossing ddx and ddy of the position, as was solved in my thread [wink]]
I have also been having a problem with this and I can not seem to figure out why. I noticed in most examples that the depth only pass and just rendering that texture shows red (since it is just writing to the red component), however, I get blue and not figure out why. It also seems to depend on the camera position for some reason. Does anyone happen to know why?

Shader Code:
float4x4 worldViewProjectionMatrix;float4x4 worldMatrix;texture tDepthTexture;sampler2D sDepth = sampler_state{	Texture = <tDepthTexture>;   	MagFilter = NONE;    	MinFilter = NONE;    	MipFilter = NONE;};struct VS_INPUT{	float3 position : POSITION;	float2 texCoord : TEXCOORD0;	float3 normal : NORMAL;};struct VS_OUTPUT{  	float4 position : TEXCOORD0;	float4 depthPosition : TEXCOORD1;	float2 texCoord : TEXCOORD2;};VS_OUTPUT VS(VS_INPUT IN, out float4 outPos : POSITION){	VS_OUTPUT OUT;	outPos = mul(float4(IN.position, 1.0f), worldViewProjectionMatrix);      	OUT.position = outPos;	OUT.depthPosition = mul(float4(IN.position, 1.0f), worldMatrix);  	OUT.texCoord = IN.texCoord;        	return OUT;}float4 DepthPass(VS_OUTPUT IN) : COLOR{	float fDC = IN.depthPosition.z / IN.depthPosition.w;	return float4(fDC, 0.0f, 0.0f, 1.0f);}float4 RegularPass(VS_OUTPUT IN) : COLOR{	float4 ambientColor = tex2D(sDepth, IN.texCoord);	return ambientColor;}technique DepthPass{	pass	{		VertexShader = compile vs_2_0 VS();		PixelShader = compile ps_2_0 DepthPass();	}}technique AfterAmbient{	pass	{		VertexShader = compile vs_2_0 VS();		PixelShader = compile ps_2_0 RegularPass();	}}


Render Code (general algorithm)
	DXWrapper->Clear(179, 255, 128);	DXWrapper->DeviceBegin();	LPDIRECT3DDEVICE9 pD3Dev = CDirect3D::GetInstance()->GetDirect3DDevice();	// Depth	m_DXWrapper->GetDirect3DDevice()->GetRenderTarget(0,    &m_pBackbuffer);	LPDIRECT3DSURFACE9 pSurface = NULL;	m_pRenderTarget->GetSurfaceLevel(0, &pSurface);	pD3Dev->SetRenderTarget(0,    pSurface);	pSurface->Release();	// Render Everything	m_pOM->RenderAll();	m_DXWrapper->DeviceEnd();	pD3Dev->SetRenderTarget(0, m_pBackbuffer);	m_pBackbuffer->Release();	m_pOM->m_tDepthTexture = m_pRenderTarget;	// Second Pass (After Depth)        DXWrapper->Clear(0,0,128);	DXWrapper->DeviceBegin();	// Render Everything	m_pOM->RenderAll(false);	// False just tells it to render the depth texture. Changes the technique to be used	DXWrapper->DeviceEnd();	DXWrapper->Present();


pD3Dev->CreateTexture(nWidth, nHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &m_pRenderTarget, NULL);
Is how I make my render target also.
Well, I can show you mine, maybe that will help. :). Ignore the normals dump, I'm doing that for my blur shader( which is weird atm, but heh :D ).

        float4x4 World    : WORLDVIEW;        float4x4 wViewProj: WORLDVIEWPROJ;                struct a2v        {                float4 Position: POSITION0;                float3 Normal  : NORMAL;        };        struct v2f        {                float4 Position: POSITION0;                float  Test    : TEXCOORD0;                float3 Normal  : TEXCOORD1;        };                struct f2s        {                float4 Colour  : COLOR0;        };                void vp(in a2v IN, out v2f OUT)        {                OUT.Position = mul(IN.Position, World);                OUT.Test     = length(OUT.Position.xyz);                OUT.Position = mul(IN.Position, wViewProj);                OUT.Normal   = IN.Normal;        }                void fp(in v2f IN, out f2s OUT)        {                float  Deep = IN.Test;                float3 N    = normalize(IN.Normal);                OUT.Colour = float4(N.r, N.g, N.b, Deep);        }                technique        {                pass Pass0                {                        VertexShader = compile vs_1_1 vp();                        PixelShader  = compile ps_2_0 fp();                }        }
-...-Squeeged third eye.

This topic is closed to new replies.

Advertisement