opengl - 阴影贴图和深度值混淆

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

最佳答案

除以 w 本身不会将其限制为 [0,1]。从技术上讲,您拥有的是剪辑空间坐标,然后除以 w 将它们转换为 NDC 空间。当对几何体执行此操作时,任何 < -w 或 > 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。它将生成与上面的代码片段完全相同的代码,但使用硬件功能进行实际深度测试,并且如果启用 GL_LINEAR,通常会提高性能并为您提供廉价的 PCF。

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

关于opengl - 阴影贴图和深度值混淆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21221864/

相关文章:

html - 如何创建具有 Angular 和不透明度要求的盒子阴影

opengl - 使用延迟渲染 (OpenGL) 的阴影贴图

linux - 无法在 Linux(Ubuntu)上使用 glew.h

java - LWJGL (OpenGL) VBO 模型矩阵未正确渲染

c++ - 激活多重采样 (Windows) 时如何防止出现空的 OpenGL 窗口?

opengl - glsl sampler2DShadow 和 shadow2D 说明

c++ - 使用 VBO 的 OpenGL 绘图

html - 带有 CSS 梯形形状按钮的阴影

c++ - 使用立方体贴图在 Opengl-es 2.0 中进行阴影映射