阴影映射和深度值混淆

时间:2014-01-19 19:57:48

标签: opengl shadow shadow-mapping

我正在为延迟的OpenGL 4.3渲染器中的聚光灯进行阴影贴图。

我一直在尝试关注这个主题的一些教程,并在它之后用片段着色器建模,但我不明白的是计算阴影因子的最终比较。我从深度图(“unifShadowTexture”)中采样的值在[0,1]的范围内,因为它直接来自深度缓冲区但是projCoords.z如何被钳制到[0, 1]?是.w除以[0,1]的除法吗? 我面临的问题是场景的一个主要部分是阴影,即使它不应该,如底层,在下面的图片中(由于缺乏偏见而忽略了轻微的伪影 - 重要的一点是模型点亮,但底层不是):

enter image description here

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";

1 个答案:

答案 0 :(得分:2)

w除以它本身不会将其限制为[0,1]。从技术上讲,你所拥有的是剪辑空间坐标,然后除以w将它们转换为NDC空间。任何点x,y或z都是&lt; -w或&gt;在几何体上执行此操作时会剪切w。但是,当您在纹理坐标上执行此操作时,仍然需要提供适当的纹理包装模式(通常为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以获得深度纹理
  3. 这是有效的,因为对于sampler2DShadow,它需要3D纹理坐标。 st正常工作,r是用于比较的值。默认比较函数是GL_LESS,因此您无需更改代码中的任何内容。给定GL_NEAREST纹理函数,其返回值为 1.0 0.0 。它将与上面的代码片段完全相同,但使用硬件功能进行实际深度测试,如果您启用GL_LINEAR,通常会提高性能并为您提供便宜的PCF。

    但是,我建议您在纹理坐标的Z分量上添加偏差。像(0.001)这样的东西或者你会得到很多“阴影痤疮”,太高的偏见,你会得到“彼得平移”(阴影似乎略微悬停在他们附着的任何东西之上)。