【问题标题】:SSAO artefacts in ThreeSSAO 文物三
【发布时间】:2014-02-10 23:11:19
【问题描述】:

我真的很难解决我的 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;

}

【问题讨论】:

  • 您的法线缓冲区在立方体所在的位置看起来不太好。立方体上应该只有三种颜色,每边一种,绿色为向上,红色为右侧,蓝色为正面。修复它。
  • 感谢抽象算法。我想这可能是正常的。我一直在网上搜索正确的方法,但我找不到任何似乎有效的方法。你会碰巧知道着色器可能是什么吗?我基于三个正常着色器的正常着色器。即 vNormal = normalize( normalMatrix * normal );除了我使用了转换的法线,因为对象可能有变形或皮肤。
  • 看起来不错。也许normalMatrix 错了?尝试将 normal 与没有缩放和没有平移的 MVMatrix 相乘。此外,Three.js 中有一个选项 - THREE.FlatShading 模式用于材料的shading 参数,也许这会有所帮助。
  • 嗨抽象算法;经过一些测试,它确实似乎是阴影。当我使用平面着色时,它看起来好多了。你知道我怎么能克服这个吗?我有一个场景需要平滑法线的多个对象。我不能只应用法线材质而不必每次都重新生成法线。 IE。 material.shading = THREE.FlatShading 将不起作用,除非我每次通过都重新生成法线(场景从 phong 切换到普通着色器)
  • 必须有办法避免每次都重新生成法线。我不确定如何使用 Three.js 来完成。此外,使用更少的通道,例如,您可以在一个通道中存储法线+深度。对于 re-gen,@WestLangley 可能会有所帮助。

标签: opengl-es three.js glsl ssao


【解决方案1】:

看起来法线不垂直于立方体的表面,这可能是来自shading材质参数的问题。

尝试为材料的shading 选项设置THREE.FlatShading 参数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多