Артефакты SSAO в Трех
Я действительно изо всех сил пытаюсь решить проблему с моим шейдером SSAO и могу отчаянно использовать некоторую помощь. В основном шейдер, кажется, работает с некоторыми объектами, но на других выглядит очень плохо. Снизу вы можете видеть, что сфера выглядит правильно, но куб, кажется, делает окклюзию нормалей, чего не должно быть. Вот скриншот:
Я основываю свои шейдеры на этом уроке: http://devmaster.net/posts/3095/shader-effects-screen-space-ambient-occlusion
В моей цепочке рендеринга я отрисовываю 2 цели рендеринга, которые позже используются в некоторых пост-процессных эффектах. Один из них хранит положение и глубину. Другой хранит нормали. Обе цели являются плавающими текстурами.
Вот скриншот каждого из шагов.
Это моя позиция / глубина шейдера:
Вершинный шейдер:
varying vec4 vPosition;
.....
vPosition = mvPosition;
Frag Shader:
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;
}
1 ответ
Похоже, что нормали не перпендикулярны поверхности куба, это может быть проблемой shading
параметр материала.
Попробуйте установить THREE.FlatShading
парам для shading
вариант для материала.