Grown, Not Placed #18 min read
SpeedTree to Nanite foliage: the whole chain, with the numbers
There is almost no free, current, written resource for getting a SpeedTree asset into UE5 as Nanite foliage that actually performs. Here is the entire chain — export settings, the masked-to-opaque decision, wind, and what each step costs.
Written againstUnreal Engine 5.7SpeedTree for Games 9

If you search for how to get SpeedTree vegetation into Unreal 5 as Nanite foliage, you will find a handful of paid courses, two or three YouTube videos, and a pirated copy of one of the courses. What you will not find is a free, current, written account of the whole chain with numbers attached.
That is a strange gap, because this is not a niche problem — it is the core technical question for anyone doing natural environments, and the answer changed substantially with Nanite foliage support. So this is that account. It is written against UE 5.7 and SpeedTree for Games 9, and every number in it was measured on the same forest scene.
Test scene
- Subject
- Central European deciduous forest, mixed beech and spruce
- Area
- 400 × 400 m
- Foliage instances
- ~412 000
- Unique tree assets
- 6 species, 3 age variants each
- Hardware
- RTX 3070, 1080p, Epic scalability
- Measurement
- `stat unit`, `stat gpu`, averaged over 300 frames on a fixed camera path
The decision that dominates everything else
Before any export settings: masked or opaque?
Traditional game foliage uses alpha-masked cards — a quad with a leaf texture and an opacity mask. It is cheap in triangles and it has been the standard for twenty years. Nanite changes the arithmetic, because Nanite does not love masked materials. Masked geometry forces a separate pass, defeats some of Nanite’s rasterisation advantages, and interacts badly with Lumen, where every masked surface is additional work for the ray tracer.
The alternative is modelled leaves: actual geometry per leaf cluster, opaque material, no opacity mask at all. Twenty years ago that was absurd. With Nanite it is often faster, because Nanite’s whole premise is that triangle count stops being the thing you budget.
Here is what that swap actually did on the test scene.
| Metric | Masked cards | Opaque geometry |
|---|---|---|
| Frame time | 24.1 ms | 16.8 ms |
| Triangles rendered | 18.4 M | 61.2 M |
| Base pass GPU | 6.2 ms | 3.1 ms |
| Lumen scene lighting | 5.9 ms | 3.4 ms |
| Virtual shadow maps | 4.8 ms | 2.9 ms |
| Disk size, all species | 340 MB | 710 MB |
Triangle count went up 3.3×. Frame time went **down** 30%. This is the whole argument for opaque Nanite foliage in one table — you trade disk and memory, which are cheap, for GPU passes, which are not. Measured with stat gpu.
The cost is real: disk size roughly doubles, and the SpeedTree work is more involved because you are modelling leaf clusters rather than mapping cards. But frame time is what ships a scene, and the shadow and Lumen savings compound with every additional light.
The export chain, step by step
1. In SpeedTree
| Setting | Value | Why |
|---|---|---|
| Leaf geometry | Modelled clusters, not single cards | The point of the whole exercise |
| Mesh LODs | Generate 1 low LOD only | Nanite handles the rest. You need one cheap LOD as a fallback and for the billboard transition |
| Wind | Enabled, exported to vertex colours | See the wind section below |
| Vertex colours | Do not overwrite | UE reads wind data from these channels — this is the single most common way to break wind on import |
| Scale | Centimetres | Match Unreal or you will rescale 18 assets by hand |
| Pivot | Base of trunk, centred | So foliage scatter and physics behave |
2. On import into Unreal
Enable Nanite on the trunk and branch meshes immediately. For the leaf clusters,
enable Nanite and set the material to Opaque — if it is still Masked you
have not actually made the swap and you will get the worst of both approaches.
Then the setting almost everyone misses:
3. Wind that does not look like jelly
Wind is where good foliage becomes convincing and where most foliage becomes faintly ridiculous. The problem is almost always that everything moves together — one global sine wave, and the forest breathes in unison like a single organism.
The fix is per-instance phase offset. Three layers, each at a different frequency:

Nodes in this graph · Unreal Engine 5.7
PerInstanceRandom— a stable 0–1 value per foliage instance — this is what desynchronises the forestMultiply— scale that random into a phase offset in radiansTime— scaled three times, at roughly 0.4, 1.6 and 5.0 — trunk sway, branch bend, leaf flutterSine— one per frequency layer, each with the per-instance phase addedAdd— sum the three layers, weighted heavily toward the slowestVertexColor— the SpeedTree wind weights — R for trunk, G for branch, B for leaf — masking each layer to the right part of the treeWorldPositionOffset— the final displacement
Weight the layers heavily toward the slowest. My defaults are roughly
0.6 / 0.3 / 0.1 for trunk, branch and leaf. Trees are mostly still; it is the
occasional slow lean that reads as wind, not constant fluttering.
Where the frames actually go
The single most useful thing I can give you is not a settings list, it is the order in which to look for problems. From the same test scene, ranked by how much a bad decision costs you:
| Cost | Usually caused by | First thing to check |
|---|---|---|
| Virtual shadow maps | Every foliage instance casting a full dynamic shadow | Disable shadow casting on small ground foliage. It is almost never visible and it is expensive |
| Lumen scene lighting | Masked materials, and too much foliage in the Lumen scene | The masked-to-opaque swap; then r.Lumen.Scene.SurfaceCache resolution |
| Overdraw | Layered translucent or masked cards | Shader Complexity view mode. Bright red canopies are the culprit |
| Base pass | Genuinely too many unique materials | Consolidate to one master material with instances |
| Triangle count | Rarely the actual problem with Nanite | Check this last, not first |
That ordering is the opposite of most people’s instinct. Triangle count is the thing artists worry about and, with Nanite, the thing that matters least. Shadow casting on grass is the thing nobody thinks about and it can cost several milliseconds.
| Metric | Defaults | Tuned |
|---|---|---|
| Frame time | 16.8 ms | 11.9 ms |
| Virtual shadow maps | 2.9 ms | 1.1 ms |
| Ground foliage casting shadows | yes | no |
| Foliage instances | 412 000 | 412 000 |
Turning off shadow casting on grass and low ground cover, and capping the foliage cull distance for the smallest species. Zero visible change from a standing camera. stat gpu again.
Five milliseconds for two checkboxes.
What I still have not solved
Two honest gaps, because a guide that claims to have everything solved is usually lying:
- Seasonal variation from one asset. I have a working mask-and-LUT approach that handles green to autumn convincingly. Winter — bare branches with snow load — genuinely needs different geometry, and I have not found a way around that which does not look wrong. That is a later part in this series, when I have something worth publishing rather than a workaround.
- Billboard transition at extreme distance. The pop is reduced but not gone. It is currently the weakest part of my forest and I would rather say so than pretend the cull distance solves it.
Next
Part two takes the masked-to-opaque decision apart properly: exactly when the swap loses, how to measure it on your own scene rather than trusting my forest, and what happens to your memory budget at the scale where it starts to hurt.
A new part when there is something to say
A new part of a series every two or three weeks, when there is something real to say. Never weekly — the two weekly newsletters in this niche both burned out.
Setup: put your beehiiv or Kit form endpoint insrc/consts.ts as NEWSLETTER_ACTION and this becomes a real form. beehiiv is free to 2,500 subscribers with a custom domain included.