diff --git a/Source/Engine/Graphics/Enums.h b/Source/Engine/Graphics/Enums.h index bbf9c0de8..6ae9ad4ec 100644 --- a/Source/Engine/Graphics/Enums.h +++ b/Source/Engine/Graphics/Enums.h @@ -236,6 +236,27 @@ API_ENUM(Attributes="Flags") enum class ShadowsCastingMode DECLARE_ENUM_OPERATORS(ShadowsCastingMode); +/// +/// The partitioning mode for shadow cascades. +/// +API_ENUM() enum class PartitionMode +{ + /// + /// Internally defined cascade splits. + /// + Manual = 0, + + /// + /// Logarithmic cascade splits. + /// + Logarithmic = 1, + + /// + /// PSSM cascade splits. + /// + PSSM = 2, +}; + /// /// Identifies expected GPU resource use during rendering. The usage directly reflects whether a resource is accessible by the CPU and/or the GPU. /// diff --git a/Source/Engine/Level/Actors/DirectionalLight.cpp b/Source/Engine/Level/Actors/DirectionalLight.cpp index 43450fd8e..32bb61653 100644 --- a/Source/Engine/Level/Actors/DirectionalLight.cpp +++ b/Source/Engine/Level/Actors/DirectionalLight.cpp @@ -41,6 +41,12 @@ void DirectionalLight::Draw(RenderContext& renderContext) data.RenderedVolumetricFog = 0; data.ShadowsMode = ShadowsMode; data.CascadeCount = CascadeCount; + data.Cascade1Spacing = Cascade1Spacing; + data.Cascade2Spacing = Cascade2Spacing; + data.Cascade3Spacing = Cascade3Spacing; + data.Cascade4Spacing = Cascade4Spacing; + + data.PartitionMode = PartitionMode; data.ContactShadowsLength = ContactShadowsLength; data.StaticFlags = GetStaticFlags(); data.ID = GetID(); @@ -56,6 +62,12 @@ void DirectionalLight::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE_GET_OTHER_OBJ(DirectionalLight); SERIALIZE(CascadeCount); + SERIALIZE(Cascade1Spacing); + SERIALIZE(Cascade2Spacing); + SERIALIZE(Cascade3Spacing); + SERIALIZE(Cascade4Spacing); + + SERIALIZE(PartitionMode); } void DirectionalLight::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) @@ -64,6 +76,12 @@ void DirectionalLight::Deserialize(DeserializeStream& stream, ISerializeModifier LightWithShadow::Deserialize(stream, modifier); DESERIALIZE(CascadeCount); + DESERIALIZE(Cascade1Spacing); + DESERIALIZE(Cascade2Spacing); + DESERIALIZE(Cascade3Spacing); + DESERIALIZE(Cascade4Spacing); + + DESERIALIZE(PartitionMode); } bool DirectionalLight::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) diff --git a/Source/Engine/Level/Actors/DirectionalLight.h b/Source/Engine/Level/Actors/DirectionalLight.h index d5f31324a..cb29112c0 100644 --- a/Source/Engine/Level/Actors/DirectionalLight.h +++ b/Source/Engine/Level/Actors/DirectionalLight.h @@ -12,12 +12,42 @@ class FLAXENGINE_API DirectionalLight : public LightWithShadow { DECLARE_SCENE_OBJECT(DirectionalLight); public: + /// + /// The partitioning mode for the shadow cascades. + /// + API_FIELD(Attributes = "EditorOrder(64), DefaultValue(PartitionMode.Manual), EditorDisplay(\"Shadow\")") + PartitionMode PartitionMode = PartitionMode::Manual; + /// /// The number of cascades used for slicing the range of depth covered by the light during shadow rendering. Values are 1, 2 or 4 cascades; a typical scene uses 4 cascades. /// API_FIELD(Attributes="EditorOrder(65), DefaultValue(4), Limit(1, 4), EditorDisplay(\"Shadow\")") int32 CascadeCount = 4; + /// + /// Percentage of the shadow distance used by the first cascade. + /// + API_FIELD(Attributes = "EditorOrder(66), DefaultValue(0.05f), VisibleIf(nameof(ShowCascade1)), Limit(0, 1, 0.001f), EditorDisplay(\"Shadow\")") + float Cascade1Spacing = 0.05f; + + /// + /// Percentage of the shadow distance used by the second cascade. + /// + API_FIELD(Attributes = "EditorOrder(67), DefaultValue(0.15f), VisibleIf(nameof(ShowCascade2)), Limit(0, 1, 0.001f), EditorDisplay(\"Shadow\")") + float Cascade2Spacing = 0.15f; + + /// + /// Percentage of the shadow distance used by the third cascade. + /// + API_FIELD(Attributes = "EditorOrder(68), DefaultValue(0.50f), VisibleIf(nameof(ShowCascade3)), Limit(0, 1, 0.001f), EditorDisplay(\"Shadow\")") + float Cascade3Spacing = 0.50f; + + /// + /// Percentage of the shadow distance used by the fourth cascade. + /// + API_FIELD(Attributes = "EditorOrder(69), DefaultValue(1.0f), VisibleIf(nameof(ShowCascade4)), Limit(0, 1, 0.001f), EditorDisplay(\"Shadow\")") + float Cascade4Spacing = 1.0f; + public: // [LightWithShadow] void Draw(RenderContext& renderContext) override; diff --git a/Source/Engine/Level/DirectionalLight.cs b/Source/Engine/Level/DirectionalLight.cs new file mode 100644 index 000000000..201d35dc1 --- /dev/null +++ b/Source/Engine/Level/DirectionalLight.cs @@ -0,0 +1,10 @@ +namespace FlaxEngine +{ + public partial class DirectionalLight + { + bool ShowCascade1 => CascadeCount >= 1 && PartitionMode == PartitionMode.Manual; + bool ShowCascade2 => CascadeCount >= 2 && PartitionMode == PartitionMode.Manual; + bool ShowCascade3 => CascadeCount >= 3 && PartitionMode == PartitionMode.Manual; + bool ShowCascade4 => CascadeCount >= 4 && PartitionMode == PartitionMode.Manual; + } +} diff --git a/Source/Engine/Renderer/RenderList.h b/Source/Engine/Renderer/RenderList.h index a2cd48696..3f9a25694 100644 --- a/Source/Engine/Renderer/RenderList.h +++ b/Source/Engine/Renderer/RenderList.h @@ -46,6 +46,12 @@ struct RendererDirectionalLightData float ShadowsDistance; int32 CascadeCount; + float Cascade1Spacing; + float Cascade2Spacing; + float Cascade3Spacing; + float Cascade4Spacing; + + PartitionMode PartitionMode; float ContactShadowsLength; ShadowsCastingMode ShadowsMode; diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index ba326b728..937cd9e99 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -247,19 +247,12 @@ void ShadowsPass::SetupLight(RenderContext& renderContext, RenderContextBatch& r minDistance = cameraNear; maxDistance = cameraNear + shadowsDistance; - // TODO: expose partition mode? - enum class PartitionMode - { - Manual = 0, - Logarithmic = 1, - PSSM = 2, - }; - PartitionMode partitionMode = PartitionMode::Manual; + PartitionMode partitionMode = light.PartitionMode; float pssmFactor = 0.5f; - float splitDistance0 = 0.05f; - float splitDistance1 = 0.15f; - float splitDistance2 = 0.50f; - float splitDistance3 = 1.00f; + float splitDistance0 = light.Cascade1Spacing; + float splitDistance1 = Math::Max(splitDistance0, light.Cascade2Spacing); + float splitDistance2 = Math::Max(splitDistance1, light.Cascade3Spacing); + float splitDistance3 = Math::Max(splitDistance2, light.Cascade4Spacing); // Compute the split distances based on the partitioning mode if (partitionMode == PartitionMode::Manual)