Skip to content

Commit 5c3ffc9

Browse files
authored
Merge pull request o3de#17654 from RobotecAI/mw/shadow_culling
Shadow culling outside view frustum
2 parents 69d0bab + 88976dd commit 5c3ffc9

File tree

4 files changed

+57
-6
lines changed

4 files changed

+57
-6
lines changed

Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,39 @@
2828

2929
namespace AZ::Render
3030
{
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+
3164
void ProjectedShadowFeatureProcessor::Reflect(ReflectContext* context)
3265
{
3366
if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
@@ -631,14 +664,23 @@ namespace AZ::Render
631664
}
632665
}
633666
}
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)
636670
{
637671
if (m_primaryProjectedShadowmapsPass != nullptr)
638672
{
639673
RPI::RenderPipeline* renderPipeline = m_primaryProjectedShadowmapsPass->GetRenderPipeline();
640674
if (renderPipeline)
641675
{
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+
642684
auto& shadowProperties = m_shadowProperties.GetDataVector();
643685
for (ShadowProperty& shadowProperty : shadowProperties)
644686
{
@@ -648,6 +690,12 @@ namespace AZ::Render
648690
{
649691
continue;
650692
}
693+
auto lightPosition = shadowProperty.m_desc.m_transform.GetTranslation();
694+
if (cullShadowmapOutsideViewFrustum &&
695+
!IsLightInsideAnyViewFrustum(mainViewFrustums, lightPosition, shadowProperty.m_desc.m_farPlaneDistance))
696+
{
697+
continue;
698+
}
651699

652700
const RPI::PipelineViewTag& viewTag = shadowProperty.m_shadowmapPass->GetPipelineViewTag();
653701
const RHI::DrawListMask drawListMask = renderPipeline->GetDrawListMask(viewTag);

Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ namespace AZ::Render
3939
void Activate() override;
4040
void Deactivate() override;
4141
void Simulate(const SimulatePacket& packet) override;
42-
void PrepareViews(const PrepareViewsPacket&, AZStd::vector<AZStd::pair<RPI::PipelineViewTag, RPI::ViewPtr>>&) override;
42+
void PrepareViews(
43+
const PrepareViewsPacket& prepareViewsPacket, AZStd::vector<AZStd::pair<RPI::PipelineViewTag, RPI::ViewPtr>>&) override;
4344
void Render(const RenderPacket& packet) override;
4445

4546
// ProjectedShadowFeatureProcessorInterface overrides ...

Gems/Atom/RPI/Code/Include/Atom/RPI.Public/FeatureProcessor.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ namespace AZ
4646
: public SceneNotificationBus::Handler
4747
{
4848
friend class Scene;
49-
public:
5049

51-
// [GFX TODO]: now these structure are empty, but we will clean up them later when we are sure we won't have any members in them.
50+
public:
5251
struct PrepareViewsPacket
5352
{
53+
AZStd::map<ViewPtr, RHI::DrawListMask> m_persistentViews;
5454
};
5555

5656
struct SimulatePacket
@@ -105,7 +105,8 @@ namespace AZ
105105
//! views (transient views) are views that must be rendered only to correctly render
106106
//! the main views. This function is called per frame and it happens on main thread.
107107
//! Support views should be added to outViews with their associated pipeline view tags.
108-
virtual void PrepareViews(const PrepareViewsPacket&, AZStd::vector<AZStd::pair<PipelineViewTag, ViewPtr>>& /* outViews*/) {}
108+
virtual void PrepareViews(
109+
const PrepareViewsPacket& /*prepareViewPacket*/, AZStd::vector<AZStd::pair<PipelineViewTag, ViewPtr>>& /*outViews*/) {}
109110

110111
//! The feature processor should perform any internal simulation at this point - For
111112
//! instance, updating a particle system or animation. Not every feature processor

Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,7 @@ namespace AZ
786786

787787
// Collect transient views from each feature processor
788788
FeatureProcessor::PrepareViewsPacket prepareViewPacket;
789+
prepareViewPacket.m_persistentViews = persistentViews;
789790
AZStd::vector<AZStd::pair<PipelineViewTag, ViewPtr>> transientViews;
790791
for (auto& fp : m_featureProcessors)
791792
{

0 commit comments

Comments
 (0)