The phenomenon where effects created using VFX Graph suddenly disappear unnaturally just by changing the camera angle slightly is a very typical rendering problem that beginners often run into. This phenomenon occurs due to the mechanism of "bounding box culling". In this article, we will explain the concept of culling in an easy-to-understand manner using a familiar example, and explain settings and script countermeasures to completely eliminate the bug.
1. Understanding with an analogy: The trap of ``invisible packaging boxes for shipping''
In order to intuitively understand this problem, let's consider ``Determination of culling of packages at a distribution center'' as an example.
Suppose you want to deliver a ``very long bellows toy (splattering particles)'' to a shipping company (GPU drawing engine). However, I applied for a ``very small palm-sized cardboard box (default Bounds)'' as a packaging size that is not visible when issuing a slip.
The sorter at the shipping company (Unity's culling judgment system) sees a ``conveyor belt'' called the camera's field of view. The sorter only relies on the "palm-sized bounds" written on the slip to check whether there is a package or not. The moment the conveyor moves and the border of the palm-sized box passes the scanner (off-screen) and leaves the frame of the conveyor, the sorter determines that ``this item has already left the sorting lane (is subject to culling)'' and stops the delivery process completely. However, in reality, the "body part of the bellows toy (the actual flying particles)" that protrudes from the box should still remain in the lane, but because the box itself has disappeared off-screen, the entire toy is not unloaded and disappears in an instant.
This is the true nature of the culling bounds mismatch bug in VFX Graph. In order to improve performance, Unity only monitors whether invisible virtual boxes (Bounds) are within the camera's field of view, and when the box leaves the camera frame, no matter how large particles are scattered inside, Unity tries to reduce the load by completely skipping the drawing process. Because the packaging size is too small, particles that should still be visible are discarded all at once.
2. Solution A: Manually set Bounds larger in the editor
The simplest and least performance-intensive solution is to manually redefine the size of the invisible packaging box (Bounds) on the VFX Graph asset side to match the range of the particles that will actually spread.
Setup steps:
- Double-click the desired VFX Graph asset to open the VFX editor window.
- Select the "Initialize" block that performs initial settings for effects or the "VFX System" block that represents settings for the entire system.
- Check the Bounds section of the Inspector window. In most cases, it is set to "Automatic (automatic calculation)" in the initial state, but automatic calculation in VFX Graph cannot completely predict changes in variants, gravity, and movement of noise parameters, so it tends to be fixed at a size that is too small.
- Switch this to Manual.
- A light blue box frame (this is the Bounds packaging box) will appear on the graph. Adjust the Box Center and Box Size values in the Inspector so that even when particles are thrown the furthest by wind or gravity, they always grow to a size that fits completely inside the box.
*If you make it too large, it will cause drawing calculations to run even when it is not visible to the camera, reducing performance, so it is best to set it to about 1.2 times the maximum scatter radius of the effect.
3. Solution B: Expand Bounds dynamically from a C# script
If you have a dynamic wind simulation or the behavior and scale of particles change drastically depending on the game situation, static Bounds settings may not be able to cover it. In that case, update the bounding box dynamically from a C# script.
using UnityEngine;
using UnityEngine.VFX;
[RequireComponent(typeof(VisualEffect))]
public class DynamicVfxBoundsFix : MonoBehaviour
{
private VisualEffect vfx;
public Vector3 customBoundsSize = new Vector3(10f, 10f, 10f);
public Vector3 customBoundsCenter = Vector3.zero;
void Start()
{
vfx = GetComponent<VisualEffect>();
ApplyDynamicBounds();
}
void ApplyDynamicBounds()
{
if (vfx == null) return;
// Setting the VFX bounding box directly from C#
// Center is the relative coordinates from the GameObject, Size is the height and width of the packaging box.
Bounds newBounds = new Bounds(transform.position + customBoundsCenter, customBoundsSize);
vfx.SetConceptBounds(newBounds);
}
// Display the setting range in the editor scene gizmo for easy viewing
void OnDrawGizmosSelected()
{
Gizmos.color = Color.cyan;
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.DrawWireCube(customBoundsCenter, customBoundsSize);
}
}
By attaching this script to a VFX object and running it, it will force the set bounding box to be wide enough, completely preventing bugs where the effect disappears halfway no matter how you rotate the camera.