Particle system overview

From PopcornFX
Revision as of 13:08, 30 June 2016 by Admin (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
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.


Birth

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

Simulation

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

Rendering

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

Advanced

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


Performance

"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粒子的高层次概念,对一些贯穿于此维基版面的常用术语的定义,像粒子的媒体(mediums)进化器(evolvers)采集器(samplers)等等有个总体了解。

要想对编辑器界面有个总体概观,请参阅Editor界面概述


产生

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),它们每一个都允许您通过用户定义曲线来随着时间动画化粒子属性。


出于对性能方面的考虑,粒子媒体通常会包含来自同一生成器(spawner)的不同实例的粒子,甚至还可包含来自不同生成器的实例的粒子,这些粒子都将共享相同的仿真状态。


假设某一火花特效带有可生成10个粒子的单一粒子层,且该特效又会使用包含10个粒子的单一粒子媒体。
那么如果该火花特效会在游戏世界中生成10,000个实例,则总会存在某个单一粒子媒体会将这所有实例的全部100,000个粒子都包含其中。

粒子媒体的边界

在实时编辑器视窗下显示粒子媒体的边界

渲染

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

粒子的渲染媒体可渲染一个或多个粒子媒体。

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

粒子网格渲染器

网格(Mesh)粒子渲染器

高级

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

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


粒子事件 粒子事件(Particle events)可在像有粒子生成,消失或与物理世界碰撞这样的特殊情况出现时被触发。 这些事件可触发其它粒子特效的实例化。 C++运行时也可允许有特殊的粒子事件被触发时通知用户游戏代码。
粒子事件对于许多事物是非常有用的,其涉及了从烟花到生成的火花,印花,甚至是有粒子与虚拟世界碰撞时产生的声音这一适用范围。


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

粒子扰动采集器

扰动采集器,120K个粒子


调试和优化

特效编辑器也拥有一些调试和优化工具,即:


1- 性能开销指示器(Performance budget indicators)
用来为当前特效的显示粗略又易读的性能/内存指示器。
2- 跟踪报告(Trace report)
允许您在实时视窗下通过鼠标拾取任意粒子,也允许您检查该粒子的字段。
3- 粒子历史(Particle history)
当其被激活后,它就会记录之前的所有仿真帧,直到达到用户定义的最大数量为止,它也允许您对这些帧进行回放和检查。
4- 一些性能警告(Performance warnings)
当有异常情况可能会严重削减性能时这些警告就会非常明确地告知您。
5- 调试渲染缓冲区(Debug render buffers)
不与粒子直接相关,但这对于在渲染自定义背景时未产生您期望的结果的情况下查看出现了什么情况是非常有用的。


尽管我们没打算让特效师们直接使用(具体请参阅维基主页下的'适用开发者'部分),但我们还是提供了:

6- 实时分析器(Realtime profiler)
可用来对代码段进行精确定时,允许查看整帧画面,还可查看花费时间计算的是什么内容,也有助于跟踪内存事务处理和线程问题。
粒子拾取

P跟踪报告模式下的粒子拾取


性能

"不过,不得不承认脚本处理的确是慢!"

但如下三项举措的实施则使得Popcorn脚本拥有了超高执行效率:


1. 数据流(Streams)

程序不会为每个粒子都执行一次脚本,相反,却会为大批粒子执行一次脚本。
每项操作都将以并行方式进行大规模地执行,且会一次性处理整个值数组。

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


2. 编译器(Compiler)

脚本编译器会执行优化,也能对表达进行非常积极的折叠。 编译器知晓哪些值会每经一个粒子就发生改变,哪些值在整个粒子批次自始至终都不会改变 ,哪些值在整个仿真帧自始至终都不会改变,还知晓哪些值从来都不会改变。
编译器可根据表达在何处不改变来重写它们,同时又对它们进行组合,还会:

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

例如,假设我们使用了一个名为"MyCurve"的曲线采集器,其位于粒子层内部(也就是不能在运行时更改),我们编写如下代码:
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;

当项目配置了Z轴朝上的坐标系时,上述表达就会被编译为:
Position = (LifeRatio * LifeRatio + 10.5).0x0 + (MyCurve.integrate(0, LifeRatio) * 3.478).00x;

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


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

除了上述谈到所有内容之外,我们还通过每个目标平台CPU的专用SIMD指令集对执行脚本指令的代码都做了高度优化和手工调优。
此外,为了实现极速执行,从v1.9.0版本开始,我们引入了可生成D3D11 GPU计算着色器的试验性GPU后端