diff --git a/Source/Engine/Level/Actors/DirectionalLight.cpp b/Source/Engine/Level/Actors/DirectionalLight.cpp index cef1eba97..97f9c3b58 100644 --- a/Source/Engine/Level/Actors/DirectionalLight.cpp +++ b/Source/Engine/Level/Actors/DirectionalLight.cpp @@ -40,6 +40,7 @@ void DirectionalLight::Draw(RenderContext& renderContext) data.CastVolumetricShadow = CastVolumetricShadow; data.ShadowsUpdateRate = ShadowsUpdateRate; data.ShadowFrame = _invalidateShadowFrame; + data.ShadowsResolution = (int32)ShadowsResolution; data.ShadowsUpdateRateAtDistance = ShadowsUpdateRateAtDistance; data.ShadowsMode = ShadowsMode; data.CascadeCount = CascadeCount; diff --git a/Source/Engine/Level/Actors/Light.h b/Source/Engine/Level/Actors/Light.h index 8fd40c530..9c3d6ff73 100644 --- a/Source/Engine/Level/Actors/Light.h +++ b/Source/Engine/Level/Actors/Light.h @@ -66,7 +66,7 @@ public: const Vector3 size(50); return BoundingBox(_transform.Translation - size, _transform.Translation + size); } - + virtual void DrawLightsDebug(RenderView& view); #endif void Serialize(SerializeStream& stream, const void* otherObj) override; @@ -79,6 +79,26 @@ public: API_CLASS(Abstract) class FLAXENGINE_API LightWithShadow : public Light { DECLARE_SCENE_OBJECT_ABSTRACT(LightWithShadow); + + /// + /// List of fixed resolutions for light shadow map. + /// + API_ENUM() enum class ShadowMapResolution + { + // Use automatic dynamic resolution based on distance to view. + Dynamic = 0, + // Shadow map of size 128x128. + _128 = 128, + // Shadow map of size 256x256. + _256 = 256, + // Shadow map of size 512x512. + _512 = 512, + // Shadow map of size 1024x1024. + _1024 = 1024, + // Shadow map of size 2048x2048. + _2048 = 2048, + }; + protected: uint32 _invalidateShadowFrame = 0; @@ -143,12 +163,18 @@ public: API_FIELD(Attributes="EditorOrder(105), EditorDisplay(\"Shadow\", \"Update Rate At Distance\"), Limit(0.0f, 1.0f)") float ShadowsUpdateRateAtDistance = 0.5f; + /// + /// Defines the resolution of the shadow map texture used to draw objects projection from light-point-of-view. Higher values increase shadow quality at cost of performance. + /// + API_FIELD(Attributes="EditorOrder(105), EditorDisplay(\"Shadow\", \"Resolution\")") + ShadowMapResolution ShadowsResolution = ShadowMapResolution::Dynamic; + /// /// Describes how a visual element casts shadows. /// API_FIELD(Attributes="EditorOrder(60), EditorDisplay(\"Shadow\", \"Mode\")") ShadowsCastingMode ShadowsMode = ShadowsCastingMode::All; - + /// /// Marks the light shadow to be refreshes during next drawing. Invalidates any cached shadow map and redraws static shadows of the object (if any in use). /// diff --git a/Source/Engine/Level/Actors/PointLight.cpp b/Source/Engine/Level/Actors/PointLight.cpp index 5f2be645b..3c85be612 100644 --- a/Source/Engine/Level/Actors/PointLight.cpp +++ b/Source/Engine/Level/Actors/PointLight.cpp @@ -106,6 +106,7 @@ void PointLight::Draw(RenderContext& renderContext) data.ShadowsUpdateRate = ShadowsUpdateRate; data.ShadowsUpdateRateAtDistance = ShadowsUpdateRateAtDistance; data.ShadowFrame = _invalidateShadowFrame; + data.ShadowsResolution = (int32)ShadowsResolution; data.ShadowsMode = ShadowsMode; data.Radius = radius; data.FallOffExponent = FallOffExponent; diff --git a/Source/Engine/Level/Actors/SpotLight.cpp b/Source/Engine/Level/Actors/SpotLight.cpp index 892bdf02e..38457c94e 100644 --- a/Source/Engine/Level/Actors/SpotLight.cpp +++ b/Source/Engine/Level/Actors/SpotLight.cpp @@ -156,6 +156,7 @@ void SpotLight::Draw(RenderContext& renderContext) data.ShadowsUpdateRate = ShadowsUpdateRate; data.ShadowsUpdateRateAtDistance = ShadowsUpdateRateAtDistance; data.ShadowFrame = _invalidateShadowFrame; + data.ShadowsResolution = (int32)ShadowsResolution; data.ShadowsMode = ShadowsMode; data.Radius = radius; data.FallOffExponent = FallOffExponent; diff --git a/Source/Engine/Renderer/RenderList.h b/Source/Engine/Renderer/RenderList.h index 8f15175be..8f8e86973 100644 --- a/Source/Engine/Renderer/RenderList.h +++ b/Source/Engine/Renderer/RenderList.h @@ -59,6 +59,7 @@ struct RenderLightData float ShadowsUpdateRate; float ShadowsUpdateRateAtDistance; uint32 ShadowFrame; + int32 ShadowsResolution; bool CanRenderShadow(const RenderView& view) const; }; diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index 59d9bf45a..a9920ffb2 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -142,6 +142,7 @@ struct ShadowAtlasLightCache float Distance; Float4 CascadeSplits; Float3 ViewDirection; + int32 ShadowsResolution; void Set(const RenderView& view, const RenderLightData& light, const Float4& cascadeSplits = Float4::Zero) { @@ -152,6 +153,7 @@ struct ShadowAtlasLightCache ShadowsUpdateRateAtDistance = light.ShadowsUpdateRateAtDistance; Direction = light.Direction; ShadowFrame = light.ShadowFrame; + ShadowsResolution = light.ShadowsResolution; if (light.IsDirectionalLight) { // Sun @@ -247,6 +249,7 @@ struct ShadowAtlasLight !Math::NearEqual(Cache.ShadowsUpdateRate, light.ShadowsUpdateRate) || !Math::NearEqual(Cache.ShadowsUpdateRateAtDistance, light.ShadowsUpdateRateAtDistance) || Cache.ShadowFrame != light.ShadowFrame || + Cache.ShadowsResolution != light.ShadowsResolution || Float3::Dot(Cache.Direction, light.Direction) < SHADOWS_ROTATION_ERROR) { // Invalidate @@ -1119,9 +1122,12 @@ void ShadowsPass::SetupShadows(RenderContext& renderContext, RenderContextBatch& auto& atlasLight = shadows.Lights[light->ID]; // Calculate resolution for this light - // TODO: add support for fixed shadow map resolution assigned per-light - float lightResolutionFloat = baseLightResolution * light->ScreenSize; - atlasLight.Resolution = QuantizeResolution(lightResolutionFloat); + atlasLight.Resolution = light->ShadowsResolution; + if (atlasLight.Resolution == 0) + { + // ScreenSize-based automatic shadowmap resolution + atlasLight.Resolution = QuantizeResolution(baseLightResolution * light->ScreenSize); + } // Cull too small lights if (atlasLight.Resolution < SHADOWS_MIN_RESOLUTION)