Particle system overview

From PopcornFX
Jump to: navigation, search

Here you will find an overview of the high-level concepts of PopcornFX particles, with some definition of common terms that will be used throughout this wiki, such as particle mediums, evolvers, samplers, etc...

For an overview of the editor's interface, see Editor interface overview.


PopcornFX particles are added into the world through Particle spawners 'Particle spawners'

When editing, spawners are seen in the realtime editor as Particle layers, and they contain the whole particle definition.
A particle is defined by a list of properties, called Particle fields 'Particle fields', They store things like Position, Color, Velocity, Life duration, etc...

When added to the world, the particles are initialized by a Spawn script, before being added to a special particle container called a Particle medium.
Particle mediums have a simulation definition, called an evolve state attached to them, telling the PopcornFX runtime how to update the particles.
Each evolve state contains a list of Particle evolvers 'Particle evolvers'.

Particle treeview

8 Particle spawners/layers in the treeview


Particle evolvers 'Particle evolvers' are objects that take a list of particles each simulation frame, and applies some operation to them.

Notable particle evolvers are the Physics evolver Physics evolver, the Rotation evolver Rotation evolver, and also the Field evolver Field evolver, that allows you to animate a particle property through time using a user-defined curve.

For performance considerations, a particle medium will often contain particles coming from different instances of the same spawner, and even from instances of different spawners, that share the same simulation state.

A spark effect with a single layer, spawning 10 particles, will use a single particle medium, that will contain 10 particles.
If 10 000 instances of that spark effect are spawned into the game world, there will still be a single particle medium, that will contain all the 100 000 particles of all the instances.

Particle medium bounds

Displaying particle-medium bounds in the realtime editor viewport


Particles are rendered through Particle renderers 'Particle renderers'. As there are particle mediums for the simulation, there are particle render mediums for the rendering.

A particle render medium can render one or more particle mediums.

For example, if two particle layers have different evolve states, but share the same particle renderer (the settings are the same), then they will share the same render medium, and be rendered in a single geometry batch / draw call.

Particle mesh renderer

Mesh particle renderer


Particle scripts can use Particle samplers 'Particle samplers', either to directly set into a particle field, or to use in some intermediate computation.

Notable particle samplers are the Shape sampler Shape sampler, the Curve sampler Curve sampler, and the Procedural turbulence sampler Procedural turbulence sampler.

Particle events Particle events can be triggered when something special happens, such as when a particle is born, dies, or collides with the physical world. They can trigger the instantiation of other particle effects. The C++ runtime also allows user game-code to be notified when a specific particle event is triggered.
Particle events are useful for many things, ranging from fireworks, to spawning sparks, decals or even sounds when particles collide with the world.

Last but not least, Particle effect attributes can be defined. They are vector values global to a specific instance of a whole particle effect, and can be accessed from scripts anywhere inside any layer of the effect. Particle attributes are useful to allow gameplay code to control particle effects behavior on a per-instance scale.

Particle turbulence sampler

Turbulence sampler, 120K particles

Debugging & Optimization

The effect editor also has a few debugging and optimization tools, namely:

1- The Performance budget indicators
Displays easy-to-read rough performance/memory indicators for the current effect
2- The Trace report
It allows you to mouse-pick any particle in the realtime viewport, and inspect its fields
3- The Particle history
When activated, it records all the previous simulation frames, up to a maximum user-defined amount, and allows you to play them back and inspect them.
4- Some Performance warnings
Telling you very explicitely when something unusual could badly hurt performance.
5- The Debug render buffers
Not directly related to particles, but can be useful to see what's going on when rendering custom backdrops, if it doesn't produce the result you'd expect.

Although not intended to be used directly by fx-artists (see the 'For developers' section in the main wiki page):

6- The Realtime profiler
Allows accurate timing of code-sections, and allows to view an entire frame, and what took time computing. also helps tracking memory transactions and threading issues.
Particle picking

Particle-picking in the trace report mode


"But scripting is slow, right?"

There are three things that make Popcorn scripts super efficient:

1. Streams

A script is not run once for every particle, but instead, it is run once for a large batch of particles.
Each operation will run in a massively parallel fashion and process a whole array of values in one go.

When you write something like a = cos(b * c);, if 'b' or 'c' have a stream qualifier, the compiled script can be fed a large array of values, and that simple line will expand into two streamed operations: a vectorized multiplication that performs all the multiplications for the whole batch, then a vectorized cosine that performs all the cosines on the whole batch in one go.

2. Compiler

The script compiler performs optimizations, and is able to fold expressions pretty aggressively. It knows which values vary per particle, which values do not change across a whole particle batch, which values do not change across a whole simulation frame, and which values do not change at all, ever.
It can rewrite and group together expressions based on where they do not change, and:

  • precompute all the constant values at compile time
  • perform some computations only once per batch

For example, let's say we have a curve sampler named "MyCurve", that's inside a layer (ie: cannot change at runtime), and we write the following:
Position = scene.axisForward() * (4*3 + pow(LifeRatio, 2) - 1.5) + scene.axisUp() * MyCurve.integrate(0, LifeRatio) / MyCurve.integrate(0, 1);

This is what the constant folding will produce:
Position = (LifeRatio * LifeRatio + 10.5).00x + (MyCurve.integrate(0, LifeRatio) * 3.478).0x0;

when the project is configured with a Z-up axis system, it will compile down to:
Position = (LifeRatio * LifeRatio + 10.5).0x0 + (MyCurve.integrate(0, LifeRatio) * 3.478).00x;

Here, 3.478 would be the inverse of the integral of that specific curve between 0 and 1. The compiler will see the function call can be precomputed, call it at compile time, and bake the results in with the other constants. It's allowed to do that only if the curve is local to the layer, therefore cannot change at runtime. Otherwise, if the curve was an attribute sampler, the optimizer couldn't make any assumptions, and leave the sample call in the final script, as the curve data could be overridden at runtime by the game code.

3. Optimized SIMD & GPU Backends

In addition to all that, the code that executes the script instructions is heavily optimized and hand-tuned, using each target platform CPU's specific SIMD instruction set.
Also, since v1.9.0 (release pending), we have an experimental GPU backend that generates D3D11 GPU compute shaders, for even faster execution.




PopcornFX粒子是通过粒子生成器 '粒子生成器(Particle spawners)'。

当进行编辑时,粒子生成器(spawners)在实时编辑器中是作为粒子层(Particle layers)的形式出现的,这些粒子层包含了完整粒子的定义。
粒子由一系列属性来定义,这些属性被称为粒子字段 '粒子字段(Particle fields)',,这些字段存储了像位置(Position),颜色(Color),速度(Velocity),生命期(Life duration)等等这样的内容。

当将粒子添加到虚拟世界中之后,在它们被添加到称之为粒子媒体(Particle medium)的特殊粒子容器之前,这些粒子是由生成脚本(Spawn script)来初始化的。
粒子媒体(Particle mediums)附有被称为进化状态(Evolve state)的仿真定义,为的就是告知PopcornFX运行时如何更新粒子。
每个进化状态都包含了一系列的粒子进化器 '粒子进化器(Particle evolvers)'。

Particle treeview

处于树视图下的8个粒子生成器 / 粒子层


粒子进化器 '粒子进化器(Particle evolvers)'指的是每一仿真帧都会带走一批粒子的对象,每个进化器又都会为这些粒子应用某种操作。

著名的粒子进化器包括物理进化器 物理进化器(Physics evolver)旋转进化器 旋转进化器(Rotation evolver)以及字段进化器 字段进化器(Field evolver),它们每一个都允许您通过用户定义曲线来随着时间动画化粒子属性。






粒子是通过粒子渲染器 '粒子渲染器(Particle renderers)'被渲染的。 粒子媒体用于仿真,粒子的渲染媒体用于渲染。


例如,假如两个粒子层拥有不同的进化状态,但却共享同一粒子渲染器(设置都相同),那它们就会共享相同的渲染媒体,且它们也会在单个几何批次 / 绘图调用中被渲染。




粒子脚本可使用粒子采集器 '粒子采集器(Particle samplers)'来直接设置粒子字段,或是将其用在中间计算中。

著名的粒子采集器包括形状采集器 形状采集器(Shape sampler)曲线采集器 曲线采集器(Curve sampler)以及程序式扰动采集器 程序式扰动采集器(Procedural turbulence sampler)

粒子事件 粒子事件(Particle events)可在像有粒子生成,消失或与物理世界碰撞这样的特殊情况出现时被触发。 这些事件可触发其它粒子特效的实例化。 C++运行时也可允许有特殊的粒子事件被触发时通知用户游戏代码。

最后一项但同样很重要的就是可以定义粒子特效的属性(attributes)。 对整个粒子特效的某个具体实例而言这些属性都是全局向量值,且可在特效的任意粒子层内部的任何地方通过脚本来访问这些属性。 粒子属性(Particle attributes)对于让游戏设置代码来控制每个实例尺度上的粒子特效的表现是非常有帮助的。





1- 性能开销指示器(Performance budget indicators)
2- 跟踪报告(Trace report)
3- 粒子历史(Particle history)
4- 一些性能警告(Performance warnings)
5- 调试渲染缓冲区(Debug render buffers)


6- 实时分析器(Realtime profiler)





1. 数据流(Streams)


当您编写像a = cos(b * c);这样的代码时,如果'b''c'使用了数据流限定符,那就可将编译好的脚本输送给大型值数组,且简单的线操作也会扩展成两项流操作:一项是向量化乘法,用来执行整个批次的所有乘法,还有一项是向量化余弦,其会在之后对整个批次上的所有余弦进行一次性执行。

2. 编译器(Compiler)

脚本编译器会执行优化,也能对表达进行非常积极的折叠。 编译器知晓哪些值会每经一个粒子就发生改变,哪些值在整个粒子批次自始至终都不会改变 ,哪些值在整个仿真帧自始至终都不会改变,还知晓哪些值从来都不会改变。

  • 在编译时(compile time)预先计算所有常量值
  • 在每批次只执行一些计算指令

Position = scene.axisForward() * (4*3 + pow(LifeRatio, 2) - 1.5) + scene.axisUp() * MyCurve.integrate(0, LifeRatio) / MyCurve.integrate(0, 1);

Position = (LifeRatio * LifeRatio + 10.5).00x + (MyCurve.integrate(0, LifeRatio) * 3.478).0x0;

Position = (LifeRatio * LifeRatio + 10.5).0x0 + (MyCurve.integrate(0, LifeRatio) * 3.478).00x;

其中,3.478就是特定曲线在0和1之间的积分的倒数。 编译器会查看可被预先计算的函数调用,并在运行时调用该函数调用,之后利用其它常量烘焙结果。 只有曲线对粒子层而言是本地的才被允许这样做,因此不能在运行时更改。 否则,如果曲线是一属性采集器,那优化程序就不能作出任何假设,也会将采集调用留在最终脚本中,其原因就是可通过游戏代码在运行时对曲线数据进行重写。

3. 经过优化的SIMD和GPU后端

此外,为了实现极速执行,从v1.9.0版本开始,我们引入了可生成D3D11 GPU计算着色器的试验性GPU后端