【问题标题】:Cascaded Shadow maps not quite right级联阴影贴图不太正确
【发布时间】:2015-03-04 06:15:04
【问题描述】:

好的。所以,上周我一直在玩弄我的游戏引擎中的阴影。我主要实现了级联阴影贴图 (CSM),但我遇到了一些我似乎无法解决的阴影问题。

此场景中唯一的光是定向光(太阳),指向 {-0.1 -0.25 -0.65}。我使用以下代码为我的 CSM 的四个拆分计算 4 组截锥边界:

// each projection matrix calculated with same near plane, different far
Frustum make_worldFrustum(const glm::mat4& _invProjView) {
    Frustum fr; glm::vec4 temp;
    temp = _invProjView * glm::vec4(-1, -1, -1, 1);
    fr.xyz = glm::vec3(temp) / temp.w;
    temp = _invProjView * glm::vec4(-1, -1,  1, 1);
    fr.xyZ = glm::vec3(temp) / temp.w;
    ...etc 6 more times for ndc cube
    return fr;
}

对于灯光,我得到一个像这样的视图矩阵:

glm::mat4 viewMat = glm::lookAt(cam.pos, cam.pos + lightDir, {0,0,1});

然后我从每个平截头体的边界创建每个正交矩阵:

lightMatVec.clear();
for (auto& frus : cam.frusVec) {
    glm::vec3 arr[8] {
        glm::vec3(viewMat * glm::vec4(frus.xyz, 1)),
        glm::vec3(viewMat * glm::vec4(frus.xyZ, 1)),
        etc...
    };

    glm::vec3 minO = {INFINITY, INFINITY, INFINITY};
    glm::vec3 maxO = {-INFINITY, -INFINITY, -INFINITY};
    for (auto& vec : arr) {
        minO = glm::min(minO, vec);
        maxO = glm::max(maxO, vec);
    }

    glm::mat4 projMat = glm::ortho(minO.x, maxO.x, minO.y, maxO.y, minO.z, maxO.z);
    lightMatVec.push_back(projMat * viewMat);
}

我有一个 4 层的 TEXTURE_2D_ARRAY 绑定到 4 个帧缓冲区,我使用一个非常简单的顶点着色器(禁用片段或穿透 alpha)将场景绘制到其中。

然后我画最后一幕。顶点着色器输出四个阴影纹理坐标:

out vec3 slShadcrd[4];
// stuff
for (int i = 0; i < 4; i++) {
    vec4 sc = WorldBlock.skylMatArr[i] * vec4(world_pos, 1);
    slShadcrd[i] = sc.xyz / sc.w * 0.5f + 0.5f;
}

还有一个片段着色器,它决定要使用的分割:

int csmIndex = 0;
for (uint i = 0u; i < CameraBlock.csmCnt; i++) {
    if (-view_pos.z > CameraBlock.csmSplits[i]) index++;
    else break;
}

并使用此函数对阴影贴图数组进行采样:

float sample_shadow(vec3 _sc, int _csmIndex, sampler2DArrayShadow _tex) {
    return texture(_tex, vec4(_sc.xy, _csmIndex, _sc.z)).r;
}

而且,这是我得到的场景(每个分割都略微着色,并且 4 个深度层重叠): 伟大的!看起来挺好的。

但是,如果我将相机稍微向右转动: 然后阴影开始消失(取决于角度,出现在不应该出现的地方)。

我启用了 GL_DEPTH_CLAMP,所以这不是问题。我正在剔除正面,但将其关闭对这个问题没有影响。

我错过了什么?我觉得这是我的一个预测的问题,但它们在我看来都是正确的。谢谢!

编辑: 光的所有四个平截头体都画好了。它们都在那里,但只有 z 相对于相机在变化(见下面的评论):

编辑: 可能更有用,这就是当我只更新一次截锥体时的样子,当相机位于 (0,0,0) 并指向前方 (0,1,0) 时。这次我也用深度测试画了它们。

重要编辑: 看来这个问题是和灯光的视图矩阵直接相关的,目前:

glm::mat4 viewMat = glm::lookAt(cam.pos, cam.pos + lightDir, {0,0,1});

更改 eye 和 target 的值似乎会影响错误的阴影。但我不知道我实际上应该将它设置为什么?对于比我更了解的人来说应该很容易:D

【问题讨论】:

  • 我在代码中看不到任何明显的东西。也许画出级联的截锥体,看看它们是否正确(地面上的颜色不能提供足够的信息。)
  • (通过“绘制截锥体”,我的意思是一个线框框。)
  • 你要画哪个截锥体?我只是设置代码来绘制最终阴影矩阵的截锥体,它们从相机的 POV 看起来都相同,但确实具有不同的值。这是因为相对于相机变化的唯一值是 Z 值吗?应该是这样吗?
  • 另外,我只是画出了相机的视锥体,它们只是在视口周围形成了一个轮廓,这似乎是正确的。

标签: opengl shadow-mapping


【解决方案1】:

解决了!这确实是光的视图矩阵的问题!我所要做的就是将camPos 替换为每个平截头体的中心点!这意味着每个拆分的光矩阵需要不同的视图矩阵。所以我只是像这样创建每个视图矩阵......

glm::mat4 viewMat = glm::lookAt(frusCentre, frusCentre+lightDir, {0,0,1});

然后简单地获得 frusCentre...

glm::vec3 calc_frusCentre(const Frustum& _frus) {
    glm::vec3 min(INFINITY, INFINITY, INFINITY);
    glm::vec3 max(-INFINITY, -INFINITY, -INFINITY);
    for (auto& vec : {_frus.xyz, _frus.xyZ, _frus.xYz, _frus.xYZ,
                      _frus.Xyz, _frus.XyZ, _frus.XYz, _frus.XYZ}) {
        min = glm::min(min, vec);
        max = glm::max(max, vec);
    }
    return (min + max) / 2.f;
}

然后砰!一切都非常出色!

编辑(最后一个!): 我所拥有的并不完全正确。视图矩阵实际上应该是:

glm::lookAt(frusCentre-lightDir, frusCentre, {0,0,1});

【讨论】:

  • 有趣...我以前从未考虑过使用INFINITY 来初始化min。我一直担心它不会在所有系统上都受支持,所以我使用了FLT_MAX。但是,INFINITY 是一个更好的解决方案,因为涉及它的数学运算会产生NAN,这很明显有些东西没有初始化。 我刚刚检查并在不支持 Infinity 和 Quiet NaN 浮点位模式的系统上将 INFINITY 定义为 FLT_MAX,这使得它可以在所有系统上完美使用; +1 让我了解到这一点;)
  • 考虑到我从你那里学到了很多东西,这让我感觉非常棒:D
猜你喜欢
  • 2016-02-03
  • 2020-11-21
  • 1970-01-01
  • 2018-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-22
相关资源
最近更新 更多