# 5. VFX Graph Comparison and Performance Part of [[Particle Systems and Layered Effects]]. Requires Unity 6.3 LTS+ and URP. Previous: [[4.Controlling Particles from Script]]. Next: [[6.Particle Pitfalls]]. ## 5.1 Particle System vs VFX Graph | | Particle System (Shuriken) | VFX Graph | |---|---|---| | Runs on | CPU | GPU (compute shaders) | | Practical particle counts | Hundreds to low thousands | Hundreds of thousands to millions | | Physics collisions | Real colliders (World collision), `OnParticleCollision` callbacks | Primitives, signed distance fields, **depth buffer** collision. No callbacks into C# physics | | Reading particles from C# | `GetParticles` / `SetParticles` | Not practical (data stays on the GPU) | | Spawning other particles | Sub emitters | **GPU Events** (Trigger Event On Die / On Collide / Rate / Always) feeding another system in the same graph | | Authoring | Inspector modules | Node graph, custom attributes, Shader Graph output | | Platforms | Everything, including Quest and WebGL | Needs compute shader support. Quest supports compute but VFX Graph on Quest is costly and less tested *(unverified for 6.3)* | | Lights per particle | Lights module | No | **Stay with the Particle System when** you need physics collisions with C# callbacks, gameplay that reads particles, small counts, or you target Quest/mobile. **Switch to VFX Graph when** you need very large counts (rain over a whole city, swarms, magic with 100k sparks), effects that sample meshes, textures, or signed distance fields, or GPU-side chaining. In VFX Graph the rain splash from part 3 becomes: rain system with a **Collide with Depth Buffer** block → **Trigger Event On Collide** → a GPU Event context → a splash system. It's entirely on the GPU, so it can't call `OnParticleCollision`; to tell gameplay about hits, use **Output Event** handlers on the CPU side (which only fire for events you send yourself, not per-particle collisions). Install VFX Graph through **Window > Package Manager > Visual Effect Graph**, then **Create > Visual Effects > Visual Effect Graph**. ## 5.2 Overdraw: the main cost of particles Transparent particles don't write depth, so every overlapping layer gets shaded again. A screen full of large smoke puffs can shade every pixel 20+ times. That's usually your bottleneck, not particle count. - Use the **Scene view draw mode: Overdraw** (URP: **Rendering Debugger > Rendering > Overdraw**, via Window > Analysis > Rendering Debugger) to see hot spots. - **Fewer, smaller, more opaque** particles beat many large faint ones. Smoke with 8 dense puffs looks as good as 40 faint ones. - **Max Particle Size** in the Renderer caps on-screen size (fraction of the screen). Set `0.3–0.5` for smoke. - **Camera Fading** on the material, so particles right in front of the camera fade out. - Trim the transparent area of your textures. For large textures with lots of empty space, Unity's Sprite-mesh-style trimming isn't available on particles, but you can use **Mesh** render mode with a tight custom mesh (an octagon instead of a quad). ## 5.3 Particle count and simulation cost - Set **Max Particles** deliberately in every system. - **Noise**: Quality 1D/2D, one octave. 3D noise with multiple octaves is expensive. - **Collision**: prefer **Planes**, then World **Low/Medium** with a small **Collides With** mask. High quality is a raycast per particle per frame. - **Lights module**: a handful of lights at most. - **Trails**: small Ratio, short lifetime. - Use the **Profiler** (CPU > `ParticleSystem.Update` and `ParticleSystem.Draw`) to measure. Shuriken multi-threads simulation, so the cost often shows up on worker threads. ## 5.4 Culling - A system can only be **procedurally culled** (simulation paused off-screen and fast-forwarded on return) when it uses no modules that make its motion unpredictable. If a system can't be culled, the Inspector shows a small warning icon at the top of the system listing the reason (for example World collision, Noise, or Local→World changes). - **Culling Mode** (Main module) for non-predictable systems: **Automatic** (pause if looping, keep simulating if not), **Pause and Catch-up**, **Pause**, or **Always Simulate**. - Bounds: make sure the renderer bounds cover the full effect (Show Bounds in the overlay), or the effect will cull while part of it is still visible. - Beyond that, disable distant effect GameObjects yourself with a distance check or `CullingGroup` API, or swap to a cheaper LOD effect. ## 5.5 Quest and mobile notes See [[Meta Quest 2]] for the platform setup. For particles: - VR renders twice (once per eye, or Single Pass Instanced). Particle shaders from URP support Single Pass Instanced; custom Shader Graphs do too when made in URP. - **Overdraw is the killer** on the Quest 2's tile-based GPU. Budget a handful of overlapping layers near the player. Large smoke right in front of the face is the worst case, so use Camera Fading and Max Particle Size. - Prefer **Particles/Unlit** and **Simple Lit** over Lit. Skip soft particles if you don't need them (they need the depth texture, which costs a resolve on tile GPUs). - Skip **Opaque Texture / Scene Color** distortion entirely; it forces an extra copy of the screen. - Skip the **Lights module**. Use one flickering point light per fire, or bake light into the environment and fake glow with an additive sprite. - Keep total live particles in the low hundreds per effect; prefer one shared World-space emitter with `EmitParams` (part 4) over many small systems. - Billboards in VR can look "stuck to your face" when you turn your head. Use **Render Alignment: Facing** (faces the camera position instead of the view plane) for close-up particles.