From f508c4a4b5d79a8da6459a4eaf744c6afc6f2b4a Mon Sep 17 00:00:00 2001 From: Michael Riegger Date: Sat, 24 Apr 2021 00:28:43 -0700 Subject: [PATCH] Initial pointlight shadow work --- .../CoreLights/ViewSrg.azsli | 2 + .../RayTracingSceneSrg.azsli | 2 + .../Shaders/LightCulling/LightCulling.azsl | 2 + .../PointLightFeatureProcessorInterface.h | 3 + .../CoreLights/DiskLightFeatureProcessor.h | 2 +- .../CoreLights/PointLightFeatureProcessor.cpp | 87 +++++++++++++++++++ .../CoreLights/PointLightFeatureProcessor.h | 10 +++ 7 files changed, 107 insertions(+), 1 deletion(-) diff --git a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli index 5404cf8b69..c236730cc1 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli @@ -73,6 +73,8 @@ partial ShaderResourceGroup ViewSrg float m_invAttenuationRadiusSquared; // For a radius at which this light no longer has an effect, 1 / radius^2. float3 m_rgbIntensityCandelas; float m_bulbRadius; + uint m_shadowIndex; + uint m_padding[3]; }; StructuredBuffer m_pointLights; diff --git a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/RayTracingSceneSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/RayTracingSceneSrg.azsli index 0aeb43bcf2..b306ae7e61 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/RayTracingSceneSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/RayTracingSceneSrg.azsli @@ -63,6 +63,8 @@ partial ShaderResourceGroup RayTracingSceneSrg float m_invAttenuationRadiusSquared; float3 m_rgbIntensity; float m_bulbRadius; + uint m_shadowIndex; + uint m_padding[3]; }; StructuredBuffer m_pointLights; diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCulling.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCulling.azsl index 367720d13b..8672350d35 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCulling.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCulling.azsl @@ -59,6 +59,8 @@ ShaderResourceGroup PassSrg : SRG_PerPass float m_invAttenuationRadiusSquared; // For a radius at which this light no longer has an effect, 1 / radius^2. float3 m_rgbIntensityCandelas; float m_bulbRadius; + uint m_shadowIndex; + uint m_padding[3]; }; struct DiskLight diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h index 68e1a01e56..d55e50f012 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h @@ -14,6 +14,7 @@ #include #include +#include namespace AZ { @@ -48,6 +49,8 @@ namespace AZ virtual void SetAttenuationRadius(LightHandle handle, float attenuationRadius) = 0; //! Sets the bulb radius for the provided LightHandle. Values greater than zero effectively make it a spherical light. virtual void SetBulbRadius(LightHandle handle, float bulbRadius) = 0; + //! Sets if shadows are enabled + virtual void SetShadowsEnabled(LightHandle handle, bool enabled) = 0; }; } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h index 446b008921..483f3cb711 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h @@ -84,7 +84,7 @@ namespace AZ template void SetShadowSetting(LightHandle handle, Functor&&, ParamType&& param); - ProjectedShadowFeatureProcessor* m_shadowFeatureProcessor; + ProjectedShadowFeatureProcessor* m_shadowFeatureProcessor = nullptr; IndexedDataVector m_diskLightData; GpuBufferHandler m_lightBufferHandler; diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp index 661a7d4bf4..21d01c642b 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp @@ -54,6 +54,7 @@ namespace AZ desc.m_elementCountSrgName = "m_pointLightCount"; desc.m_elementSize = sizeof(PointLightData); desc.m_srgLayout = RPI::RPISystemInterface::Get()->GetViewSrgAsset()->GetLayout(); + m_shadowFeatureProcessor = GetParentScene()->GetFeatureProcessor(); m_lightBufferHandler = GpuBufferHandler(desc); } @@ -83,6 +84,11 @@ namespace AZ { if (handle.IsValid()) { + ShadowId shadowId = ShadowId(m_pointLightData.GetData(handle.GetIndex()).m_shadowIndex); + if (shadowId.IsValid()) + { + m_shadowFeatureProcessor->ReleaseShadow(shadowId); + } m_pointLightData.RemoveIndex(handle.GetIndex()); m_deviceBufferNeedsUpdate = true; handle.Reset(); @@ -177,5 +183,86 @@ namespace AZ return m_lightBufferHandler.GetElementCount(); } + void PointLightFeatureProcessor::SetShadowsEnabled(LightHandle handle, bool enabled) + { + auto& light = m_pointLightData.GetData(handle.GetIndex()); + ShadowId shadowId = ShadowId(light.m_shadowIndex); + if (shadowId.IsValid() && enabled == false) + { + // Disable shadows + m_shadowFeatureProcessor->ReleaseShadow(shadowId); + shadowId.Reset(); + light.m_shadowIndex = shadowId.GetIndex(); + m_deviceBufferNeedsUpdate = true; + } + else if (shadowId.IsNull() && enabled == true) + { + // Enable shadows + light.m_shadowIndex = m_shadowFeatureProcessor->AcquireShadow().GetIndex(); + + UpdateShadow(handle); + m_deviceBufferNeedsUpdate = true; + } + } + + void PointLightFeatureProcessor::UpdateShadow(LightHandle handle) + { + const auto& pointLight = m_pointLightData.GetData(handle.GetIndex()); + ShadowId shadowId = ShadowId(pointLight.m_shadowIndex); + if (shadowId.IsNull()) + { + // Early out if shadows are disabled. + return; + } + + ProjectedShadowFeatureProcessorInterface::ProjectedShadowDescriptor desc = + m_shadowFeatureProcessor->GetShadowProperties(shadowId); + + Vector3 position = Vector3::CreateFromFloat3(pointLight.m_position.data()); + + constexpr float SmallAngle = 0.01f; + desc.m_fieldOfViewYRadians = 1.57f; + + // To handle bulb radius, set the position of the shadow caster behind the actual light depending on the radius of the bulb + // + // \ / + // \ / + // \_____/ <-- position of light itself (and forward plane of shadow casting view) + // . . + // . . + // * <-- position of shadow casting view + // + desc.m_transform = Transform::CreateLookAt(position, AZ::Vector3::CreateZero()); + + desc.m_aspectRatio = 1.0f; + desc.m_nearPlaneDistance = 0.1f; + + const float invRadiusSquared = pointLight.m_invAttenuationRadiusSquared; + if (invRadiusSquared <= 0.f) + { + AZ_Assert(false, "Attenuation radius have to be set before use the light."); + return; + } + const float attenuationRadius = sqrtf(1.f / invRadiusSquared); + desc.m_farPlaneDistance = attenuationRadius; + + m_shadowFeatureProcessor->SetShadowProperties(shadowId, desc); + } + + template + void PointLightFeatureProcessor::SetShadowSetting(LightHandle handle, Functor&& functor, ParamType&& param) + { + AZ_Assert(handle.IsValid(), "Invalid LightHandle passed to PointLightFeatureProcessor::SetShadowSetting()."); + + auto& light = m_pointLightData.GetData(handle.GetIndex()); + ShadowId shadowId = ShadowId(light.m_shadowIndex); + + AZ_Assert(shadowId.IsValid(), "Attempting to set a shadow property when shadows are not enabled."); + if (shadowId.IsValid()) + { + AZStd::invoke(AZStd::forward(functor), m_shadowFeatureProcessor, shadowId, AZStd::forward(param)); + } + } + } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h index 25b9bfa2cd..8f159ea455 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h @@ -16,6 +16,7 @@ #include #include #include +#include namespace AZ { @@ -31,6 +32,8 @@ namespace AZ float m_invAttenuationRadiusSquared = 0.0f; // Inverse of the distance at which this light no longer has an effect, squared. Also used for falloff calculations. AZStd::array m_rgbIntensity = { { 0.0f, 0.0f, 0.0f } }; float m_bulbRadius = 0.0f; // Radius of spherical light in meters. + uint32_t m_shadowIndex; + uint32_t m_padding[3]; }; class PointLightFeatureProcessor final @@ -58,14 +61,21 @@ namespace AZ void SetPosition(LightHandle handle, const AZ::Vector3& lightPosition) override; void SetAttenuationRadius(LightHandle handle, float attenuationRadius) override; void SetBulbRadius(LightHandle handle, float bulbRadius) override; + void SetShadowsEnabled(LightHandle handle, bool enabled) override; const Data::Instance GetLightBuffer() const; uint32_t GetLightCount()const; private: PointLightFeatureProcessor(const PointLightFeatureProcessor&) = delete; + using ShadowId = ProjectedShadowFeatureProcessor::ShadowId; static constexpr const char* FeatureProcessorName = "PointLightFeatureProcessor"; + void UpdateShadow(LightHandle handle); + // Convenience function for forwarding requests to the ProjectedShadowFeatureProcessor + template + void SetShadowSetting(LightHandle handle, Functor&&, ParamType&& param); + ProjectedShadowFeatureProcessor* m_shadowFeatureProcessor = nullptr; IndexedDataVector m_pointLightData; GpuBufferHandler m_lightBufferHandler;