For someone who has been working with Unreal Engine for just about a year, the learning curve can feel pretty steep. The engine exposes a huge number of properties and console variables, and when you first encounter them, it can feel like opening a control panel with way too many buttons and absolutely no labels.
In this blog, I'll talk about a fairly simple concept (although it felt quite complex to me when I first ran into it) and try to explain it in simpler terms. Think of this as me documenting the things I painfully figured out so that future-me doesn't have to go down the same rabbit hole again. And if this ends up helping someone else who is also staring at the same mysterious settings panel… well, that's a nice bonus.
The Problem
We have a Directional Light set up in a large level that is responsible for casting shadows on a few selected actors. Interestingly, when these shadows were cast on a vehicle, they appeared sharp at close range but started getting noticeably blurrier just around 2–3 meters away from the camera.
Naturally, the expectation was for the shadow to remain crisp and seamless, without this abrupt drop in quality.
Is the Quick Fix the Best Fix?
After a quick look into the famous BaseScalability.ini (which sits in the Config folder of the Engine), I found a scalability group setting for shadows. Scalability Groups are divided into Low, Medium, High, Epic, and Cinematic — corresponding to identifiers 0, 1, 2, 3, and Cine in the ini file.
Since I was debugging this on a high-end device, I looked at the CVars under [ShadowQuality@2] and tweaked r.Shadow.DistanceScale to get a seamless, crisp shadow across the vehicle.
But I wasn't in the mood to just make a quick change and walk away from the problem — especially since I'm usually the one breaking my head trying to squeeze out a few extra milliseconds of frame time so our devices don't start doubling as hand warmers while running the game.
So instead, I decided to dig a little deeper and try to understand what these shadow-related settings actually mean. I'm definitely not going to cover all of them here (mostly because I don't know all of them), but I'll walk through the ones I managed to study and experiment with.
Shadow Settings
r.Shadow.CSM.MaxCascades
Cascades are essentially distance ranges — the camera view is split into these ranges. Each cascade gets its own shadow map.
More cascades generally improve shadow quality at farther distances, but they also increase GPU cost.
r.Shadow.MaxResolution
Sets the maximum resolution allowed for dynamic shadow maps generated by lights. This directly affects how sharp the shadows appear.
Higher resolution = sharper shadows, but also higher GPU memory usage and rendering cost.
r.Shadow.MaxCSMResolution
Sets the maximum resolution specifically for Cascaded Shadow Maps generated by Directional Lights (for example, the Sun).
This is separate from r.Shadow.MaxResolution and applies only to cascaded shadows.
r.Shadow.RadiusThreshold
Controls when objects stop casting dynamic shadows based on their screen size. If an object appears smaller than this threshold on screen, it will not cast a shadow.
This helps reduce shadow rendering cost for tiny distant objects. Increasing this value can yield some performance gains, at the cost of small objects no longer casting shadows.
Example: If the value is 0.04, any object occupying less than 4% of the screen height will not cast a shadow.
r.Shadow.DistanceScale
Scales the maximum distance from the camera where dynamic shadows are rendered.
A lower value reduces the shadow rendering distance, while a higher value extends shadows further into the scene.
This directly impacts how far the shadowed region extends and proportionally affects GPU cost as well.
So yes — the quick "just increase the quality" fix wasn't really the best solution after all, was it?
Stop Looking at the Shadow — Maybe Check the Light?
Now, let's look at a few properties of the Directional Light in our level.
The Cascaded Shadow Maps section of the Directional Light has the following:
DynamicShadowDistanceStationaryLight
How far from the camera dynamic shadows from the directional light are rendered. Within this distance, shadows are calculated in real time using CSMs. Beyond this distance, shadows switch to baked/static shadows (or disappear if none exist).
DynamicShadowCascades
How many cascades are used for the directional light's CSM shadows. Instead of one large shadow map, the camera view is divided into multiple distance slices, and each cascade gets its own shadow map.
CascadeDistributionExponent
Controls how the cascades are distributed across the shadow distance. The range is from 1.0 to 4.0.
This is the interesting one — it took me a while to wrap my head around the concept:
Low Exponent (1.0): Cascades are spread more evenly across the distance.
High Exponent (4.0): More cascades are concentrated close to the camera, with lower detail farther away.
Wrapping Up
Consider the following Directional Light setup:
- Dynamic Shadow Distance = 20,000 units
- Num Cascades = 4
- Distribution Exponent = 3.0
The cascade boundaries are computed using this formula:
Boundary(i) = DynamicShadowDistance × (i / NumCascades) ^ DistributionExponent
With Exponent = 3.0, the cascade boundaries work out to:
| Cascade | Formula | Boundary (Unreal Units) | Boundary (Meters) |
|---|---|---|---|
| 1 | 20000 × (1/4)³ = 20000 × 0.0156 | 312.5 | 3.12 m |
| 2 | 20000 × (2/4)³ = 20000 × 0.125 | 2,500.0 | 25.00 m |
| 3 | 20000 × (3/4)³ = 20000 × 0.4219 | 8,437.5 | 84.38 m |
| 4 | 20000 × (4/4)³ = 20000 × 1.0 | 20,000.0 | 200.00 m |
And this would explain why at a distance of roughly 3 meters (312.5 Unreal units) from the camera, we saw a drop in shadow quality — that's exactly where the first cascade ends and the next one begins.
If we change the Distribution Exponent to 2.0, the cascades shift like this:
| Cascade | Formula | Boundary (Unreal Units) | Boundary (Meters) |
|---|---|---|---|
| 1 | 20000 × (1/4)² = 20000 × 0.0625 | 1,250.0 | 12.50 m |
| 2 | 20000 × (2/4)² = 20000 × 0.25 | 5,000.0 | 50.00 m |
| 3 | 20000 × (3/4)² = 20000 × 0.5625 | 11,250.0 | 112.50 m |
| 4 | 20000 × (4/4)² = 20000 × 1.0 | 20,000.0 | 200.00 m |
This means the quality drop is pushed out to 12.5 meters instead of 3.12 meters — roughly 4× farther from the camera.
Trade-off?
Of course there is a trade-off!
The max CSM resolution of 2048 was originally being used for a relatively small coverage distance of 3.12 meters. After changing the cascade distribution, the same resolution now covers a much larger range of 12.5 meters.
What this means in practice is that the shadow texel density (texels per world unit) decreases. In simpler terms, the same number of texels now has to cover a larger area of the world, which naturally makes the shadow appear softer.
Important Insight
This does not mean the entire scene suddenly becomes 4× blurrier.
In reality, only Cascade 1 changes dramatically because its coverage area increases significantly. Meanwhile, Cascades 2–4 actually get sharper when using a distribution exponent of 2, since more shadow resolution is now distributed across a wider near-field range, and the far cascades cover proportionally less area than before.
That's pretty much the rabbit hole I went down while trying to understand why a shadow decided to become blurry a few meters away from the camera.
The takeaway for me was simple: many of these Unreal settings don't exist in isolation. Changing one value often shifts the balance somewhere else — sometimes improving one cascade while quietly hurting another. Understanding that trade-off is far more useful than blindly pushing everything to higher values.
More importantly, the next time I see a shadow doing something strange, at least I'll know where to start looking.
And hopefully, future-me will thank present-me for writing this down.






Top comments (0)