One of the biggest initial offenders was the Landscape. Thus we added simple Nanite Landscape support primarily to improve Virtual Shadow Map performance, which effectively eliminated it as a problem.In Fortnite Battle Royale Chapter 4, we were able to convert the vast majority of geometry to Nanite, greatly improving shadow performance. There are still some non-Nanite shadow casting objects (in particular, the player models), but they are few and small enough that they generally do not cause performance problems.

To support various volumetric effects, Virtual Shadow Maps also maintain a very low-resolution map that covers the whole frustum, called “coarse pages.” The amazing LOD of Nanite makes rendering coarse pages efficient. Non-Nanite geometry, however, has caused large and variable overhead in previous releases, mainly due to needless vertex processing. To mitigate this issue, we added small-object filtering to the coarse pages, which has enabled us to ship Fortnite with this feature enabled. This is also enabled by default in Unreal Engine 5.1.

Foliage

Improved foliage was one of the major visual goals for Fortnite Battle Royale Chapter 4, but it was also one of the main performance concerns.

Optimizing uncached shadow rendering performance boils down to many of the same things as for the main view, and is covered in detail in the Nanite blog post. Most impactful for shadow performance was removing alpha test and making the World Position Offset function as cheap as possible (largely pre-computed in Fortnite). The cost of these features is already higher with these very detailed Nanite meshes, but we may also have to rasterize mesh clusters a few times when they span multiple shadow clipmap levels.

We implemented the World Position Offset distance culling for the shadow pass (relative to the main camera position, not the light) in part for consistency with the main view, but also for a modest performance improvement.

Fortnite already used a “shadow proxy” system before Chapter 4, and early on, we were unsure if we would be able to hit our shadow performance budget with the full 300k+ polygon tree meshes in the shadow pass, since shadows are typically allocated a smaller frame budget than the primary view despite rendering similar (or in some cases more) pixel counts. Shadow proxies are implemented by having two static mesh components on a tree actor: one for the main view with “cast shadow” disabled and a hidden one for the shadow proxy with “cast hidden shadow” enabled.

In the case of Chapter 4, the shadow proxy meshes are still fairly complicated: 60k+ triangles is typical.

Source: Unreal Engine Blog