GPU Particle Systems Emitting Increase Over Time?

I’ve noticed that if I have a particle effect that runs at a certain rate for a while and then I let it go for 20-30 mins and come it seems that the emission rate increases and there are noticeably more more particles on the screen.

Take this one for example:

If you hit play you will notice a subtle rain effect. But if you leave it for 10-15 mins and come back it will be a monsoon.

The emitRate and min/max LifeTime I think are particles or time per frame. You have 400 particles per frame lasting 5 frames, so a total of 2000 particles exist at once. Was that your intention?

Not really the issue, the issue is that the emissions does not stay consistent over large timeframes.

When we look through the inspector in Actives Particles, we see the number constantly increasing. How if the particles did not die and therefore increased again and again.
This increases by 1000 every second or so.

That would still not account for there being noticeably more being emitted 20 mins later vs when the scene first starts. Especially in this kind of setup where they are only alive for a set time and then are off the screen. It should be stable given this setup.

I modified the example GPUParticleSystem playground.

What it appears to be doing is emitting a number of particles each second. The number of particles emitted starts at zero and increases by 1 (edit: i.e. increases by emitRate) every second until it reaches the capacity.

I don’t see it documented this way, so might be inferring incorrectly.

emitRate can be less than 1. Setting it to 0.5 means that the number of particles emitted increases by 1 every two seconds.

So yeah it should not be doing that, the emit rate should stay at the same number.

Great example to showcase what I’m talking about!

1 Like

I think what I’m seeing in that playground, in terms of emitted particles, is corroborated by the source code. Especially lines 1851-1855.

The documentation doesn’t seem to match.

emitRate: number
The maximum number of particles to emit per frame

There are also some other properties that might be useful, namely

manualEmitCount: number
If you want to launch only a few particles at once, that can be done, as well.

And updateSpeed.

Not sure if or how these properties work.

Does this help?

That does not answer the fact that each iteration the emission count starts going up.

If its the dead particles being displayed then why are they not displayed at the start? If they have the ability to be hidden completely when dead then they should be just like at the start. It seems more like the emission rate is compounding.

If I set my emission rate to 1, it should stay at one… its rate should not increase, the number of living particles should only added to by the rate of emission not the rate of emission plus dead particles…

If I start a system at a rate of one and then come back 10 mins later and its emission rate is at a couple thousand that is incorrect.

You can see here in @HiGreg’s example that each emission cycle the counts are going up. It should only emit one particle each emission cycle. Not +1 each time.

I’m not sure what you mean by “should.”

The relevant code within GPUParticleSystem is within render(), so this happens each particleSystem “frame”:

    this._accumulatedCount += this.emitRate * this._timeDelta;
    if (this._accumulatedCount > 1) {
        const intPart = this._accumulatedCount | 0;
        this._accumulatedCount -= intPart;
        this._currentActiveCount += intPart;
    }

    this._currentActiveCount = Math.min(this._maxActiveParticleCount, this._currentActiveCount);

    if (!this._currentActiveCount) {
        return 0;
    }

Where this._maxActiveParticleCount is from the capacity parameter to constructor.

In words, each render call _accumulatedCount is increased by updateTime * emitRate and when that value is strictly greater than one, the number of activeParticles is increased by that amount until the there are capacity number of active particles. The number of active particles is the number in view (generally). When a particle dies, a new one takes its place automatically, keeping the number of active particles constant.

The emitRate, then, is proportional to the increase in active particles on each step.

Looking through the code I discovered you can increase the number of new particles using _accumulatedCount += numberOfNewParticles. As long as numberOfNewParticles is strictly greater than one (1.1 works if you need one particle). If you just want to set the number of particles, then also set emitRate to 0. I don’t see manualEmitCount actually implemented. Arguably, it should set _accumulatedCount internally on the next render() (then reset itself to 0), but doesn’t. Also, the code should perhaps check for >= 1 rather than > 1 so that one particle woukd be created.

Another function I can think of you might want to do is to add a new particle that goes away when it dies, instead of being replaced. I don’t see how to do that currently.

I would add that this is not intuitive functionality to me. But I haven’t played around with it enough to strongly recommend making it “intuitive to me.” I might take a shot at proposing an alternative and figuring out if that alternative can be implemented without a breaking change.

That is inherently wrong by particle systems is what I am saying. For example if I go into Unity and spin up a particle system, set the rate to 30 (not single burst but continuous) then every emission cycle it will emit 30 more particles until it hits system capacity. It does not emit 30 particles for one second then 60 for the next then 90 the next and so on.

Im just going to have to look at the code and see when there is time here. There is no reason to set an emission rate of one (with the assumption that it is then one per second) and a lifetime of one second on the particles for there to ever be more than one visible. That is inherently incorrect and the fact it eventually goes to emission rate = to the system capacity on each emission cycle would infer that emission rate is not an emission rate but an accumulation of emission rate.

All in all if I set an emission rate of one and a lifetime of one on the system for the particles, I should be able to walk away and come back to the same visual results. Yes, I could limit the system to one particle in this dumb example but that does not fix the actual problem.

You can see here how particle systems should behave. Them being GPU particles they should still respect the emission rate.

I see what you are saying, and the ParticleSystem implementation makes more sense than the GPUParticleSystem. It’s not immediately clear how to fix GPUParticleSystem. The relevant lines for ParticleSystem are in the ThinParticleDystem.ts file. One relevant line for GPU is in

Shaders/gpuUpdateParticles.vertex.fx lines 184-:

// If particle is dead and system is not stopped, spawn as new particle
if (newAge >= life && stopFactor != 0.) {
    vec3 newPosition;
    vec3 newDirection;
    .
    .
    .

To minimize the need to go back to CPU, lifetime might need to be managed by the GPU in the shader. But it’s not clear whether this works well with the current split of code/data between CPU and GPU. If particles die when age>lifetime, then the CPU updating the uniform buffers is much more difficult because “buffer of alive particles” is not contiguous. I’d be interested to see what updates you recommend!

@Deltakosh I think you did the GPU particles right? Can we get some input on maybe where on the chain to look to get this to emit in the same manor as the non GPU particles?

Sorry but I did not read the entire thread because I’m in a rush. but GPU particles does not work the same way CPU particles do. (You may have noticed it)

On every frame the system increase the active buffer size. Precisely the active particle count:
Babylon.js/packages/dev/core/src/Particles/gpuParticleSystem.ts at 718f4376127a669a44a82f5b64fac772f95bb1b5 · BabylonJS/Babylon.js

this number is used to define how many particles are drawn:
Babylon.js/packages/dev/core/src/Particles/gpuParticleSystem.ts at 718f4376127a669a44a82f5b64fac772f95bb1b5 · BabylonJS/Babylon.js

Hence why at the beginning there is one particle then 2 (because GPU particle works that way: the emit rate is the number of new particles): Babylon.js/packages/dev/core/src/Particles/gpuParticleSystem.ts at 718f4376127a669a44a82f5b64fac772f95bb1b5 · BabylonJS/Babylon.js

There is no notion of dead particles. They get recycled into new ones because the GPU system does not support NOT rendering them

This is something we could add but it is not yet supported. We could discard the fragment when a particle is dead

We probably should, it effectible makes GPU particles systems useless for sustained systems. Its not a problem for one shots, but for long term systems its not going to cut it. (unless there is some magic prop that I don’t understand and am not using).

Its weird that the system can start with hidden particles, but cant hide them on death later.

Thanks for taking the time to look into this with me <3.

I basically would love to leave a GPU particle system alone and the next time I look at it, it be the same as when I left it.

1 Like

They are not hidden, they are not yet alive. They are not rendered at all. Once the buffer is in you cannot go back without losing all perf

The thing is that if you want 1 part at the time you need to set it to one manually. and done. So it will emit only one part forever

Ohhhhhh, ok so for a long term GPU system then its like “one shot” on the spawning and then it will just recycle them?

Interesting. I can probably make them work just have to shift how they are constructed.

let me get a demo for you