【问题标题】:How to improve the quality of my shadows?如何提高我的阴影质量?
【发布时间】:2012-03-24 18:39:19
【问题描述】:

首先,截图:

如您所见,阴影的顶部看起来不错(如果您看一下灌木顶部投影的泥土,它看起来或多或少是正确的),但阴影的底部距离很远。

图像的左下角显示了我计算的阴影贴图。这是来自光的 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);
}

我相当肯定这是一个偏移问题。我的阴影看起来大约有 1 个街区,但我不知道如何移动它们,也不知道是什么导致它们关闭。


实际上看起来像“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;
}

【问题讨论】:

  • 所以摄像头位于人的头部。顶部是矿灯。然后阴影贴图应该从稍微抬高的位置查看场景的深度 -
  • @StefanHanke:不知道你所说的“稍微抬高”是什么意思。这只是一个观察,还是我可以做点什么?
  • 观察。由于我从未实现过阴影贴图,因此我不确定以下几点:阴影贴图是使用灯光的位置生成的。由于这是在人的头上,我希望视图是从上面看的。从图像来看,灯似乎低于头部。那么,也许与光矩阵有关?对不起,如果这完全是假的......
  • @StefanHanke:你的观察是正确的。灯比头部低一点;我相信“矿工的光”表达只是意味着光接近相机,而不是实际上直接上方,或完全相同的点。
  • 您介意发布顶点着色器吗?

标签: opengl rendering


【解决方案1】:

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

实际上,您似乎正在从阴影贴图上的正确纹理像素中采样,只是在阴影发生的位置正是您期望的位置,但是您的比较已关闭。

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

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 以匹配采样值,我可能并不完全正确,但这可能是问题所在。此外,如果您确实要使用级联阴影贴图(我建议使用世界对齐),我强烈建议您绘制平截头体来表示每个阴影贴图正在查看的位置——这会使调试变得更加容易。

【讨论】:

  • 您先生,真是个天才!它works pretty well now。偏见was needed;花了一些时间,但-0.0005 似乎工作得很好。
  • 也许我应该早点提到这一点,但我关注的是this tutorial;在his example 中可能不是很明显。我会向作者报告的。
  • 非常感谢 :) 对此非常兴奋。我将很快研究级联阴影贴图;我将需要一些非常有效的东西来完成我正在做的事情。希望它适用于延迟着色......我可能也需要它。一次一步:) (one more screenie)
  • 不错的照片,看起来不错!阴影贴图不一定会影响延迟着色,这取决于它是如何完成的。延迟着色本身非常简单,困难的部分是如何打包缓冲区以实现有效的内存使用和传输。延迟着色本身在没有太多事情要做时相对昂贵,但在有很多事情要做时相对便宜,因此 YMMV 取决于您的资产和渲染管道的复杂性。
【解决方案2】:

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

【讨论】:

  • 这不是我在做的吗?我首先通过从灯光的角度绘制场景来计算阴影贴图,然后当我进行最后的渲染过程时,我使用相同的光投影矩阵,这样我就可以确定一个像素在阴影贴图中的位置。
  • @Mark:不。你是从光中绘制视图,但在世界空间中。这个想法是以某种方式生成阴影贴图,使其空间分辨率在最后一步中分布得更均匀。只是谷歌这个主题。第一次点击:cg.tuwien.ac.at/research/vr/lispsm
  • 上次更新时间是 2006 年。从那以后什么都没有改变?希望有一个描述该方法的教程,而不是一堆源代码来挖掘和破译。哦……非易失性。有一张纸。我明天试着读一下。谢谢。
  • @Mark:嗯,这几乎消除了令人讨厌的锯齿状边缘伪影。从那时起,兴趣就在于绘制柔和的阴影。那里有很多东西。谷歌搜索“阴影映射纸”会产生很多有趣的结果。我推荐阅读 Crytek 发表的论文。
  • link 如果您阅读幻灯片 64 和 65...“前灯中的鹿”问题听起来与我在这里遇到的相反。在我的图像(矿灯)中,灯光和相机几乎对齐,根据这些幻灯片,这是最佳的。
【解决方案3】:

NVidia OpenGL SDK 有“级联阴影贴图”示例。您可能想检查一下(虽然我自己没有使用过)。

如何提高我的阴影质量?

这个问题可能是由于在渲染阴影时使用了不正确的矩阵。您的示例没有演示如何设置光矩阵。根据墨菲定律,我必须假设错误存在于这段缺失的代码中——既然你认为这部分不重要,它可能会导致问题。如果在测试阴影时使用的矩阵与用于渲染阴影的矩阵不同,您就会遇到这个问题。

我建议暂时忘记整个我的世界,在简单的应用程序中玩弄阴影。用平面和旋转立方体(或茶壶或任何你想要的东西)制作一个独立的应用程序,并在那里调试阴影贴图,直到你掌握它。由于您愿意在问题上投入 +100 赏金,因此您不妨在此处发布您的独立示例的完整代码 - 如果您仍然在示例中遇到问题。尝试将您不熟悉的技术融入工作(?)引擎的中间并不是一个好主意。慢慢来,习惯技术/技术/效果,然后整合。

【讨论】:

  • 我会看看那篇论文。看起来比我见过的其他人更容易阅读。设置一个最小的示例需要大量的工作......在您开始使用阴影之前,必须编写大量代码。不过......也许我会尝试一下,看看我是否有同样的问题。光矩阵应该完全相同......我使用的是相同的变量;如果我把它搞砸了,我怀疑它会走得更远。
  • @Mark:“建立一个最小示例需要大量工作” 一种方法是从网上获取一些现有的“GLSL 教程”并将其用作最小示例的基础。此外,假设您有处理着色器的类或例程,您应该能够在大约一小时内复制粘贴最小示例(使用当前引擎的一部分)。在这种情况下,使用 SDL 或 GLUT 并在附近有“最小的 opengl 应用程序”会有所帮助。
  • 我正在使用 C# 和 OpenTK。不过我会拼凑一些东西。过几天再来汇报。
猜你喜欢
  • 2018-08-26
  • 1970-01-01
  • 2016-01-04
  • 1970-01-01
  • 1970-01-01
  • 2021-04-14
  • 2013-05-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多