You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DirectX11级联阴影地图出现无限FOV异常问题求助

DirectX11级联阴影地图(CSM)无限FOV异常问题

用DirectX11实现级联阴影地图(CSM),参考LearnOpenGL的CSM教程,灯光设置为垂直向下照射,但最终生成的阴影图出现无限FOV的异常效果。

核心代码片段

LookAt函数实现

template <class T>
inline CU::Matrix4x4<T> CU::Matrix4x4<T>::LookAt(CU::Vector3f aEye, CU::Vector3f aCenter, CU::Vector3f aUp)
{
    CU::Matrix4x4<T> mat;

    auto Z = aCenter - aEye;
    Z.Normalize();

    auto X = Z.Cross(aUp);
    X.Normalize();

    if (!X.Length())
    {
        aUp = CU::Vector3f(0, 0, 1);

        X = Z.Cross(aUp);
        X.Normalize();
    }

    auto Y = X.Cross(Z);
    Y.Normalize();

    mat.SetRow(1, X.ToVector4(( - X.Dot(aEye))));
    mat.SetRow(2, Y.ToVector4(( - Y.Dot(aEye))));
    mat.SetRow(3, (-Z).ToVector4((Z.Dot(aEye))));
    mat.SetRow(4, CU::Vector4f(0, 0, 0, 1));

    return mat.GetTranspose();
}

视锥体顶点转换到灯光空间代码

auto vec = MainSingleton::Get()->mySceneManager->GetCamera()->GetCameraData().GetFrustumCornersWorldSpace();

std::vector<std::pair<CU::Vector3f, CU::Vector3f>> minMax;
std::vector <CU::Matrix4x4f> views;
constexpr float zMult = 100.0f;
for (auto& v : vec)
{
    CU::Vector3f center;
    for (auto& vP : v)
    {
        center += vP;
    }
    center /= static_cast<float>(v.size());

    views.emplace_back(CU::Matrix4x4f::LookAt(center + myDirectionalLights.front()->GetData().direction, center, CU::Vector3f(0.f, 1.f, 0.f)));

    //views.back().SetRow(4, myDirectionalLights.front()->gameObject->GetTransform().GetPosition().ToVector4(1));

    minMax.emplace_back();

    minMax.back().first.x = std::numeric_limits<float>::max();
    minMax.back().first.y = std::numeric_limits<float>::max();
    minMax.back().first.z = std::numeric_limits<float>::max();

    minMax.back().second.x = std::numeric_limits<float>::lowest();
    minMax.back().second.y = std::numeric_limits<float>::lowest();
    minMax.back().second.z = std::numeric_limits<float>::lowest();

    for (auto& vP : v)
    {
        const auto trf = vP * views.back();

        minMax.back().first.x = std::min(minMax.back().first.x, trf.x);
        minMax.back().first.y = std::min(minMax.back().first.y, trf.y);
        minMax.back().first.z = std::min(minMax.back().first.z, trf.z);

        minMax.back().second.x = std::max(minMax.back().second.x, trf.x);
        minMax.back().second.y = std::max(minMax.back().second.y, trf.y);
        minMax.back().second.z = std::max(minMax.back().second.z, trf.z);
    }

    if (minMax.back().first.z < 0)
        minMax.back().first.z *= zMult;
    else
        minMax.back().first.z /= zMult;

    if (minMax.back().second.z < 0)
        minMax.back().second.z /= zMult;
    else
        minMax.back().second.z *= zMult;

}

阴影图渲染代码

std::wstringstream stream;
stream << "Shadow map nr: " << index;
myRHI->BeginRenderTag(stream.str().c_str());

GraphicsEngine::GetRHI()->GetContext()->ClearDepthStencilView(myShadowMaps[index]->GetDepthStencilView().Get(), D3D11_CLEAR_DEPTH, 1.0f, 0);

ID3D11ShaderResourceView* nullView = nullptr;
GraphicsEngine::GetRHI()->GetContext()->PSSetShaderResources(105 + index, 1, &nullView);

GraphicsEngine::GetRHI()->GetContext()->OMSetRenderTargets(0, nullptr, myShadowMaps[index]->GetDepthStencilView().Get());

CU::Camera camera;
auto val = miniMax[index];

//camera.GetSettings().myNearPlane = 10;
//camera.GetSettings().myFarPlane = 500000;

//float x = val.second.x - val.first.x;
//float y = val.second.y - val.first.y;


float left = val.first.x;
float right = val.second.x;
float bottom = val.first.y;
float top = val.second.y;
float nearPlane = val.first.z;
float farPlane = val.second.z;

camera.Init(left, right, bottom, top, nearPlane, farPlane);

auto& timer = CU::Time::Get();
myFrameBuffer.myCameraPosition = view[index].GetRowVector(4);
myFrameBuffer.myInverseViewMatrix = view[index].GetInverse();
myFrameBuffer.myProjectionMatrix = camera.GetClipSpace();
myFrameBuffer.myScreenResolution = GetResolution();
myFrameBuffer.myDeltaTime = timer.GetDeltaTime();
myFrameBuffer.myUnscaledDeltaTime = timer.GetDeltaTimeUnscaled();
myFrameBuffer.myTotalTime = timer.GetTotalTime();
myFrameBuffer.myForward = cam->GetData().direction;
UpdateAndSetConstantBuffer(ConstantBufferType::FrameBuffer, myFrameBuffer);

//this->SetFrameBuffer(cam->GetShadowCamera());

//auto& transform = cam->GetShadowCamera()->GetTransform();
//myFrameBuffer.myCameraPosition = transform.GetPosition().ToVector4(1.f);
//myFrameBuffer.myInverseViewMatrix = cam->GetShadowCamera()->GetView().GetInverse();
//myFrameBuffer.myForward = transform.Forward().GetNormalized();

D3D11_VIEWPORT viewport = {};
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = 4096;
viewport.Height = 4096;
viewport.MaxDepth = 1.0f;
myRHI->GetContext()->RSSetViewports(1, &viewport);


GraphicsEngine::GetRHI()->GetContext()->PSSetShader(nullptr, nullptr, 0);

视锥体顶点获取函数

std::vector< std::vector<CU::Vector3f>> Camera::GetFrustumCornersWorldSpace()
{
    std::vector< std::vector<CU::Vector3f>> frustumCorners;

    const Vector3f up = myTransform.Up();
    const Vector3f right = myTransform.Right();

    const int nbSplits = 3;

    std::vector<float> splitpoints{10, 100, 300, 1000};

    //for (int i = 0; i < nbSplits; i++)
    //{
    //  float ratio = static_cast<float>(i) / nbSplits;
    //  splitpoints[i] = mySettings.myNearPlane + ratio * (mySettings.myFarPlane - mySettings.myNearPlane);
    //}

    for (int i = 1; i < nbSplits + 1; i++)
    {
        float fakeNearPlane = splitpoints[i - 1];
        float fakeFarPlane = splitpoints[i];

        const float farHeight = std::tan(mySettings.myFov * Deg2Rad * 0.5f) * fakeFarPlane;
        const float farWidth = farHeight * (16.f / 9.f);
        const float nearHeight = std::tan(mySettings.myFov * Deg2Rad * 0.5f) * fakeNearPlane;
        const float nearWidth = nearHeight * (16.f / 9.f);

        const Vector3f farCentre = myTransform.GetPosition() + myTransform.Forward() * fakeFarPlane;
        const Vector3f ftr = farCentre + up * farHeight + right * farWidth; //Far Top Right
        const Vector3f ftl = farCentre + up * farHeight - right * farWidth; //Far Top Left
        const Vector3f fbr = farCentre - up * farHeight + right * farWidth; //Far Bottom Right
        const Vector3f fbl = farCentre - up * farHeight - right * farWidth; //Far Bottom Left

        frustumCorners.emplace_back();

        frustumCorners.back().emplace_back(ftr);
        frustumCorners.back().emplace_back(ftl);
        frustumCorners.back().emplace_back(fbr);
        frustumCorners.back().emplace_back(fbl);

        const Vector3f nearCentre = myTransform.GetPosition() + myTransform.Forward() * fakeNearPlane;
        const Vector3f ntr = nearCentre + up * nearHeight + right * nearWidth;//Near Top Right
        const Vector3f ntl = nearCentre + up * nearHeight - right * nearWidth;//Near Top Left
        const Vector3f nbr = nearCentre - up * nearHeight + right * nearWidth;//Near Bottom Right
        const Vector3f nbl = nearCentre - up * nearHeight - right * nearWidth;//Near Bottom Left

        frustumCorners.back().emplace_back(ntr);
        frustumCorners.back().emplace_back(ntl);
        frustumCorners.back().emplace_back(nbr);
        frustumCorners.back().emplace_back(nbl);
    }

    return frustumCorners;
}

异常效果

生成的阴影图呈现无限FOV异常:阴影被过度拉伸,覆盖范围远超相机视锥体,画面中出现大量不应存在的阴影区域,整体表现为视野无限扩大的错误状态。

已尝试的解决方案

  • 修改裁剪矩阵参数
  • 放大平行光方向向量以模拟灯光拉远
  • 对裁剪空间矩阵、投影矩阵及其逆矩阵执行求逆和转置操作

以上方案均未解决问题,恳请提供技术帮助。


内容的提问来源于stack exchange,提问作者Simon Henriksson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 08:30:55