This is a plugin to add Foliage Shadow Imposters to Nanite Foliage for increased performance when using World Position Offset (WPO) and Virtual Shadow Maps. We gain performance by disabling the expensive WPO shadow on the Nanite Foliage and replacing it with a cheap non-WPO shadow casting Foliage Shadow Imposter. The compromise here is that the foliage will animate in the wind, but the shadows cast on the ground will not change. This compromise leads to massive performance gains.
This plugin searches all InstancedFoliageActors for an OriginalFoliageMesh and adds a FoliageShadowImposterMesh with the same location, rotation, and scale. The OriginalFoliageMesh should have Nanite Foliage turned on and be set to not cast a shadow in the FoliageType in the Foliage editor. The OriginalFoliageMesh should have World Position Offset enabled. The FoliageShadowImposterMesh should be a low poly LOD for the purpose of casting a cheaper shadow. The FoliageShadowImposterMesh must have FoliageShadowImposterMesh disabled. The plugin will automatically enable the shadow for FoliageShadowImposterMesh, set HiddenInGame, and set cast shadow when hidden.
Interesting concept! I agree, crazy ideas are often the best :).
I couldn’t find the actual figures for speed increase in your video though - what were they? Also what hardware are you using there?
The speed increase depends heavily on the complexity of the trees and the number of trees casting shadows to the current viewport. The performance difference increases a lot if you run a 4k screen like I do. In the video I show the difference going from 29fps up to 65 fps when WPO shadows are off vs on. When adding the Foliage Shadow Imposters in that scene the fps stays close to 65 fps with the non-WPO shadows on. So the increase there would be a 124% increase. If you add more trees it goes up even more. I have tested scenes in 4k where without this plugin the fps was less than 1 and with the Foliage Shadow Imposters added by the plugin, the fps was around 50.
This is a great idea! We’re not on 5.1 yet, but in your testing is the shadow expense mostly caused by WPO on Nanite meshes in general or because of the VSM cache being invalidated due to their movement?
I’m wondering if things like fully static tree trunks (with WPO for bark heightmaps randomized or scaled via shader) or even just walls with brick and plaster variation created through WPO have performance issues, or if it’s just the VSM cache issue.
EDIT: Something else I’m curious about is what the performance is like if you use the Shadow Pass Switch node in the material for just the single main Nanite tree (as compared to the double-tree hidden shadow solution)? Have WPO piping into the Default input and a static ‘0’ value into to Shadow input. It would still be shadowing thin Nanite foliage instead of the imposter LOD, but it shouldn’t be moving and invalidating the VSM cache.
Either way, thanks for creating and documenting this!
Thanks! Yeah, the issue had nothing to do with rendering the Nanite tree, it was all about the VSM cache being invalidated. Someone in my Discord tried the Shadow Pass Switch node in the material and said it didn’t work (I didn’t verify). There also seems to be some console variables that might be able to be used to control WPO and VSM, so we are experimenting with those. We are also testing different tree detail levels, Nanite vs non-Nanite rendering to VSM, and various foliage density to get an idea of what kind of rendering budget we have available. This was just our first attempt at Nanite Foliage in UE5.1. We’re still working on better solutions for our game
Yeah I hope they fix the VSM cache invalidation due to WPO. I just did a test in our 5.0.x build and having anything at all in the WPO slot invalidates the VSM cache according to the debug visualizer.
The material nodes were behaving as expected with static shadows and animated WPO effects for the visual mesh, but even setting fully static or zeroed out WPO offsets invalidated the cache.
Everything you’re doing is something I was hoping to see back in UE4 as well. Back then without Nanite I was hoping for some option to mark a given LOD as the shadow caster maximum for a given asset since a lot of finer control edges and micro details in L0 and L1 weren’t useful for coarse shadow maps back then anyway.
A similar approach was used manually by Guerrilla way back on Killzone Shadowfall. One of their GDC presentations talked about doing it for level geometry, especially with levels that had exterior terrain alongside buildings with interiors. They really only needed a basic sun-facing silhouette shell mesh for the buildings rather than having the expensive cascaded shadowmap pass process all the interior geo detail.
We were looking for the same kind of solution back in UE4 as well where we could designate a certain LOD as a shadow caster. We were able to find a solution for our game in UE4 though. We set the expensive cascade shadow distance very short and then used the lower quality, but much faster distance field shadows for anything past a few meters in front of the camera. Using this method we were able to have foliage in the 20 to 30 million tris per scene range and still get 60+ fps in DLSS 4k with a GTX 2080. Now that we are moving to UE5, we are again trying to come up with a complete solution for our game and that is where we ran into this VSM issue.
A wonderful performance solution, seems essential for Nanite foliage. I’m wondering if it’s possible to animate the shadows for the closest assets to the camera with a distance based or LOD type of result.
Hello!
Great plugin! I need some help. I’m on UE5.3 I added the meshes to the data table and when I click on the double tree icon I can see both instances of the trees one is moving other is not. The shadows also I can see moving and static alltogether.
How to hide impostor meshes and how to hide shadows that are moving?