|
28 | 28 |
|
29 | 29 | namespace AZ::Render
|
30 | 30 | {
|
| 31 | + namespace |
| 32 | + { |
| 33 | + AZ_CVAR( |
| 34 | + bool, |
| 35 | + r_cullShadowmapOutsideViewFrustum, |
| 36 | + true, |
| 37 | + nullptr, |
| 38 | + AZ::ConsoleFunctorFlags::DontReplicate | AZ::ConsoleFunctorFlags::DontDuplicate, |
| 39 | + "If set, enables filtering of shadow maps that are outside of the view frustum."); |
| 40 | + |
| 41 | + bool IsShadowmapCullingEnabled() |
| 42 | + { |
| 43 | + bool cullShadowmapOutsideViewFrustum = true; |
| 44 | + if (auto* console = AZ::Interface<AZ::IConsole>::Get()) |
| 45 | + { |
| 46 | + console->GetCvarValue("r_cullShadowmapOutsideViewFrustum", cullShadowmapOutsideViewFrustum); |
| 47 | + } |
| 48 | + return cullShadowmapOutsideViewFrustum; |
| 49 | + } |
| 50 | + |
| 51 | + bool IsLightInsideAnyViewFrustum(AZStd::span<AZ::Frustum> viewFrustums, const AZ::Vector3& lightPosition, float attenuationRadius) |
| 52 | + { |
| 53 | + return std::any_of( |
| 54 | + viewFrustums.begin(), |
| 55 | + viewFrustums.end(), |
| 56 | + [lightPosition, attenuationRadius](const AZ::Frustum& viewFrustum) |
| 57 | + { |
| 58 | + return viewFrustum.IntersectSphere(lightPosition, attenuationRadius) != AZ::IntersectResult::Exterior; |
| 59 | + }); |
| 60 | + } |
| 61 | + |
| 62 | + } // namespace |
| 63 | + |
31 | 64 | void ProjectedShadowFeatureProcessor::Reflect(ReflectContext* context)
|
32 | 65 | {
|
33 | 66 | if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
|
@@ -631,23 +664,41 @@ namespace AZ::Render
|
631 | 664 | }
|
632 | 665 | }
|
633 | 666 | }
|
634 |
| - |
635 |
| - void ProjectedShadowFeatureProcessor::PrepareViews(const PrepareViewsPacket&, AZStd::vector<AZStd::pair<RPI::PipelineViewTag, RPI::ViewPtr>>& outViews) |
| 667 | + |
| 668 | + void ProjectedShadowFeatureProcessor::PrepareViews( |
| 669 | + const PrepareViewsPacket& prepareViewsPacket, AZStd::vector<AZStd::pair<RPI::PipelineViewTag, RPI::ViewPtr>>& outViews) |
636 | 670 | {
|
637 | 671 | if (m_primaryProjectedShadowmapsPass != nullptr)
|
638 | 672 | {
|
639 | 673 | RPI::RenderPipeline* renderPipeline = m_primaryProjectedShadowmapsPass->GetRenderPipeline();
|
640 | 674 | if (renderPipeline)
|
641 | 675 | {
|
| 676 | + AZStd::vector<AZ::Frustum> mainViewFrustums; |
| 677 | + for (const auto& [view, viewTag] : prepareViewsPacket.m_persistentViews) |
| 678 | + { |
| 679 | + AZ::Frustum viewFrustum = AZ::Frustum::CreateFromMatrixColumnMajor(view->GetWorldToClipMatrix()); |
| 680 | + mainViewFrustums.push_back(viewFrustum); |
| 681 | + } |
| 682 | + bool cullShadowmapOutsideViewFrustum = IsShadowmapCullingEnabled(); |
| 683 | + |
642 | 684 | auto& shadowProperties = m_shadowProperties.GetDataVector();
|
643 | 685 | for (ShadowProperty& shadowProperty : shadowProperties)
|
644 | 686 | {
|
645 | 687 | uint16_t shadowIndex = shadowProperty.m_shadowId.GetIndex();
|
646 | 688 | const FilterParameter& filterData = m_shadowData.GetElement<FilterParamIndex>(shadowIndex);
|
647 |
| - if (filterData.m_shadowmapSize == aznumeric_cast<uint32_t>(ShadowmapSize::None)) |
| 689 | + bool needsRender = filterData.m_shadowmapSize != aznumeric_cast<uint32_t>(ShadowmapSize::None); |
| 690 | + if (!needsRender) |
| 691 | + { |
| 692 | + continue; |
| 693 | + } |
| 694 | + auto lightPosition = shadowProperty.m_desc.m_transform.GetTranslation(); |
| 695 | + if (cullShadowmapOutsideViewFrustum && |
| 696 | + !IsLightInsideAnyViewFrustum(mainViewFrustums, lightPosition, shadowProperty.m_desc.m_farPlaneDistance)) |
648 | 697 | {
|
| 698 | + shadowProperty.m_shadowmapPass->SetEnabled(false); |
649 | 699 | continue;
|
650 | 700 | }
|
| 701 | + shadowProperty.m_shadowmapPass->SetEnabled(true); |
651 | 702 |
|
652 | 703 | const RPI::PipelineViewTag& viewTag = shadowProperty.m_shadowmapPass->GetPipelineViewTag();
|
653 | 704 | const RHI::DrawListMask drawListMask = renderPipeline->GetDrawListMask(viewTag);
|
|
0 commit comments