Nanite Niagara - How I do it (its supported since 5.0!) using Blueprints to Export Particle Positions from the GPU, and C++ to convert that data to transform arrays by static mesh.

Nanite Niagara Static Mesh Rendering brings Niagara Particles into Lumen and Nanite Draw calls… It completes the Nanite Ecosystem. With Unreal 5.5 also adding Skeletal Mesh support for Nanite, everything can be nanite.

In this 37 minute video, I walk through my working implementation of Exporting 100,000 Niagara GPU Simulated Particles, each of a random static mesh, which are exported to blueprints and converted to a Nanite ISM(Instanced Static Mesh) component each frame. Using a Batch update method of Batch Adds.

Summary:

I am exporting 100k GPU Niagara Particle Particle data each frame/tick, converting that exported array into arrays by static mesh index, of transforms in c++. I then Apply those transforms each frame via batch adds to Nanite enabled Instance Static Mesh Components. This enables Nanite Niagara GPU Simulated Static Mesh Rendering. It also supports Nanite Masked Material (some VFX/Smoke/Cutout type) or foliage as well.

I modified the included Particle Export Node to Export particle rotation instead of velocity, and packed rotation-W(orld) and my particle Mesh Index into the exported float value. That value/pack of the mesh data allows me the sort each particle via one pass into arrays.

Twitter : GameDevMicah ( twitter.com/GameDevMicah )
-LinkedIn : (Micah Berninghausen)
-Reddit : u/DiePepsi

Additional Detail:

Unreal Engine 5.0 added a new Rendering Technique called “Nanite.” Nanite generates a single, global static mesh, every tick/frame, which is a 1:1ish merged representation of any ‘nanite enabled’ static mesh in view. This greatly reduces draw calls, and performs best the more ‘nanite’ and less ‘non-nanite’ is present in the view.

Unreal Engine 4.27 added the ability to export Niagara GPU Simulation data using an experimental Niagara “Export” node, for each GPU simulated particles (CPU simulated are also supported, but CPU simulation is much slower than GPU simulation.)

Epic Games Content Sample Project(s) for Unreal Engine 4.27+ or newer UE5.0, 5.1, 5.2, 5.3, 5.4, 5.5+ you will find the “Game/Maps/Niagara_Advanced_Particles” Level, within that level “Export GPU Niagara Particles to Blueprints” will be Niagara Emitter Example 4.2.

Setup for export of particle position each frame, but still using a static mesh renderer.

This video walks through how I use Niagara GPU exported particle data per frame, as Nanite Instance Static Mesh Batch Updates each frame/tick. To do so we must convert the Exported data per particle, which scales linearly to about 20ms for 10k particles converted via Blueprints, down to .3ms in c++. 100k particles then done in c++, reaches 60fps 4k nanite rendered instance static meshes.

This could be further refined, by offloading the conversion to another thread from the gamethread and may be my next action. The main workload is not the export of the data from Niagara, but the conversion of that data to transforms and sorting it into transform arrays.

The Niagara particle export bandwidth seems to be cheap, a small impact, and scales linearly as you export more particles.

You CAN Import position arrays per frame TO GPU Niagara Particles. You can also export on particle death to blueprint (this video is exporting each frame, not JUST the death frame.)

So Import and Export of Objects to GPU Simulate in Niagara is now fully supported, and using this runtime particle nanite rendering, we can do fully-nanite VFX… besides transparency? (skeletal meshes are supported in UE5.5 source build.)

Important Settings or Details of the setup:

Niagara*** Update: I forgot to include how to access Niagara Particle Data Structure Types within C++ requires including Niagara in the Build.cs file for your project, in addition to including niagara in your header file/cpp includes. Below is the snippit to include for .Build.cs

  • GPU Simulation is the best choice, CPU is supported.

  • Collisions are done with Global Mesh Distance Fields,

useful commands:

r.AOGlobalDistanceField.MinMeshSDFRadiusInVoxels 5 (default 0.5)

This means meshes smaller than this many voxels will be excluded from global field generation. A setting of 5.0 is small enough meshes included yet fast enough generation for me, adjust to your impact and liking.

you can also set this as a radius : r.AOGlobalDistanceField.MinMeshSDFRadius

MeshDistanceFields:

Each Static Mesh Asset in your project, needs to be enabled to “generate mesh distance fields” and your project settings needs to “enabled mesh distance fields generation.” Both the static mesh build settings, and the project settings, have mesh distance fields “density” settings that can be reduced, the speed up field generation.

There is: “Mesh Distance Fields” and “Global Mesh Distance Fields.”

Global Mesh Distance Fields is Cheaper SHADOW COPY, and goes further distance from the camera than the Mesh Distance Fields. Both editor “view” modes exist, be sure you are looking at “Global distance fields” when debugging what particles are interacting with. GLOBAL DISTANCE FIELDS

If using hardware ray tracing, that may cause an issue for global mesh distance fields. I don’t know if they are generated. This setting should enable RTX Distance Field Generation but I have NOT tested it : r.DistanceFields.SupportEvenIfHardwareRayTracingSupported 1

Below are the C++ Header and Body files for the BlueprintFunctionLibrary call made to convert Niagara Exported Particle Data Arrays into Transform Arrays based on Mesh Index. This requires a modification of the included 'Niagara Export Particle Data to Blueprint" node to include Mesh Rotation instead of velocity, and pack mesh index and q-w value into the scale particle data. This detail is (below in snips) reviewed in the video on youtube, and also done via blueprints in that video and below.

<This Tutorial (with code snips) is in review with Epic right now, but has my Niagara Export Node Graph detail, along with C++ convert .h and .cpp files, and my build.cs that includes niagara as a dependency. >
https://dev.epicgames.com/community/learning/tutorials/w6wG/unreal-engine-nanite-niagara-gpu-particle-renderer-and-rendering-of-100-000-particles-using-a-single-niagara-system-with-33-different-meshs-blueprint-and-c-code-performance-review

Note, you need to increase the particle export limit via a console variable, you can do this via a blueprint instead of an ini like so. I will try to update and include this detail:
GPUMaxReadbackCount 110000
https://x.com/GameDevMicah/status/1782445095620640982