我正在延迟的 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";
最佳答案
除以 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);
假设你做了两件事:
- 将
unifShadowTexture
声明为sampler2DShadow
- 将深度纹理的
GL_TEXTURE_COMPARE_MODE
设置为GL_COMPARE_R_TO_TEXTURE
这之所以有效,是因为对于 sampler2DShadow
,它需要 3D 纹理坐标。 s
和 t
照常工作,r
是它用于比较的值。默认比较函数是GL_LESS
,因此您不必更改代码中的任何内容。给定 GL_NEAREST
纹理函数,其返回值将是 1.0 或 0.0。它将生成与上面的代码片段完全相同的代码,但使用硬件功能进行实际深度测试,并且如果启用 GL_LINEAR
,通常会提高性能并为您提供廉价的 PCF。
但是,我建议您为纹理坐标的 Z 分量添加偏差。像(0.001)这样的东西,否则你会得到很多“阴影痤疮”,偏差太高,你会得到“彼得潘”(阴影似乎稍微悬停在它们所附着的任何东西之上)。
关于opengl - 阴影贴图和深度值混淆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21221864/