如何提高阴影的质量?

时间:2012-03-24 18:39:20

标签: opengl rendering

首先是截图:

正如你所看到的,阴影的顶部看起来还不错(如果你看看灌木顶部投射的污垢,它看起来或多或少是正确的),但阴影的底部是偏离的。 / p>

图像的左下角显示了我计算的阴影贴图。它是光的POV的深度图,也是我的角色站立的地方。

这是另一个镜头,从不同的角度来看:

任何想法可能会导致它像这样出现?灌木面的深度是否与其正后方的地面深度相似?如果是这样,我该如何解决这个问题?

我会在下面发布片段着色器,如果还有其他需要看的话,请留言。

片段着色器

#version 330

in vec2 TexCoord0;
in vec3 Tint0;
in vec4 WorldPos;
in vec4 LightPos;

out vec4 FragColor;

uniform sampler2D TexSampler;
uniform sampler2D ShadowSampler;
uniform bool Blend;

const int MAX_LIGHTS = 16;
uniform int NumLights;
uniform vec3 Lights[MAX_LIGHTS];
const float lightRadius = 100;

float distSq(vec3 v1, vec3 v2) {
    vec3 d = v1-v2;
    return dot(d,d);
}

float CalcShadowFactor(vec4 LightSpacePos)
{
    vec3 ProjCoords = LightSpacePos.xyz / LightSpacePos.w;
    vec2 UVCoords;
    UVCoords.x = 0.5 * ProjCoords.x + 0.5;
    UVCoords.y = 0.5 * ProjCoords.y + 0.5;
    float Depth = texture(ShadowSampler, UVCoords).x;
    if (Depth < (ProjCoords.z + 0.0001))
        return 0.5;
    else
        return 1.0;
}

void main()
{
    float scale;

    FragColor = texture2D(TexSampler, TexCoord0.xy);

    // transparency
    if(!Blend && FragColor.a < 0.5) discard;

    // biome blending
    FragColor *= vec4(Tint0, 1.0f);

    // fog
    float depth = gl_FragCoord.z / gl_FragCoord.w;
    if(depth>20) {
        scale = clamp(1.2-15/(depth-19),0,1);
        vec3 destColor = vec3(0.671,0.792,1.00);
        vec3 colorDist = destColor - FragColor.xyz;
        FragColor.xyz += colorDist*scale;
    }

    // lighting
    scale = 0.30;
    for(int i=0; i<NumLights; ++i) {
        float dist = distSq(WorldPos.xyz, Lights[i]);
        if(dist < lightRadius) {
            scale += (lightRadius-dist)/lightRadius;
        }
    }

    scale *= CalcShadowFactor(LightPos);
    FragColor.xyz *= clamp(scale,0,1.5);
}

我很确定这是一个偏移问题。我的阴影看起来大约有一个街区,但我无法弄清楚如何移动它们,也不知道是什么导致它们被关闭。


实际上看起来像“depth map bias”:

不完全确定如何设置此....我只是在渲染场景之前调用glPolygonOffset吗?会尝试...

glPolygonOffset设置为100,100可以解决问题:

我在渲染阴影贴图之前设置了它:

GL.Enable(EnableCap.PolygonOffsetFill);
GL.PolygonOffset(100f, 100.0f);

然后再次禁用它。我不确定这是不是我该怎么做。增加这些值可以放大这个问题....将它们降低到1以下似乎并没有改善它。

另请注意左下方的阴影贴图是如何更改的。

顶点着色器

#version 330

layout(location = 0) in vec3 Position;

layout(location = 1) in vec2 TexCoord;
layout(location = 2) in mat4 Transform;
layout(location = 6) in vec4 TexSrc; // x=x, y=y, z=width, w=height
layout(location = 7) in vec3 Tint; // x=R, y=G, z=B

uniform mat4 ProjectionMatrix;
uniform mat4 LightMatrix;

out vec2 TexCoord0;
out vec3 Tint0;
out vec4 WorldPos;
out vec4 LightPos;

void main()
{
    WorldPos = Transform * vec4(Position, 1.0);
    gl_Position = ProjectionMatrix * WorldPos;
    LightPos = LightMatrix * WorldPos;
    TexCoord0 = vec2(TexSrc.x+TexCoord.x*TexSrc.z, TexSrc.y+TexCoord.y*TexSrc.w); 
    Tint0 = Tint;
}

3 个答案:

答案 0 :(得分:2)

这被称为缓冲映射阴影中的“鹿头灯”效果。有几种方法可以最大限度地减少这种影响。寻找“光空间阴影映射”。

答案 1 :(得分:1)

NVidia OpenGL SDK有“级联阴影贴图”示例。您可能想要查看它(但是我自己没有使用它)。

  

如何提高阴影的质量?

在渲染阴影时使用不正确的矩阵可能会导致问题。您的示例未演示如何设置浅色矩阵。根据墨菲定律,我将不得不假设错误在于这段缺失的代码 - 因为你认为这部分并不重要,它可能会导致问题。如果在测试阴影时使用的矩阵与用于渲染阴影的矩阵不同,那么您将得到这个问题。

我建议暂时忘掉整个我的世界,并在简单的应用程序中玩弄阴影。制作一个带有地板平面和旋转立方体(或茶壶或任何你想要的)的独立应用程序,并在那里调试阴影贴图,直到你掌握它为止。既然你愿意在这个问题上投入+100赏金,你可以在这里发布你的独立样本的完整代码 - 如果你仍然在样本中得到问题。无论如何,试图将你不熟悉的技术粘在工作(?)引擎的中间并不是一个好主意。慢慢来,习惯技术/技术/效果,然后整合它。

答案 2 :(得分:1)

虽然世界对齐的级联阴影贴图很棒并且在大多数新游戏中使用,但它与您的阴影与当前实现的偏差有什么关系。

实际上,看起来你正在从阴影贴图上的正确纹理像素中采样正好在正在发生的阴影正好在你预期的位置,但是你的比较已经关闭。

我在您的代码中添加了一些注释:

vec3 ProjCoords = LightSpacePos.xyz / LightSpacePos.w; // So far so good...
vec2 UVCoords;
UVCoords.x = 0.5 * ProjCoords.x + 0.5; // Right, you're converting X and Y from clip
UVCoords.y = 0.5 * ProjCoords.y + 0.5; // space to texel space...
float Depth = texture(ShadowSampler, UVCoords).x; // I expect we sample a value in [0,1]
if (Depth < (ProjCoords.z + 0.0001)) // Uhoh, we never converted Z's clip space to [0,1]
    return 0.5;
else
    return 1.0;

所以,我怀疑你想要与ProjCoords.z * 0.5 + 0.5进行比较:

if (Depth < (ProjCoords.z * 0.5 + 0.5 + 0.0001))
    return 0.5;
else
    return 1.0;

此外,这种偏见因素让我感到紧张。更好的是,现在就把它拿出来,一旦你把阴影出现在正确的位置就处理它:

const float bias = 0.0;
if (Depth < (ProjCoords.z * 0.5 + 0.5 + bias))
    return 0.5;
else
    return 1.0;

关于如何转换ProjCoords.z以匹配采样值,我可能不完全正确,但这可能是个问题。此外,如果您移动到级联阴影贴图(我建议使用世界对齐),我强烈建议绘制表示每个阴影贴图正在查看的位置的平截头体 - 它使调试变得更加容易。