Alright, so I've got my UV coordinates for the full screen quad (dividing the SV_Position variable by the screen size), and am able to get the algorithm pretty much working. I switched over my map generation shader to render to multiple render targets, one for position, normals, and depth. My problem is now that when I calculate the AO factor, the screen seems to be split in 4 quadrants, and the render fades almost completely to black the further you go down the screen. I'll post screenshots once I get back to my computer.
EDIT: I just did some more testing. It appears that the resulting SSAO map changes in strange ways depending on what color I use to clear the pos/normal maps before rendering to them (green was what I was experiencing the problem I described above with). It looks like clearing with black is the right way to go. Now I'm noticing a lot of linear artefacting. Is there really any way to fix this outside of fully uv unwrapping the model? I attached screenshots below.
Here is my shader to generate the maps:
matrix MatrixPalette[255];
matrix worldMatrix;
matrix viewMatrix;
//Vertex Input
struct VS_INPUT_SKIN
{
float4 position : POSITION;
float3 normal : NORMAL;
float2 tex0 : TEXCOORD;
float3 boneLinks : BONELINKS;
};
struct SSAO_PS_INPUT
{
float4 pos : SV_POSITION;
float3 actPos : TEXCOORD0;
float3 normal : TEXCOORD1;
float depth : TEXCOORD2;
};
struct SSAO_PS_OUTPUT
{
float4 posMap : SV_Target0;
float4 normalMap : SV_Target1;
float4 depthMap : SV_Target2;
};
float4 skinVert(float4 vert, float fact, matrix bone1, matrix bone2)
{
float4 p = float4(0.0f, 0.0f, 0.0f, 1.0f);
//vertex skinning
float bone1Weight = fact;
float bone2Weight = 1.0f - bone1Weight;
p += bone1Weight * mul(vert,bone1);
p += bone2Weight * mul(vert,bone2);
p.w = 1.0f;
return p;
}
float3 skinNorm(float3 vert, float fact, matrix bone1, matrix bone2)
{
float3 norm = float4(0.0f, 0.0f, 0.0f, 1.0f);
float bone1Weight = fact;
float bone2Weight = 1.0f - bone1Weight;
norm += bone1Weight * mul(vert,bone1);
norm += bone2Weight * mul(vert,bone2);
norm = normalize(norm);
return norm;
}
SSAO_PS_INPUT vs_SSAO(VS_INPUT_SKIN IN)
{
SSAO_PS_INPUT OUT;
float4 skinnedPos = skinVert(IN.position, IN.boneLinks[2], MatrixPalette[IN.boneLinks[0]], MatrixPalette[IN.boneLinks[1]]);
float3 skinnedNormal = skinNorm(IN.normal, IN.boneLinks[2], MatrixPalette[IN.boneLinks[0]], MatrixPalette[IN.boneLinks[1]]);
float4 worldPos = mul(skinnedPos, worldMatrix);
OUT.pos = mul(worldPos, viewMatrix);
OUT.pos = mul(OUT.pos, projMatrix);
OUT.actPos = mul(worldPos, viewMatrix);
OUT.normal = mul(skinnedNormal, worldMatrix);
OUT.normal = mul(OUT.normal, viewMatrix);
OUT.depth = normalize(mul(worldPos, viewMatrix).z);
return OUT;
}
SSAO_PS_OUTPUT ps_SSAO(SSAO_PS_INPUT IN) : SV_Target
{
SSAO_PS_OUTPUT OUT;
OUT.posMap = float4(IN.actPos, 1);
OUT.normalMap = float4(IN.normal, 1);
OUT.depthMap = float4(IN.depth, IN.depth, IN.depth, 1);
return OUT;
}
technique10 SSAO_T
{
pass P0
{
SetVertexShader( CompileShader( vs_4_0, vs_SSAO() ) );
SetPixelShader( CompileShader( ps_4_0, ps_SSAO() ) );
SetGeometryShader( NULL );
}
}
And here is my shader to generate the AO map:
matrix MatrixPalette[255];
matrix worldMatrix;
matrix viewMatrix;
matrix projMatrix;
matrix lightViewMatrix;
float2 texelSize;
Texture2D posMap;
Texture2D normalMap;
Texture2D depthMap;
Texture2D randomTexture;
SamplerState DifferredSampler
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Clamp;
AddressV = Clamp;
};
struct VS_OUTPUT
{
float4 position : SV_POSITION;
float3 normal : NORMAL;
};
float3 getPosition(in float2 uv)
{
return posMap.Sample(DifferredSampler, uv).xyz;
}
float3 getNormal(in float2 uv)
{
return normalize(normalMap.Sample(DifferredSampler, uv).xyz * 2.0f - 1.0f);
}
float getDepth(in float2 uv)
{
return depthMap.Sample(DifferredSampler, uv).w;
}
float3 getRandom(in float2 uv)
{
return randomTexture.Sample(DifferredSampler, texelSize*uv/float2(64,64)).xyz * 2.0f - 1.0f;
}
float g_sample_rad = 3.0f;
float g_intensity = 3.0f;
float g_scale = 1.0f;
float g_bias = 0.001f;
float doAmbientOcclusion(in float2 tcoord,in float2 uv, in float3 p, in float3 cnorm)
{
float3 diff = getPosition(tcoord + uv) - p;
const float3 v = normalize(diff);
const float d = length(diff)*g_scale;
return max(0.0,dot(cnorm,v)-g_bias)*(1.0/(1.0+d))*g_intensity;
}
float4 getOcclusion(float2 uv)
{
float4 o;
o.rgb = 1.0f;
o.a = 1.0f;
const float2 vec[4] = {float2(1,0),float2(-1,0),
float2(0,1),float2(0,-1)};
float3 p = getPosition(uv);
float3 n = getNormal(uv);
float2 rand = getRandom(uv);
float ao = 0.0f;
float rad = g_sample_rad/p.z;
int iterations = 4;
for (int j = 0; j < iterations; ++j)
{
float2 coord1 = reflect(vec[j],rand)*rad;
float2 coord2 = float2(coord1.x*0.707 - coord1.y*0.707,
coord1.x*0.707 + coord1.y*0.707);
ao += doAmbientOcclusion(uv,coord1*0.25, p, n);
ao += doAmbientOcclusion(uv,coord2*0.5, p, n);
ao += doAmbientOcclusion(uv,coord1*0.75, p, n);
ao += doAmbientOcclusion(uv,coord2, p, n);
}
ao/=(float)iterations*4.0;
o.rgb = ao;
return o;
}
float4 ps_lighting(VS_OUTPUT IN) : SV_Target
{
float4 ao = float4(1.0f, 1.0f, 1.0f, 1.0f);
float2 uv = IN.position.xy;
uv.x /= texelSize[0];
uv.y /= texelSize[1];
ao = getOcclusion(uv);
return ao;
}
VS_OUTPUT vs_Skinning(VS_INPUT_SKIN IN)
{
VS_OUTPUT OUT = (VS_OUTPUT)0;
float4 p = float4(0.0f, 0.0f, 0.0f, 1.0f);
float3 norm = float3(0.0f, 0.0f, 0.0f);
//vertex skinning
float bone1Weight = IN.boneLinks[2];
float bone2Weight = 1.0f - bone1Weight;
p += bone1Weight * mul(IN.position,MatrixPalette[IN.boneLinks[0]]);
p += bone2Weight * mul(IN.position,MatrixPalette[IN.boneLinks[1]]);
p.w = 1.0f;
norm += bone1Weight * mul(IN.normal,MatrixPalette[IN.boneLinks[0]]);
norm += bone2Weight * mul(IN.normal,MatrixPalette[IN.boneLinks[1]]);
norm = normalize(norm);
norm = mul(norm, worldMatrix);
OUT.normal = normalize(mul(norm, lightViewMatrix));
//move pos to worldviewproj space
float4 worldPos = mul(p, worldMatrix);
OUT.position = mul(worldPos, viewMatrix);
OUT.position = mul(OUT.position, projMatrix);
return OUT;
}
Position map with buffer cleared to black:
Normal map with buffer cleared to black:
Depth map with buffer cleared to black:
Resulting SSAO map with all buffers cleared to green:
Resulting SSAO map with all buffers cleared to white:
Resulting SSAO map with all buffers cleared to black: