【问题标题】:Shadow mapping and depth values confusion阴影贴图和深度值混淆
【发布时间】:2014-01-19 19:57:47
【问题描述】:

我在延迟的 OpenGL 4.3 渲染器中为聚光灯做阴影映射。

我一直在尝试遵循一些有关该主题的教程,并在其后通过片段着色器进行建模,但我不明白的是计算阴影因子的最终比较。我从深度图 ("unifShadowTexture") 中采样的值在 [0, 1] 的范围内,因为它直接来自深度缓冲区,但 projCoords.z 是如何限制在 [0, 1] 的? .w 的除法是否将其限制为 [0,1]? 我面临的问题是场景的主要部分是阴影,即使它不应该是,例如底层,在下图中(忽略光伪影,由于缺乏偏差 - 重点是模型亮了,但底层没有):

const std::string gDirLightFragmentShader =
"#version 430                                                                                                                   \n \
                                                                                                                                \n \
layout(std140) uniform;                                                                                                         \n \
                                                                                                                                \n \
uniform UnifDirLight                                                                                                            \n \
{                                                                                                                               \n \
    mat4 mWVPMatrix;                                                                                                            \n \
    mat4 mVPMatrix;   // light view-projection matrix, pre-multiplied by the bias-matrix                                                                                                          \n \
    vec4 mLightColor;                                                                                                           \n \
    vec4 mLightDir;                                                                                                             \n \
    vec4 mGamma;                                                                                                                \n \
    vec2 mScreenSize;                                                                                                           \n \
} UnifDirLightPass;                                                                                                             \n \
                                                                                                                                \n \
layout (binding = 2) uniform sampler2D unifPositionTexture;                                                                     \n \
layout (binding = 3) uniform sampler2D unifNormalTexture;                                                                       \n \
layout (binding = 4) uniform sampler2D unifDiffuseTexture;                                                                      \n \
layout (binding = 5) uniform sampler2D unifShadowTexture;                                                                       \n \
                                                                                                                                \n \
out vec4 fragColor;                                                                                                             \n \
                                                                                                                                \n \
void main()                                                                                                                     \n \
{                                                                                                                               \n \
    vec2 texcoord = gl_FragCoord.xy / UnifDirLightPass.mScreenSize;                                                             \n \
                                                                                                                                \n \
    vec3 worldPos = texture(unifPositionTexture, texcoord).xyz;                                                                 \n \
    vec3 normal   = normalize(texture(unifNormalTexture, texcoord).xyz);                                                        \n \
    vec3 diffuse  = texture(unifDiffuseTexture, texcoord).xyz;                                                                  \n \
                                                                                                                                \n \
    vec4 lightClipPos = UnifDirLightPass.mVPMatrix * vec4(worldPos, 1.0);                                                      \n \
    vec3 projCoords   = lightClipPos.xyz / lightClipPos.w;                                                                   \n \
                                                                                                                                \n \
    float depthValue = texture(unifShadowTexture, projCoords.xy).x;                                                           \n \
    float visibilty  = 1.0;                                                                                                   \n \
    if (depthValue < (projCoords.z))                                                                                  \n \
         visibilty = 0.0;                                                                                                    \n \
                                                                                                                                \n \
    float angleNormal = clamp(dot(normal, UnifDirLightPass.mLightDir.xyz), 0, 1);                                               \n \
                                                                                                                                \n \
    fragColor = vec4(diffuse, 1.0) * visibilty * angleNormal * UnifDirLightPass.mLightColor;                                 \n \
}                                                                                                                               \n";

【问题讨论】:

    标签: opengl shadow shadow-mapping


    【解决方案1】:

    w 的除法本身不会将其钳制为 [0,1]。从技术上讲,您拥有的是剪辑空间坐标,然后除以 w 将它们转换为 NDC 空间。对几何图形执行此操作时,任何 w 的点 x、y 或 z 都会被剪裁。但是,当您在纹理坐标上执行此操作时,您仍然需要提供适当的纹理环绕模式(通常是GL_CLAMP_TO_EDGE),因为坐标不会自动固定。

    请注意,由于坐标被固定,所有投影到阴影贴图远平面之外的点都会表现出相同的行为。通常这表现为超出一定距离的一切都完全处于阴影中。这就是您希望阴影贴图的平截头体与衰减光量更紧密匹配的原因。


    另外,在这种情况下,sampler2DShadow 很有意义...

    整件事:

    float depthValue = texture(unifShadowTexture, projCoords.xy).x;
    float visibilty  = 1.0;
    
    if (depthValue < (projCoords.z))
      visibilty = 0.0;            
    

    可以简化为:

    float visibility = texture (unifShadowTexture, projCoords);
    

    只要你做两件事:

    1. unifShadowTexture 声明为sampler2DShadow
    2. GL_TEXTURE_COMPARE_MODE 设置为GL_COMPARE_R_TO_TEXTURE 以获得深度纹理

    这很有效,因为对于sampler2DShadow,它需要 3D 纹理坐标。 st 照常工作,r 是它用于比较的值。默认比较函数是GL_LESS,因此您不必更改代码中的任何内容。给定一个 GL_NEAREST 纹理函数,它的返回将是 1.00.0。它将产生与您上面的代码完全相同的 sn-p,但使用硬件功能进行实际深度测试,并且如果您启用 GL_LINEAR,通常会提高性能并获得便宜的 PCF。

    不过,我建议您为纹理坐标的 Z 分量添加一个偏差。像 (0.001) 之类的东西,否则你会得到很多“阴影粉刺”,偏差太高,你会得到“彼得平移”(阴影似乎稍微悬停在它们所附着的任何东西上方)。

    【讨论】:

    • 令人惊讶的是,使用 sampler2Dshadow 效果非常好,非常感谢。我不明白为什么我没有它的尝试没有奏效。至于偏差,它似乎是一个非常精确的值,或者整个场景都处于阴影中或存在粉刺。是否有任何理由不使用 sampler2Dshadow,例如质量或其他?
    • 我想不出许多不将它用于标准阴影映射的理由。还有更高级的算法,例如方差阴影映射,您实际上并不比较单个深度值(它比较深度和深度*深度)。对于这些算法,您不能使用sampler2DShadow。但对于简单的阴影贴图,我总是建议使用sampler2DShadow。您是否已经设置了GL_TEXTURE_COMPARE_MODE?使用sampler2D 对具有该集合的纹理进行采样是未定义的行为;在没有该集合的情况下对sampler2DShadow 进行采样也是未定义的行为。
    • @KaiserJohaan:如果您需要一个非常精确的偏置值才能起作用,那么当您生成的阴影贴图相距太远时,您可能有近/远平面。您希望使用尽可能小的距离来最大化阴影贴图的精度,通常您的阴影贴图不需要与场景的其余部分(除了像太阳这样的定向光)接近相同距离的任何地方,因为光线会随着距离而衰减.
    • 拯救了我的一天!谢谢你:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多