Search Unity

Particle system mesh LOD

Discussion in 'General Graphics' started by brad-bit, Mar 28, 2019.

  1. brad-bit

    brad-bit

    Joined:
    Mar 26, 2015
    Posts:
    9
    Hi! I'm trying to create an asteroid field using Unity's particle system. Each asteroid is a mesh, and the particle system is using instancing to draw 100k of them, which is fine as long as the polycount is low. However I'd like the meshes to switch into higher LOD versions when the camera gets close to the asteroid field ring, while still keeping distant asteroids on a low LOD. Any ideas how to do this?

    I could have several particle systems running each with a different LOD mesh, but fading between those would be obvious since the different systems have particles in different places. It might work if I could render a single system's particles using different renderers. Or maybe I could somehow supply the particle system renderer with different vertex streams for different LODs, and switch those in a shader? If nothing else is feasible, I think a tesselation shader with a height map should work, but it's probably slower.

    Has anyone done anything like this? Thanks for your ideas :)
     
    dalekvan likes this.
  2. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Never tried this, but it does seem like it solves the problems with asteroids, particularly the 200k asteroid demo on the site.

    https://assetstore.unity.com/packages/tools/utilities/gpu-instancer-117566

    Other (fancy) options:
    • tessellation shader, keep it lowest lod, add detail closer
    • keep particles billboard but fade out as they get closer to camera in shader, while using particle system callbacks to spawn mesh replacements closer
    • probably many more fancy options people can add
     
  3. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    A script that sets the mesh assigned to the particle system based on camera distance of the particle game object?

    Alternatively, 19.1 let’s you set the per particle mesh index from script, though it would be slow to do a camera distance check per particle. I’d just set the mesh for the whole system based on its distance.
     
    dalekvan and hippocoder like this.
  4. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Duh! sometimes the simplest options are the best...
     
    richardkettlewell likes this.
  5. brad-bit

    brad-bit

    Joined:
    Mar 26, 2015
    Posts:
    9
    Thanks for the ideas! Particle system callbacks was a great idea. I now have a lowpoly particle system, and upon entering a camera-attached collider, I set their lifetimes to zero and emit an identical particle using a different highpoly particle system. And similarly for swapping a high LOD particle for a low one upon exiting the collider. Probably a lot more overhead than doing everything on GPU, but good enough!

    The biggest issue is that I don't think there's any way to do LOD levels beyond two, since (I think?) you can only have one trigger module in a particle system, and you'd need 2 different trigger actions in a middle LOD level... like, low LOD particles can be swapped for medium LOD ones when they enter the medium LOD collider, but the medium LOD particle system can only react to the medium LOD collider (for switching to low LOD upon exiting), or to the high LOD collider (for switching to high LOD upon entering), but not both...

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. public class ParticleSystemLod : MonoBehaviour {
    5.   public ParticleSystem LodParticleSystem;
    6.   public ParticleSystemTriggerEventType Trigger;
    7.  
    8.   private ParticleSystem particles;
    9.   private List<ParticleSystem.Particle> triggerParticles;
    10.  
    11.   public void Start() {
    12.     particles = GetComponent<ParticleSystem>();
    13.     triggerParticles = new List<ParticleSystem.Particle>();
    14.   }
    15.  
    16.   public void OnParticleTrigger() {
    17.     var num = particles.GetTriggerParticles(Trigger, triggerParticles);
    18.     for (var i = 0; i < num; i++) {
    19.       LodParticleSystem.Emit(triggerParticles[i]);
    20.       var p = triggerParticles[i];
    21.       p.remainingLifetime = 0;
    22.       triggerParticles[i] = p;
    23.     }
    24.     particles.SetTriggerParticles(Trigger, triggerParticles);
    25.   }
    26. }
    Notes: Apparently the Unity team has deprecated Emit(Particle) in favor of Emit(EmitParams), which sucks because it makes it unnecessarily hard to spawn clones of a particle. I'm still using it here. Also, there seems to be a bug with setting particle lifetimes to zero - it doesn't actually kill the particles if the particle system's default lifetime is Infinity or a very large value.
     
    dalekvan likes this.
  6. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    We actually just fixed that bug, it's heading to a 2018.3 patch real soon :) https://issuetracker.unity3d.com/is...array-when-remaininglifetime-equals-0-is-used

    You're right, this does make it harder. EmitParams actually (secretly) contains a Particle struct element. I will make a note for us to expose that, so you can simply say:

    Code (CSharp):
    1. var emitParams = new ParticleSystem.EmitParams() { Particle = myParticle };
    2. system.Emit(particleCount, emitParams);
    Until then, you'll have to copy each property out of the particle and into the emitparams.

    Thanks for the feedback!
     
  7. brad-bit

    brad-bit

    Joined:
    Mar 26, 2015
    Posts:
    9
    Wow, thanks! Awesome support :D In 19.1 I'll try switching the particle mesh index, so won't have the need to spawn clones anymore. Thanks for the help!
     
    richardkettlewell likes this.