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)