我试图在我的渲染引擎中用OpenGL实现对方向阴影的方差阴影映射。
我已经阅读了多篇文章,比如- https://developer.nvidia.com/gpugems/gpugems3/part-ii-light-and-shadows/chapter-8-summed-area-variance-shadow-maps、https://graphics.stanford.edu/~mdfisher/Shadows.html来开发这个项目。
该算法的基本流程如下:
下面是具有正投影矩阵的定向光的深度着色器:
#version 440 core
uniform float farPlane;
uniform vec3 lightPos;
uniform mat4 directional_light_space_matrix;
in vec4 FragPos;
out vec2 depth;
void main()
{
vec4 FragPosLightSpace = directional_light_space_matrix * FragPos;
float d = FragPosLightSpace.z / FragPosLightSpace.w;
d = d * 0.5 + 0.5;
float m1 = d;
float m2 = d * d;
float dx = dFdx(depth.x);
float dy = dFdx(depth.y);
m2 += 0.25 * (dx * dx + dy * dy);
depth.r = m1;
depth.g = m2;
}这是片段着色器的片段,检查一个片段被点亮了多少。
float linstep(float mi, float ma, float v)
{
return clamp ((v - mi)/(ma - mi), 0, 1);
}
float ReduceLightBleeding(float p_max, float Amount)
{
return linstep(Amount, 1, p_max);
}
float chebyshevUpperBound(float dist, vec2 moments)
{
float p_max;
if(dist <= moments.x)
{
return 1.0;
}
float variance = moments.y - (moments.x * moments.x);
variance = max(variance, 0.1);
float d = moments.x - dist;
p_max = variance / (variance + d * d);
return ReduceLightBleeding(p_max, 1.0);
}
float CheckDirectionalShadow(float bias, vec3 lightpos, vec3 FragPos)
{
vec3 projCoords = FragPosLightSpace.xyz / FragPosLightSpace.w;
projCoords = projCoords * 0.5 + 0.5;
vec2 closest_depth = texture(shadow_depth_map_directional, projCoords.xy).rg;
return chebyshevUpperBound(projCoords.z, closest_depth);
}这是两遍高斯模糊着色器。
#version 440 core
layout (location = 0) out vec2 out_1;
in vec2 TexCoords;
uniform sampler2D inputTexture_1;
uniform bool horizontal;
float weights[5] = float[](0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216);
void main()
{
vec2 tex_offset = 1.0 / textureSize(inputTexture_1,0);
vec2 o1 = texture(inputTexture_1, TexCoords).rg * weights[0];
if(horizontal)
{
for(int i=1; i<4; i++)
{
o1 += texture(inputTexture_1, TexCoords + vec2(tex_offset.x * i, 0.0)).rg * weights[i];
o1 += texture(inputTexture_1, TexCoords - vec2(tex_offset.x * i, 0.0)).rg * weights[i];
}
}
else
{
for(int i=1; i<4; i++)
{
o1 += texture(inputTexture_1, TexCoords + vec2(0.0, tex_offset.y * i)).rg * weights[i];
o1 += texture(inputTexture_1, TexCoords - vec2(0.0, tex_offset.y * i)).rg * weights[i];
}
}
out_1 = o1;
}我把我的框架缓冲区生成代码,以了解我如何存储的时刻。
// directional ----------------------------------------------------------------------------------------------------------------------------------------------
glGenFramebuffers(1, &directional_shadow_framebuffer);
glGenTextures(1, &directional_shadow_framebuffer_depth_texture);
glBindTexture(GL_TEXTURE_2D, directional_shadow_framebuffer_depth_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32F, shadow_map_width, shadow_map_height, 0, GL_RG, GL_FLOAT, NULL);
float border_color[] = { 0.0f,0.0f,0.0f,1.0f };
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color);
glBindFramebuffer(GL_FRAMEBUFFER, directional_shadow_framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, directional_shadow_framebuffer_depth_texture, 0);
glGenRenderbuffers(1, &directional_shadow_framebuffer_renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, directional_shadow_framebuffer_renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, shadow_map_width, shadow_map_height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, directional_shadow_framebuffer_renderbuffer);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
LOGGER->log(ERROR, "Renderer : createShadowMapBuffer", "Directional Shadow Framebuffer is incomplete!");
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// ----------------------------------------------------------------------------------------------------------------------------------------------上述操作的结果与预期相去甚远。我不是得到柔和的半影阴影,而是像锋利的阴影一样有斑点。

这是第一个瞬间(深度)的样子,而第二个瞬间几乎是一样的,但更暗。

我试过用最小方差,阴影核大小,高斯样本,模糊通行证..。但我还没有接近解决方案。
我有一种感觉,在上面给出的Framebuffer生成代码中设置纹理过滤参数时,我可能做错了什么。
我最后的问题是:
发布于 2021-01-03 14:30:16
所以,我解决了这个问题。
实现是非常好的,但是ReduceLightBleeding的最小方差和数量参数需要调优。
我发现,减小最小方差参数会使阴影更加柔和,但会大大增加轻出血。为了消除这种副作用,我们可以在低于某个阈值时将p_max值调到0,否则就会在0到1之间重新增加。这正是ReduceLightBleeding函数所做的,在上面链接的同一个站点中也有描述。但是,在ReduceLightBleeding中增加数量参数会使阴影看起来像斑点,这可以在我上面发布的截图中看到。
我设法调整了最小方差和轻微的减少出血量,以找到一个最佳的地点。然而,我永远也无法完全摆脱这件艺术品。
方差阴影映射的一个更好的选择是它的扩展- 指数方差阴影映射。
我不太理解数学,但我仍然很容易地实现了它。在gamedev.stackexchange上查看这个问题以获得提示- EVSM。
ESVM通过将出血减少到不能被注意或忽略的程度,做了很好的工作。
https://stackoverflow.com/questions/65023605
复制相似问题