OpenGL变量阴影映射倒置衰减

3
我正在尝试使用OpenGL API实现Variance Shadow Map技术。我一直在使用教程(Fabien Sanglard的软阴影与VSM)并按照每个步骤进行操作,但我的阴影图看起来有点奇怪。我注意到的主要问题是,当我改变光线(透视)投影矩阵的近剪裁平面时,它开始看起来奇怪。
例如,这是在1.0f近剪裁http://postimg.org/image/rupf6wqcx/时的效果(此结果被认为是好的)
而这里是在0.1f值http://postimg.org/image/fox04z14z/时的效果
请注意,光的位置保持不变。
我已经连续三天试图找出问题,但没有结果。你能帮我吗?
这是阴影片段着色器的代码。
in vec4 v_position;
out vec4 color;

void main()
{
    float depth = v_position.z / v_position.w;
    depth = depth * 0.5 + 0.5;

    float dx = dFdx(depth);
    float dy = dFdy(depth);

    float moment1 = depth;
    float moment2 = depth * depth - 0.25 * (dx * dx + dy * dy);

    color = vec4(moment1, moment2, 0.0, 1.0);
}

而阴影映射部分来自实际渲染通道的片段着色器

in vec4 ShadowPosition;
out vec4 outColor;
uniform sampler2D shadowMap;
vec4 sc;

float chebyshevUpperBound(float distance)
{
    float p = 0.0;

    // We retrive the two moments previously stored (depth and depth*depth)
    vec2 moments = texture2D(shadowMap, sc.xy).rg;

    // Surface is fully lit. as the current fragment is before the light occluder
    if (distance <= moments.x)
        p = 1.0;

    // The fragment is either in shadow or penumbra. We now use chebyshev's upperBound to check
    // How likely this pixel is to be lit (p_max)
    float variance = moments.y - (moments.x * moments.x);
    variance = max(variance, 0.00001);

    float d = distance - moments.x;
    float p_max = variance / (variance + d*d);

    return max(p, p_max);
}

void main()
{
    /* Shadow Mapping */
    vec3 pixColor = vec3(1.0, 1.0, 1.0);
    sc = ShadowPosition / ShadowPosition.w;
    sc = sc * 0.5 + 0.5;
    float visibility = chebyshevUpperBound(sc.z);
    outColor = vec4(visibility * pixColor, 1.0);
}

顶点着色器很简单,使用MVP矩阵从光源或相机的角度计算顶点,因此我认为不需要发布它们。

这段代码是用于初始化和渲染:

glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);

glGenFramebuffers(1, &FramebufferName);

glGenTextures(1, &light_s.shadowBO);
glBindTexture(GL_TEXTURE_2D, light_s.shadowBO);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 1024, 1024, 0, GL_RGBA, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
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, borderColor);

glGenTextures(1, &light_s.shadowBOZ);
glBindTexture(GL_TEXTURE_2D, light_s.shadowBOZ);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, 1024, 1024, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
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, borderColor);

glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, light_s.shadowBO, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, light_s.shadowBOZ, 0);

while (running)
{
    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    else
    {
        /* Shadow pass */
        glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
        glViewport(0, 0, 1024, 1024);
        glDrawBuffer(GL_BACK);
        glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

        glUseProgram(theShadowProgram);
        glEnableVertexAttribArray(svxposition);

        glUniformMatrix4fv(spmatrix, 1, GL_FALSE, light_s.mProjection);
        glUniformMatrix4fv(svmatrix, 1, GL_FALSE, light_s.mView);
        glUniformMatrix4fv(smmatrix, 1, GL_FALSE, bunny_s.mModel);

        glBindBuffer(GL_ARRAY_BUFFER, bunny_s.vertexBufferObject);
        glVertexAttribPointer(svxposition, 3, GL_FLOAT, GL_FALSE, 0, NULL);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bunny_s.elementBufferObject);
        glDrawElements(GL_TRIANGLES, bunny_s.elementBufferSize, GL_UNSIGNED_INT, NULL);

        glBindBuffer(GL_ARRAY_BUFFER, vbplaneVert);
        glVertexAttribPointer(svxposition, 3, GL_FLOAT, GL_FALSE, 0, NULL);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbplaneElem);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL);

        glDisableVertexAttribArray(svxposition);

        /* Rendering pass */
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glViewport(0, 0, 1024, 768);
        glDrawBuffer(GL_BACK);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

        glUseProgram(theProgram);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, light_s.shadowBO);

        glEnableVertexAttribArray(vxposition);
        glEnableVertexAttribArray(normals);

        glUniformMatrix4fv(dpmatrix, 1, GL_FALSE, light_s.mProjection);
        glUniformMatrix4fv(dvmatrix, 1, GL_FALSE, light_s.mView);
        glUniformMatrix4fv(dbmatrix, 1, GL_FALSE, mDepthBias);

        glUniformMatrix4fv(pmatrix, 1, GL_FALSE, camera_s.mProjection);
        glUniformMatrix4fv(vmatrix, 1, GL_FALSE, camera_s.mView);
        glUniformMatrix4fv(mmatrix, 1, GL_FALSE, bunny_s.mModel);

        glUniform3f(campos, camera_s.x, camera_s.y, camera_s.z);
        glUniform3f(lightpos, light_s.x, light_s.y, light_s.z);

        glUniform1i(frsampler, 0);

        glBindBuffer(GL_ARRAY_BUFFER, bunny_s.vertexBufferObject);
        glVertexAttribPointer(vxposition, 3, GL_FLOAT, GL_FALSE, 0, NULL);

        glBindBuffer(GL_ARRAY_BUFFER, bunny_s.normalBufferObject);
        glVertexAttribPointer(normals, 3, GL_FLOAT, GL_FALSE, 0, NULL);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bunny_s.elementBufferObject);
        glDrawElements(GL_TRIANGLES, bunny_s.elementBufferSize, GL_UNSIGNED_INT, NULL);

        glBindBuffer(GL_ARRAY_BUFFER, vbplaneVert);
        glVertexAttribPointer(vxposition, 3, GL_FLOAT, GL_FALSE, 0, NULL);

        glBindBuffer(GL_ARRAY_BUFFER, vbplaneNorm);
        glVertexAttribPointer(normals, 3, GL_FLOAT, GL_FALSE, 0, NULL);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbplaneElem);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL);

        glDisableVertexAttribArray(vxposition);
        glDisableVertexAttribArray(normals);
    }
}

它没有进行优化,但现在可以使用。


没有代码的话,诊断起来会很困难。你能发一些代码吗? - Chris Mantle
@ChrisMantle 我已经编辑了我的问题并附上了一些代码示例,希望能有所帮助。 - Vladislav Mikhalin
1个回答

3
如果您使用GL_RG32F代替GL_RGBA32F,由于color.zw(即BA)是常量,您可以将阴影图存储和内存带宽需求减少一半。此外,您是否知道使用透视投影时,随着越靠近近平面,depth * depthdepth相比变化缓慢?这会导致精度问题,如屏幕截图所示。这两个问题在此论文集成部分中简要讨论。
基本上,您需要更加智能地管理近和远平面(更紧密地适配到最小/最大距离)......这对于任何阴影映射算法都是很好的实践,但对于VSM尤为重要。
我建议您完全放弃透视深度,并消除非线性精度分布的问题(您不需要投影深度才能使算法工作,如果从光源计算常规距离,它也会正常工作)。这样应该极大地帮助解决您遇到的主要问题。

非常感谢您的帮助!我已经想炸了,一直在思考问题出在哪里! - Vladislav Mikhalin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接