The description of the Stationary type of mobility states that the shadowing of a light source with it must be baked by Lightmass:
But as of 5.7.4 (the same issue was present in 5.7.3, by the way), this is not the case - Stationary light can only produce dynamic shadows which are never baked into Lightmass.
You can easily reproduce the problem yourself:
Create a new project.
In the project settings: set Static Baked Lighting = Enabled, Global Illumination Method = None (instead of Lumen), Reflection Method = None (instead of Lumen), Shadow Map Method = Shadow Maps (instead of Virtual Shadow Maps). Restart the editor to apply changes.
Create a new empty level and open it.
Add “Cube” from the “Quickly add to the project” menu. It will create a Static Mesh actor with its mobility set to Static. Increase its scale in X and Y directions for convenience.
Add another “Cube” and place it on top of the first static mesh. Set Dynamic Shadow = false in its properties (it can still cast static shadows).
Add a Directional Light. Its default mobility is Stationary.
Press Build->Build Lighting Only. As you can see, the lower mesh does not receive the shadow of the upper mesh even though it should.
This issue is honestly infuriating because there is a difference in how static and dynamic shadows are rendered. For example, proper soft shadows can only be static (dynamic shadows can be soft too, but only if you enable Virtual Shadow Maps or Lumen - both require TAA which is not acceptable for a lot of games). And I can’t think of any other way of having both static and dynamic lighting in a scene.
a stationary directional light does not bake shadows into lightmaps. that is likely by design. it does bake GI and volumetric lightmaps but the shadows can be handled in realtime. like… with megalights. this is a hybrid solution. if you want baked sun shadows you gotta make the light static. and you gotta manage all the lightmap scale and uv things.
For directional lights, stationary static shadows are hidden within the cascaded shadowmap distance, even if an object is not set to not cast dynamic shadows its static shadows will still be removed.
Nothing you can do about this other than disable cascaded shadowmaps by setting the Dynamic Shadow StationaryLight Distance to 0.
Well, this just goes against its description, source code naming (ULightComponentBase::HasStaticShadowing() returns true for Stationary), the descriptions of some of the Directional Light component fields (e.g. Use Area Shadows for Stationary Light’s tooltip is “Whether to use area shadows for stationary light precomputed shadowmaps”, implying that there should be precomputed shadowmaps) and common sense.
But if this is the expected behaviour, then how can I go around it? My problem is not that I don’t know how to manage the assets for static lighting, it is that I need shadows for both static and movable objects in my game, and simply placing two identical Directional Lights with two different mobility options causes their lighting to stack, resulting in too much brightness and other issues.
Dynamic Shadow StationaryLight Distance is already set to 0 by default. I am not sure what this variable does, because changing it to values like 500 does not seem to change anything in a scene. I thought the shadows should now disappear if I move the camera too far away, but that doesn’t happen.
DistanceField Shadow Distance also needs to be set to 0. Evidently Epic changed the default at some point.
Worth mentioning that setting the dynamic shadow distance to 0 (and DF shadow distance) will fix the static shadows not appearing, but now you will have no dynamic shadows.
You can use Inset Shadows instead of CSM, but unless you only have a few small actors that need shadows, it’s most likely going to be more performant to just use cascaded shadowmaps and accept the fact that they’ll replace static shadows within their shadow distance.
Thanks, I think I am beginning to understand how Stationary Directional Light works. Depending on the distance from a camera view to a point in a scene, one of the following behaviours is chosen:
Cascaded Shadow Maps -> Dynamic Shadow Distance to Distance Field Shadows -> DistanceField Shadow Distance: DFS (fully dynamic)
Distance Field Shadows -> DistanceField Shadow Distance to infinity: “static” shadows (partially dynamic).
Those “static” shadows are, indeed, precomputed, but they are stored in a separate layer of Lightmass, or something like that (I lack the technical knowledge here). So, those “static” shadows only become visible if the camera gets very far away (or if the other two thresholds are low).
This answers my original question, I guess. Although I still can’t use Stationary light for my game because its “static” shadows are computed in a manner similar to dynamic shadows - that is, they can’t be soft. And getting soft shadows to work is my ultimate goal. I guess I will create a new topic for that…
They can be soft, but you need to enable the setting you mentioned earlier: Area shadows for stationary lights
By default stationary lights store their shadow data as a 2D distance field, this allows them to have sharp shadows that aren’t blocky without massively increasing the lightmap resolution but it comes at the cost of not being able to generate area shadows.
AFAIK they’re stored in a separate surface lightmap texture in the separate RGBA channels, that’s why stationary lights are limited to 4 overlaps and will fall back to fully dynamic shadows if you overlap more than that.
Stationary lights are deceptively complex, and they’re a source of a lot of undocumented feature conflicts, especially for directional lights because they have so many different shadow options.