DirectX12 Shadow Mapping阴影贴图无法正常渲染问题排查求助
抱歉为自动翻译内容,表述若有不通顺请谅解。
我目前在*阴影映射(Shadow Mapping)*功能开发中遇到阻塞问题:玩家坐标精确为(2000, 0, 2000),方向光作为阴影映射相机的光源,位于天空正上方位置。


问题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

