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

DirectX12 Shadow Mapping阴影贴图无法正常渲染问题排查求助

抱歉为自动翻译内容,表述若有不通顺请谅解。

我目前在*阴影映射(Shadow Mapping)*功能开发中遇到阻塞问题:玩家坐标精确为(2000, 0, 2000),方向光作为阴影映射相机的光源,位于天空正上方位置。

问题截图1
问题截图2

问题1

阴影映射逻辑判定异常:实现Shadow Mapping功能需要依赖阴影贴图,当前阴影贴图功能未正常工作。理论上只要阴影映射相机的视口覆盖对应空间,阴影贴图就应正常渲染,且初始化为1.0f的区域都应判定为处于阴影外。当前阴影映射相机位置为(2000, 100, 2000),聚焦位置为(0, 0, 0),但原本应判定为阴影外的红色正方形区域未被正确识别,反而绿色正方形区域被判定为阴影外。备注:视口内没有遮挡光线的物体,截图中可见的阴影属于视口外区域,是阴影贴图返回0.0f判定生成的。

问题2

这是核心根本问题:阴影Pass(Shadow Pass)完全无法将内容渲染到阴影贴图中。只要该问题解决,问题1可以逐步排查,但当前渲染环节本身失效,没有物体阴影生成,原因不明。

代码中CShadowShader类的ShadowPassRender方法负责渲染阴影贴图,影响该环节的核心是光源侧生成的View-Projection矩阵(对应ShadowShader类的UpdateShaderVariables部分),这是最怀疑的出错点,但实现流程和参考示例没有差异,无法定位错误点。当前使用Blinn-Phong光照模型,参照Frank Luna《Introduction to 3D Game Programming with DirectX 12》第20章Shadow Mapping内容实现,项目使用自研框架,和示例框架差异较大。

核心实现代码

阴影映射View-Projection矩阵生成代码

void CShadowShader::UpdateShaderVariables(ID3D12GraphicsCommandList* pd3dCommandList, XMFLOAT3 xmf3TargetPos)
{
    XMFLOAT3 TargetPos = {950, 0, 950};
    XMMATRIX lightView = XMMatrixLookAtLH(XMLoadFloat3(&m_pLight->GetPosition()), XMLoadFloat3(&TargetPos), XMLoadFloat3(&m_pLight->GetUp()));

    // Transform bounding sphere to light space.
    XMFLOAT3 xmf3CenterLS;
    XMStoreFloat3(&xmf3CenterLS, XMVector3TransformCoord(XMLoadFloat3(&TargetPos), lightView));

    // Ortho frustum in light space encloses scene.

    float l = xmf3CenterLS.x - 3000;
    float b = xmf3CenterLS.y - 3000;
    float n = xmf3CenterLS.z - 3000;
    float r = xmf3CenterLS.x + 3000;
    float t = xmf3CenterLS.y + 3000;
    float f = xmf3CenterLS.z + 3000;

    XMMATRIX lightProj = XMMatrixOrthographicOffCenterLH(l, r, b, t, n, f);

    // Transform NDC space [-1,+1]^2 to texture space [0,1]^2
    XMMATRIX T(
        0.5f, 0.0f, 0.0f, 0.0f,
        0.0f, -0.5f, 0.0f, 0.0f,
        0.0f, 0.0f, 1.0f, 0.0f,
        0.5f, 0.5f, 0.0f, 1.0f);

    XMMATRIX S = lightView * lightProj * T;

    XMFLOAT4X4 m_xmf4x4ShadowTransform;

    XMStoreFloat4x4(&m_xmf4x4ShadowTransform, S);

    CB_SHADOW cbShadow{ m_xmf4x4ShadowTransform, m_pLight->GetPosition() };

    m_ubShadowCB->CopyData(0, cbShadow); 
    pd3dCommandList->SetGraphicsRootConstantBufferView(3, m_ubShadowCB->Resource()->GetGPUVirtualAddress());
}

阴影Pass PSO创建代码

void CShadowShader::CreateShader(ID3D12Device* pd3dDevice, ID3D12RootSignature* pd3dGraphicsRootSignature)
{
    m_ubShadowCB = new UploadBuffer<CB_SHADOW>(pd3dDevice, 1, true);

    ID3DBlob* pd3dVertexShaderBlob = NULL, * pd3dPixelShaderBlob = NULL;

    D3D12_GRAPHICS_PIPELINE_STATE_DESC d3dPipelineStateDesc;
    ::ZeroMemory(&d3dPipelineStateDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
    d3dPipelineStateDesc.pRootSignature = pd3dGraphicsRootSignature;
    d3dPipelineStateDesc.VS = CreateVertexShader(&pd3dVertexShaderBlob);
    d3dPipelineStateDesc.PS = CreatePixelShader(&pd3dPixelShaderBlob);
    d3dPipelineStateDesc.RasterizerState = CreateRasterizerState();

    d3dPipelineStateDesc.RasterizerState.DepthBias = 10000.0f;
    d3dPipelineStateDesc.RasterizerState.DepthBiasClamp = 0.0f;
    d3dPipelineStateDesc.RasterizerState.SlopeScaledDepthBias = 1.0f;

    d3dPipelineStateDesc.BlendState = CreateBlendState();
    d3dPipelineStateDesc.DepthStencilState = CreateDepthStencilState();
    d3dPipelineStateDesc.InputLayout = CreateInputLayout();
    d3dPipelineStateDesc.SampleMask = UINT_MAX;
    d3dPipelineStateDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
    d3dPipelineStateDesc.NumRenderTargets = 0;
    d3dPipelineStateDesc.RTVFormats[0] = DXGI_FORMAT_UNKNOWN;
    d3dPipelineStateDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
    d3dPipelineStateDesc.SampleDesc.Count = 1;
    d3dPipelineStateDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
    auto tmp = pd3dDevice->CreateGraphicsPipelineState(&d3dPipelineStateDesc, __uuidof(ID3D12PipelineState), (void**)&m_pd3dPipelineState);


    if (pd3dVertexShaderBlob)
        pd3dVertexShaderBlob->Release();

    if (pd3dPixelShaderBlob)
        pd3dPixelShaderBlob->Release();

    if (d3dPipelineStateDesc.InputLayout.pInputElementDescs)
        delete[] d3dPipelineStateDesc.InputLayout.pInputElementDescs;
}

阴影Pass着色器代码

#include "Common.hlsli"

struct VertexIn
{
    float3 PosL : POSITION;
};

struct VertexOut
{
    float4 PosH : SV_POSITION;
};

VertexOut VS(VertexIn vin)
{
    VertexOut vout = (VertexOut) 0.0f;

    MATERIAL matData = material;
    
    // Transform to world space.
    float4 posW = mul(float4(vin.PosL, 1.0f), gmtxWorld);

    // Transform to homogeneous clip space.
    vout.PosH = mul(posW, gmtxShadowTransform);
    
    return vout;
}

// This is only used for alpha cut out geometry, so that shadows 
// show up correctly.  Geometry that does not need to sample a
// texture can use a NULL pixel shader for depth pass.
void PS(VertexOut pin)
{
    // Fetch the material data.
    MATERIAL matData = material;
    float4 diffuseAlbedo = matData.DiffuseAlbedo;
}

主渲染Pass Default.hlsl代码(含少量韩文注释无影响)

#include "Common.hlsli"

//정점 셰이더의 입력을 위한 구조체를 선언한다.
struct VS_DEFAULT_INPUT
{
    float3 position : POSITION;
    float3 normal : NORMAL;
};

//정점 셰이더의 출력(픽셀 셰이더의 입력)을 위한 구조체를 선언한다.
struct VS_DEFAULT_OUTPUT
{
    float4 position : SV_POSITION;
    float4 position_shadow : POSITION0;
    float3 position_w : POSITION1;
    float3 normal : NORMAL;
};


VS_DEFAULT_OUTPUT VS_Default(VS_DEFAULT_INPUT input)
{
    VS_DEFAULT_OUTPUT output;
    output.position = mul(mul(float4(input.position, 1.0f), gmtxWorld), gmtxViewProj);
    output.position_w = mul(float4(input.position, 1.0f), gmtxWorld).xyz;
    output.normal = normalize(mul(float4(input.normal, 0.0f), gmtxWorld).xyz);
    output.position_shadow = mul(float4(output.position_w, 1.0f), gmtxShadowTransform);
    
    return (output);
}

float4 PS_Default(VS_DEFAULT_OUTPUT input) : SV_TARGET
{
    float4 cColor = float4(0.0f, 0.0f, 0.0f, 0.0f);
    
    cColor += material.AmbientLight * material.DiffuseAlbedo;
    
    float3 toEyeW = normalize(cameraPos - input.position_w);
    
    float3 shadowFactor = float3(1.0f, 1.0f, 1.0f);
    shadowFactor[0] = CalcShadowFactor(input.position_shadow);
    
    for (int i = 0; i < nLights; i++)
    {
        cColor += ComputeLighting(light[i], input.position_w, input.normal, toEyeW, shadowFactor[0]);
    }
    // Add in specular reflections.
    
    float3 r = reflect(-toEyeW, input.normal);
    float4 reflectionColor = { 1.0f, 1.0f, 1.0f, 0.0f };
    
    float3 fresnelFactor = SchlickFresnel(material.FresnelR0, input.normal, r);
    cColor.rgb += material.Shininess * fresnelFactor * reflectionColor.rgb;
    
    // Common convention to take alpha from diffuse albedo.
    
    cColor.a = material.DiffuseAlbedo.a;
    
    
    return (cColor);
}

项目信息

项目集成了Bullet物理引擎,构建前需要自行下载并对接Bullet引擎到项目中。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 09:00:04