VFX Graph: How to kill particles that are out of sight?

In VFX graph, is there a way to kill particles whose individual bounds are out of the camera frustum?

For killing particles, I know about two methods:

However they are problematic in my use case: I spawn particles around the camera for creating a fog, their size is very large and I use a gradient color over lifetime to make them fade in and out. This causes the two following issues with the killing methods I mentionned:

  • Kill AABox: the size of the particles is so large that they may still be visible when entering the AABox, creating a jittering effect (some parts of the fog disappear instantly)
  • Setting their lifetime: if for instance I set a short lifetime to a particle that falls in a zone behind the camera, this will instantly change its color due to the gradient over lifetime fading effect. For the same reason as the AABox this creates also an instant change in the fog.

In other words, my issue here is that I am unable to create a zone (AABox, sphere, etc.) that would kill the particles “safely”, meaning only if they are out of sight. Is there a way to take into account the individual bounds of the particles to do this?

1 Like

Morning, what you are looking for is pretty common and can have many names. In VFX Graph, it is call “Frustum culling” and it’s a setting that you can find in your Output Context

This is usually a viable option to optimize large VFX that can have many particles outside the Camera Frustum. Note that it can cause some issue regarding shadow casting. Now regarding performance gains, this always depends on numerous factors, so don’t hesitate to profile on your targeted platforms to make the final decision.

Have a lovely day

2 Likes

Thanks, I did not know about it, this looks promising!

If I get it right, this culls the particles, hence reduces the number of quads to render, which is great, but it does not kill them in the sense of a kill AABox, right? Is there a way to use something like this to kill them so that the spawn system can spawn new particles?

The reason I want to do this is because I only spawn fog around the camera and if the player decides to move very fast, then my system would need to wait for the old particles to die before it could spawn new particles around the new location reached by the player.

You could check if the Particles are inside the Camera Frustum in the update context and modulate either the lifetime or Alive attributes.

Moving the Camera fast, erase the particles pretty quickly

Now if your player moves very fast, or just turns the camera fast enough, all particles will be erased. And you’ll need to have a high spawn rate to refill the screen, which could be pretty tricky to balance.
And if the player doesn’t move the camera, the particles will have a tendency to concentrate in this frustum.

Now, you might still be interested in a solution that is using a Camera Frustum mask. Thanks to @PaulDemeulenaere and @maximeg_unity I was able to get you one:

float3 viewPos =  WorldtoViewportPos(MainCam, attributes.position);
float2 maskXY = saturate(abs( Remap(viewPos.xy, 0, 1, -1, 1));
maskXY = step( 1, maskXY );
float maskZ = Remap(viewPos.z, MainCam.NearPlane, MainCam.FarPlane, 0, 1);
maskZ = step( frac(maskZ, 0.000001 );
float camFrustrumMask = maskZ * maskXY.x *  maskXY.y;

The mask here is either 0-1, but you could modify the computation to smoothen the mask. You could use this to modulate the age of your “out of frustum particles”.

Here are some explanations and visualizations to help you better understand the Frustum mask is computed and how to modify it to your need.

Disclaimer: There might be a better way to compute this.

The same is done on the Y Axis.

For the Z component:

Multiplying the different component together then gives us the final 0/1 mask
Unity_15IBiWaAwI

Now, that how to get the camera frustum mask has been explained, you might also consider “Warping” your particle’s position inside a volume. This is a solution that is commonly for VFX that needs to be happening near the Camera, like Snow, rain drops or mist. The Tile/Warp position block lets you tile your particles inside a volume.

In this set up, my VFX is attached to the camera and the particles spawn inside a Sphere Volume.
The Tile/warp block is set inside the Update Context.


Unity_M9wTsnls5J
This solution allows you to Re-use particles by making them tile inside the defined volume. This is easier to manage and usually gives good results. Now you can also mix this approach with the Camera Frustum mask approach.

If you would like to learn more about View-wrapping particles, you can take a look at this old but good tutorial about it. While it’s inside another pretty cool VFX editor (PopcornFX), you should be able to grasp the concept and simply use the Warp/Tile Block inside VFX Graph.

I hope that this will help you achieve what you’re looking for, and don’t hesitate to share the results with us. :hugs:

7 Likes

Many thanks for this very complete answer, I learned a lot! :smiling_face:

I tried both approaches and of course found pros and cons for my use case. Here are some of the thoughts that helped me decide what solution to chose:

Camera Frustum Mask:

  • Pros:
    • The camera is constantly surrounded by particles. However, as said earlier, this requires to have a high spawn rate and since - in my case - I’d like a smooth spawning, a little delay is created when the camera suddenly changes direction.
  • Cons:
    • Particles are not persistent (if the camera sees a group of particles that are recognizable and quickly does a 360, then this group of particles is no longer here).
    • Flickering effect: particles going out of the frustum disappear but this is decided based on their position. Part of the particle might still be visible which creates non-smooth disappearances. This is the issue I tried to alleviate (see below) by adding a margin.

Tile/Warp Positions:

  • Pros:
    • The camera is constantly surrounded by particles.
    • Particles are persistent: if the camera sees a group of particles that are recognizable and quickly does a 360, then this group of particles are still here.
  • Cons:
    • Flickering effect: when a particle appears or disappears because of the tiling, this is done instantly, without smoothing effect

Overall my main concern was to avoid the flickering effect which I could see in my two attempts to implement your ideas. There might be other ways to do it but I ended up using the camera frustum mask with a tolerance, to take into acount the size of the particles so that they disappear when they are completely off-screen.

Here is an implementation that kills particles that are twice as far from the horizontal and vertical limits of the camera frustum (this is to be chosen depending on the size of the particles of course, I’m sure it could be possible to take this size as a parameter but I did not have time to research it yet).

Notice that:

  • I directly compared the xyz values to their limit. To me this is less sophisticated than the maths you just showed but I was wondering about the difference in performance. I could not measure any significant difference though. If somebody has any thoughts about this I’d be interested!
  • I did not compared the depth to the near and far planes just because it is no use in my case (one cannot really move backwards).

I’m glad that you were able to choose a solution. Notice that the Cons of both solutions can be mitigated.

For the Frustum Mask:

  • You could create a “Timer” custom float attribute. This timer will start at 0 and will increment as long as the Particles are out of the Frustum Mask. Once inside the Frustum mask, the timer could be either reset or decrement. You could use this timer to fade their Alpha/Color and only kill them after some threshold.

For the Frustum Mask:

  • You can fade the particles at the edge of the Volume. This should allow smoothening the transition.
    In the Tutorial that I shared a part is dedicated to this.

That is all. :slight_smile:

1 Like