opengl 剪辑剔除问题附近的pssm阴影贴图

qacovj5a  于 2023-08-04  发布在  其他
关注(0)|答案(1)|浏览(138)

我试图把我的头围绕级联阴影贴图,我遇到了一个问题与近场剔除。基本上,相机后面的对象被剔除,分割的阴影也被剔除。请看下面的图片。


的数据
我找到了一种方法来解决这个问题,做glEnable(GL_DEPTH_CLAMP);之前的阴影通行证,但现在我有一些其他的问题,我们需要的远和近剪辑剔除。Sponza模型就是一个例子,看起来里面的所有东西都在阴影中结束,因为即使光线应该到达里面的物体,墙也会出现在阴影贴图中。
你们是如何处理这个问题的?
下面是我如何生成阴影贴图的投影视图矩阵

float clipRange = camera->getFarClipPlane() - camera->getNearClipPlane();
    float minZ = camera->getNearClipPlane();
    float maxZ = camera->getNearClipPlane() + clipRange;
    float range = maxZ - minZ;
    float ratio = maxZ / minZ;
    float cascadeSplitLambda = 0.95f;
    // Calculate split depths based on view camera furstum
    // Based on method presentd in https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch10.html
    cascadeSplits.clear();
    cascadeSplitsNew.clear();
    for (int i = 0; i < SHADOW_MAP_CASCADE_COUNT; i++) {
        auto i_float = static_cast<GLfloat>(i);
        float p = (i_float + 1.f) / static_cast<float>(SHADOW_MAP_CASCADE_COUNT);
        float log = minZ * std::pow(ratio, p);
        float uniform = minZ + range * p;
        float d = cascadeSplitLambda * (log - uniform) + uniform;
        GLfloat cascadeSplit = (d - camera->getNearClipPlane()) / clipRange;
        cascadeSplits.push_back(cascadeSplit);
    }

    directionalLightShadowVPMatrixes.clear();
    for (int casscadeIndex = 0; casscadeIndex < SHADOW_MAP_CASCADE_COUNT; casscadeIndex++) {
        glm::vec3 frustumCornersWS[8] = {
                glm::vec3(-1.0f, 1.0f, -1.0f),
                glm::vec3( 1.0f, 1.0f, -1.0f),
                glm::vec3( 1.0f, -1.0f, -1.0f),
                glm::vec3(-1.0f, -1.0f, -1.0f),
                glm::vec3(-1.0f, 1.0f, 1.0f),
                glm::vec3( 1.0f, 1.0f, 1.0f),
                glm::vec3( 1.0f, -1.0f, 1.0f),
                glm::vec3(-1.0f, -1.0f, 1.0f),
        };

        glm::mat4 invViewProj = glm::inverse(camera->getProjectionMatrix() * camera->getViewMatrix());
        for (unsigned int i = 0; i < 8; i++) {
            glm::vec4 inversePoint = invViewProj * glm::vec4(frustumCornersWS[i], 1.0f);
            frustumCornersWS[i] = inversePoint / inversePoint.w;

            //std::cout << "FrustumCorner oldValues" << i << " is: " << glm::to_string(frustumCornersWS[i]) << std::endl;
        }

        // TODO: Modify this if doing all 4 splits

        for (unsigned int i = 0; i < 4; i++) {
            GLfloat prevSplitDistance;
            if (casscadeIndex == 0) {
                prevSplitDistance = 0.1f / 1000.f;
            } else {
                prevSplitDistance = cascadeSplits.at(casscadeIndex - 1);
            }
            GLfloat splitDistance = cascadeSplits.at(casscadeIndex);
            glm::vec3 cornerRay = frustumCornersWS[i + 4] - frustumCornersWS[i];
            glm::vec3 nearCornerRay = cornerRay * prevSplitDistance;
            glm::vec3 farCornerRay = cornerRay * splitDistance;
            frustumCornersWS[i + 4] = frustumCornersWS[i] + farCornerRay;
            frustumCornersWS[i] = frustumCornersWS[i] + nearCornerRay;
        }

        for (unsigned int i = 0; i < 8; i++) {
           // std::cout << "FrustumCorner new values" << i << " is: " << glm::to_string(frustumCornersWS[i]) << std::endl;
        }

        // Finding frustum center by adding 8 corners and dividing by 8
        glm::vec3 frustumCenter = glm::vec3(0.0f);
        for (unsigned int i = 0; i < 8; i++) {
            frustumCenter += frustumCornersWS[i];
        }
        frustumCenter /= 8.0f;

        //std::cout << "FrustumCenter is at: " << glm::to_string(frustumCenter) << std::endl;

        // Building a sphere that encapsulates the view frustum
        GLfloat radius = 0.0f;
        for (unsigned int i = 0; i < 8; i++) {
            GLfloat distance = glm::length(frustumCornersWS[i] - frustumCenter);
            radius = glm::max(radius, distance);
        }
        radius = std::ceil(radius * 16.0f) / 16.0f;

       // std::cout << "Radius is: " << radius << std::endl;

        glm::vec3 lightDirection = frustumCenter - glm::normalize(lights.at(0)->getDirection()) * radius;
        glm::mat4 lightViewMatrix = glm::mat4(1.0f);
        lightViewMatrix = glm::lookAt(lightDirection, frustumCenter, glm::vec3(0.0f, 1.0f, 0.0f));

        glm::mat4 lightOrthoMatrix = glm::ortho(-radius, radius, -radius, radius, 0.f, 2.f * radius);

        // TODO: Maybe calculate splits like this (I believe this is in world pos)
        //std::cout << "split is: " << (camera->getNearClipPlane() + cascadeSplits.at(casscadeIndex) * clipRange) * -1.0f << std::endl;
        cascadeSplitsNew.push_back((camera->getNearClipPlane() + cascadeSplits.at(casscadeIndex) * clipRange));

        // This is used to avoid shimmering when we move the camera
        glm::mat4 shadowMatrix = lightOrthoMatrix * lightViewMatrix;
        glm::vec4 shadowOrigin = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
        shadowOrigin = shadowMatrix * shadowOrigin;
        shadowOrigin = shadowOrigin * static_cast<GLfloat>(directionalLightShadowFrameBuffer->getResolutionX()) / 2.0f;

        glm::vec4 roundedOrigin = glm::round(shadowOrigin);
        glm::vec4 roundOffset = roundedOrigin - shadowOrigin;
        roundOffset = roundOffset * 2.0f / static_cast<GLfloat>(directionalLightShadowFrameBuffer->getResolutionX());
        roundOffset.z = 0.0f;
        roundOffset.w = 0.0f;

        glm::mat4 shadowProj = lightOrthoMatrix;
        shadowProj[3] += roundOffset;
        lightOrthoMatrix = shadowProj;

        glm::mat4 directionalLightShadowVPMatrix = lightOrthoMatrix * lightViewMatrix;
        directionalLightShadowVPMatrixes.push_back(directionalLightShadowVPMatrix);

字符集

7gs2gvoe

7gs2gvoe1#

正如您所发现的,深度夹紧是解决此问题的一种方法。
另一种方法是将灯光视图矩阵从frustumCenter移开。
首先,你的glm::lookAt不对。第一个参数是眼睛的位置,而不是光线的方向。当您移动相机时,灯光视图矩阵应该跟随您。
若要不裁剪场景中的树,需要将眼睛位置从中心移开。举例来说:

lightViewMatrix = glm::lookAt(frustumCenter - lightDirection * factor, ...);

字符集

可能需要增加glm::ortho()函数中最后一个参数的远平面距离。

取决于你的片段着色器是如何计算阴影贴图的,这应该不会导致它停止工作,只要深度贴图的中间点在精确的中心(即深度图中的0.5f值应该指向frustumCenter)。
您还可以根据场景边界计算近平截头体和远平截头体,如此MSDN页面所述:改进阴影深度贴图的常用技术(部分:光平截体与场景相交计算远近平面
在我自己的Vulkan引擎中,我遵循了SaschaWillems的shadowmappingcascade示例,并最终得到如下结果:

float factor = 16.0f;

glm::lookAt(frustumCenter - lightDir * -minExtents.z * factor, frustumCenter, {0.0f, 1.0f, 0.0f});

glm::orthoLH_ZO(minExtents.x, maxExtents.x, minExtents.y, maxExtents.y, 0.0f, (maxExtents.z - minExtents.z) * factor));

**这不是解决此问题的正确方法,但它有效。**推荐的方法是MSDN页面中描述的方法。

相关问题