我真的很难解决我的 SSAO 着色器的问题,并且可以拼命地使用一些帮助。基本上,着色器似乎适用于某些对象,但在其他对象上看起来非常糟糕。从下面您可以看到球体看起来是正确的,但立方体似乎在法线上进行了不应该遮挡的遮挡。这是一个屏幕截图:
我的着色器基于本教程: http ://devmaster.net/posts/3095/shader-effects-screen-space-ambient-occlusion
在我的渲染链中,我渲染了 2 个渲染目标,这些目标稍后会在一些后期处理效果中使用。其中之一存储位置和深度。另一个存储法线。两个目标都是浮动纹理。
这是每个步骤的屏幕截图。
这是我的位置/深度着色器:
顶点着色器:
varying vec4 vPosition;
.....
vPosition = mvPosition;
片段着色器:
uniform float linearDepth; //Cam far - cam near
varying vec4 vPosition;
...
float ld = length(vPosition) / linearDepth;
gl_FragColor = vec4(vPosition.x, vPosition.y, vPosition.z, ld);
这是普通的着色器:
顶点着色器:
varying vec3 vNormal;
...
vec3 transformedNormal = normalMatrix * objectNormal;
vNormal = transformedNormal;
片段着色器:
gl_FragColor = vec4( normalize( vNormal ).xyz, 1.0);
这是我的 SSAO 片段着色器:
uniform sampler2D tDiffuse; // The original scene texture
uniform sampler2D tPositions; // View space position data
uniform sampler2D tNormals; // View space normal vectors
uniform sampler2D tNoise; // Normalmap to randomize the sampling kernel
uniform vec2 texelSize;
/// Occluder bias to minimize self-occlusion.
uniform float occluderBias;
/// Specifies the size of the sampling radius.
uniform float samplingRadius;
uniform float onlyAO;
/// <summary>
/// Ambient occlusion attenuation values.
/// These parameters control the amount of AO calculated based on distance
/// to the occluders. You need to play with them to find the right balance.
///
/// .x = constant attenuation. This is useful for removing self occlusion. When
/// set to zero or a low value, you will start to notice edges or wireframes
/// being shown. Typically use a value between 1.0 and 3.0.
///
/// .y = linear attenuation. This provides a linear distance falloff.
/// .z = quadratic attenuation. Smoother falloff, but is not used in this shader.
/// <summary>
uniform vec2 attenuation;
/// <summary>
/// Varying variables.
/// <summary>
varying vec2 vUv;
/// <summary>
/// Sample the ambient occlusion at the following UV coordinate.
/// <summary>
/// <param name=srcPosition>3D position of the source pixel being tested.</param>
/// <param name=srcNormal>Normal of the source pixel being tested.</param>
/// <param name=uv>UV coordinate to sample/test for ambient occlusion.</param>
/// <returns>Ambient occlusion amount.</returns>
float SamplePixels (vec3 srcPosition, vec3 srcNormal, vec2 uv)
{
// Get the 3D position of the destination pixel
vec3 dstPosition = texture2D(tPositions, uv).xyz;
// Calculate ambient occlusion amount between these two points
// It is simular to diffuse lighting. Objects directly above the fragment cast
// the hardest shadow and objects closer to the horizon have minimal effect.
vec3 positionVec = dstPosition - srcPosition;
float intensity = max(dot(normalize(positionVec), srcNormal) - occluderBias, 0.0);
// Attenuate the occlusion, similar to how you attenuate a light source.
// The further the distance between points, the less effect AO has on the fragment.
float dist = length(positionVec);
float attenuation = 1.0 / (attenuation.x + (attenuation.y * dist));
return intensity * attenuation;
}
/// <summary>
/// Fragment shader entry.
/// <summary>
void main ()
{
// Get position and normal vector for this fragment
vec3 srcPosition = texture2D(tPositions, vUv).xyz;
vec3 srcNormal = texture2D(tNormals, vUv).xyz;
vec2 randVec = normalize(texture2D(tNoise, vUv).xy * 2.0 - 1.0);
float srcDepth = texture2D(tPositions, vUv).w;
// The following variable specifies how many pixels we skip over after each
// iteration in the ambient occlusion loop. We can't sample every pixel within
// the sphere of influence because that's too slow. We only need to sample
// some random pixels nearby to apprxomate the solution.
//
// Pixels far off in the distance will not sample as many pixels as those close up.
float kernelRadius = samplingRadius * (1.0 - srcDepth);
// Sample neighbouring pixels
vec2 kernel[4];
kernel[0] = vec2(0.0, 1.0); // top
kernel[1] = vec2(1.0, 0.0); // right
kernel[2] = vec2(0.0, -1.0); // bottom
kernel[3] = vec2(-1.0, 0.0); // left
const float Sin45 = 0.707107; // 45 degrees = sin(PI / 4)
// Sample from 16 pixels, which should be enough to appromixate a result. You can
// sample from more pixels, but it comes at the cost of performance.
float occlusion = 0.0;
for (int i = 0; i < 4; ++i)
{
vec2 k1 = reflect(kernel[i], randVec);
vec2 k2 = vec2(k1.x * Sin45 - k1.y * Sin45,
k1.x * Sin45 + k1.y * Sin45);
k1 *= texelSize;
k2 *= texelSize;
occlusion += SamplePixels(srcPosition, srcNormal, vUv + k1 * kernelRadius);
occlusion += SamplePixels(srcPosition, srcNormal, vUv + k2 * kernelRadius * 0.75);
occlusion += SamplePixels(srcPosition, srcNormal, vUv + k1 * kernelRadius * 0.5);
occlusion += SamplePixels(srcPosition, srcNormal, vUv + k2 * kernelRadius * 0.25);
}
// Average and clamp ambient occlusion
occlusion /= 16.0;
occlusion = clamp(occlusion, 0.0, 1.0);
// Blend the two
vec3 colour = texture2D(tDiffuse, vUv).xyz;
//colour = clamp(colour - occlusion, 0.0, 1.0);
occlusion = 1.0 - occlusion;
//gl_FragColor.xyz = pow(colour, vec3(1.0 / 2.2));
if ( onlyAO == 1.0 )
gl_FragColor.xyz = vec3( occlusion, occlusion, occlusion );
else if ( onlyAO == 2.0 )
gl_FragColor.xyz = vec3( srcNormal.x, srcNormal.y, srcNormal.z );
else if ( onlyAO == 3.0 )
gl_FragColor.xyz = vec3( srcDepth, srcDepth, srcDepth );
else
{
//// Blend the two
//colour = clamp(colour - occlusion, 0.0, 1.0);
//// Apply gamma correction
//gl_FragColor.xyz = pow(colour, vec3(1.0 / 2.2));
//gl_FragColor.w = 1.0;
gl_FragColor.xyz = colour * occlusion;
}
gl_FragColor.w = 1.0;
}