Point cloud visualization with particle system

Hello!

I have actors in Unreal that generate point clouds from their environment. So far, I’ve only inspected those point clouds in tools external to Unreal, but I’d like to leverage the Niagara system to visualize them within the world for context. Basically,

  • Inputs: A list (TArray<FVector>) of a couple hundred/thousand 3d points in either local or world coordinates.
  • Outputs: Those points drawn with some small marker into the world. Markers should have perspective effects (farther away -> smaller).
  1. Is Niagara appropriate for this kind of task and, if so,
  2. How do I configure a particle system to draw a list of specified points?

Some other details/nice-to-haves:

  • Using something like a dot or fuzzy ball for a marker is fine to get started with
  • Ultimately I’d like to be able to change properties (color) of the makers based on some criteria (height, distance from the emitter, etc.)
  • Since the particles are “annotations”, I’d like to exclude them from cameras/SceneCaptureComponents that aren’t the main game window
  • I understand Niagara has CPU and GPU emitters. Right now I have these points available on the CPU, but ultimately I’d like to source them from a GPU shader. “GPU emitter for GPU data for speed” seems like a pretty intuitive step; is that the correct mental model for how these emitters work?

Thanks!

[Attachment Removed]

Hi Connor,

Are you aware of the Lidar Point Cloud Plugin that is already inside the engine? It sounds like that would cover most of what you want to do, and any customization of color and sprite size can be handled through the material. Making your own Niagara system would allow for quite a bit of customization, but I figured we can start with that.

[Attachment Removed]

Hi! Niagara can certainly be used for this sort of thing. To get individual particles spawning at specified locations, a direct way would be to use Blueprints or code to pass the array of positions into a Niagara System via user parameters, then use a Scratch Pad module to assign a position to each particle.

The longer version of the above is:

  • create a Niagara System, either based on the Minimal template (you’ll need to add a Spawn Burst Instantaneous module in the Emitter State section) or some other Burst system of your choice (just remove or disable the modules you may not want, such as Gravity or Drag).
    • create an Integer user parameter (used to only spawn the same number of items in your array)
      • apply the Integer user parameter to the Spawn Count (Spawn Burst module)
    • create a Position Array user parameter (that’s what you’ll pipe the BP array data into)
      • create a Scratch Pad (aka local-to-that-system) module in the Particle Spawn section, after the Initialize Particle module
        • In Map Get, add two pins: INPUT [name of position array user parameter] & ENGINE ExecIndex
        • In Map Set, add one pin: PARTICLES Position
        • Create a Get node; connect the position array user parameter to the Get’s Array interface pin, and connect ExecIndex to the Get’s Index pin; pipe the output to the PARTICLES Position in Map Set.
          • This will set the world position of each particle.
        • (if you want to test this before using BP values, you can just set some defaults in the Niagara System’s user parameters)
      • tinker with visuals as desired.
  • in a BP or code, create a position/vector array, and set the Niagara array user parameter to its value; also set the Niagara system’s integer user parameter based on the Array Length in the BP.

Really large arrays can sometimes impact performance, so if this is for a realtime product, do keep that in mind. As mentioned, you can set this to be a GPU emitter. GPU emitters are most relevant when spawning large numbers of particles (which sounds like will be the case here), and when the GPU isn’t the bottleneck for your project (GPU resources might be limited on certain hardware or impacted by other content/systems in the project). In general, we recommend profiling performance early and often on target hardware. However, if these are only ever used as in-editor markers, as you mentioned, then performance may not be much of a concern for this system.

You can hide this effect from your Scene Captures by adding relevant asset(s) to the Hidden Actors section of the Scene Capture asset’s Details Panel in your level. You can also mark those assets as editor-only to prevent them from being included in gameplay and runtime camera views.

I hope this helps get things moving in the right direction! Please let me know if you have other questions about this.

[Attachment Removed]

So, Scratch Pad graphs create Inputs that you can see in your module stack, rather than linking to the user parameters directly. Here’s a scratch pad with a Position Array INPUT:

[Image Removed]That INPUT Position Array above creates a ‘slot’ of the proper type in the system view, where you can drop in your USER parameter:

[Image Removed]

As for your Blueprint, your initial setup is correct: the Niagara system has been added as a component, with the proper system assigned in its Details panel.

Next, you’ll need to decide on what event you want to trigger the data updates. For example, if you created nodes chained to the Begin Play event, the data would be updated when you started playing in the editor. Since this is something you’ll probably want reflecting updates to the data in editor-time (outside of gameplay), I’d suggest creating a custom event. Name it something descriptive, then chain your logic from there, like so:

[Image Removed]The above example corresponds with the last bullet point of my previous reply.

Start by getting a reference to your Niagara System: the easiest way is to drag and drop the Niagara Component into the graph. Drag a wire off of that to get Blueprint node options that are contextual to a Niagara System, such as the Niagara Set Position Array, Niagara Set Variable Float, etc. Those first two are the only ones that are directly necessary for what you’re working on. These nodes are where you connect the values in the BP (as defined by BP parameters) to the user parameters in the Niagara System; keep in mind that the text typed in the node must match the corresponding Niagara user parameter name exactly. The Linear Color, Activate, and Print String nodes are there just for example/testing purposes.

The Position Array yellow capsule on the left is a variable. That’s the value that contains the various points you want shown. The LENGTH node counts the number of entries.

Once that’s all set up, you’ll need to address how and when you want this to update the Niagara System. As mentioned earlier, using Begin Play fires when you start playing the level (PIE), but you might want it to be updateable in the editor outside of play. With that in mind, I’d suggest setting up an EUW; I hadn’t covered this in my prior reply.

An overview of the steps to hook this BP up to an EUW:

  1. Add the Blueprint to the relevant level.
  2. Create an Editor Utility Widget asset in the Content Browser
  3. In the Design section (highlighted icon in the top right), create a Button, and drop some Text inside. Make it look however you’d like.
  4. Click on the button in the graph, and in its Details panel, click on the [+] On Clicked option, which will open the Graph view.
  5. Connected to the On Clicked event: Get All Actors of Class (your BP), for each, Cast to BP type, call the Custom Event in your BP.
  6. Save, compile.
  7. In the Content Browser, right-click on the EUW asset and select Run. This should open up a window with your button in it.
  8. Click on the button -- the Niagara System should change to reflect the data in your BP, rather than the defaults you have set in Niagara.

[Image Removed] [Image Removed] [Image Removed]

I hope that helps clarify things!

[Attachment Removed]

Happy to help! I’ll go ahead and close this case. Feel free to open up another if you need additional support on this or other topics!

[Attachment Removed]

Hi Stephen -

I’ve seen the Lidar plugin but ultimately I think we’re going to want fewer dependencies and more customization. I expect this will quickly expand from points into lines and other “object highlights”, so I’d at least like to probe the limits/mental model of doing this sort of thing with Niagara.

I’ve spent a little time looking around inside of Niagara Systems and reverse engineering some of the examples (like GridLocation), but I don’t fully understand how to put all of the pieces together. It seems like I want to Spawn Burst Instantaneous with a Sprite Renderer (the default material is enough to start with) to create the things I’ll use for each point, but it’s not clear to me how to then set each sprite’s (world) position based on data from elsewhere in the engine (a TArray<Vector> from another calculation).

It would be very helpful if you can walk me through this sort of basic workflow in Niagara since there appears to be quite a bit more abstraction than simply

for(int i = 0; i < NUM_PARTICLES; ++i) {
    particles[i] = new_particle();
    particles[i].Translation = world_coordinates[i];
}

What parts of the Niagara emitter handle that kind of work (conceptually)?

Thanks,

Connor

[Attachment Removed]

Hi Camille -

Thanks for this writeup. I’m trying to follow along here, but I’ve hit a couple stumbling blocks.

I’ve created a Niagara System and added the modules + parameters,

[Image Removed]But the Map Get inside of the Scratch Pad module does not seem to see any user parameters,

[Image Removed]I can add an INPUT: Vector Array value, but I cannot seem to get it to use the USER namespace at all:

[Image Removed]so I’m not sure how to pass the position array from an actor blueprint through the Niagara system to the Scratch Pad. Can you give me a little more detail on how USER parameters get passed around/made available to Scratch Pads?

I’m also a little confused about how to work with this in an actor blueprint. If I have an actor with a Niagara Particle System Component (that uses my asset), how do I set the User Parameters from the Actor’s Event Graph?

[Image Removed]

Thanks!

[Attachment Removed]

This is phenomenally helpful, thank you! I will certainly have more questions as I get into the thick of things and start building intuition for how Niagara works, but this gives me a very solid platform to start from.

[Attachment Removed]

1 Like