01 - 粒子系统
本文将对游戏引擎中的粒子系统做简单介绍,包括粒子基础、GPU粒子、粒子的应用等内容。
粒子基础
最早的粒子系统在电影《星际迷航》中出现,之后才在3D游戏中普遍应用。
概念
粒子(Particle)是粒子系统里最基础的单位,它通常是一个2D精灵贴图或者是3D模型,并且具有下述属性:
- 位置;
- 速度;
- 大小;
- 颜色;
- 持续时间;
粒子的种类
主要有3种粒子:广告牌粒子(Billboard Particle),网格体粒子(Ribbon Particle),条带粒子(Ribbon Particle) 。
广告牌粒子
这种类型的粒子是一个2D贴图,总会面朝摄像机。
网格体粒子
这种类型的粒子是一个3D模型,在生成时可以对它进行随机放缩等操作,体现随机性。
条带粒子
这种类型的粒子由多个粒子控制点组成,会在相邻的粒子控制点之间渲染四边形样式的特效。
使用条带粒子时候需要应用样条曲线插值,否则结果是不平滑的。
生命周期
每个粒子都有它自己的生命周期,从生成到运动到消亡。
粒子的生成
粒子发射器
粒子在粒子发射器中被生成,之后按照特定的规则逻辑进行运动。
多个粒子发射器便组成一个粒子系统。
生成位置
如下图,可以只在一处发射粒子,也能在一片区域、甚至是网格体上发射粒子。
生成模式
也能按需定制粒子的生成模式,例如每帧持续不断地生成粒子,或是在某一时间点全部生成。
粒子的模拟
生成粒子后,就要按照一定的规则来运动模拟了。
粒子可能会受到一些力的作用,例如重力、阻力、风的扰动等。
确定了粒子受哪些力影响后,便能进行逐帧近似计算,在每一帧快速更新该粒子的位置。
此外,有时候还会让粒子和环境互动,这涉及到高效的物理碰撞检测算法。
粒子渲染
透明混合排序
需要按照从远到近的顺序去渲染透明物,否则会在视觉上出错。
粒子系统的排序主要有两种:
- 全局排序:对系统中所有粒子进行排序,然后从远到近渲染。这样做虽然结果准确,但性能消耗大。
- 派生排序:先对系统中的粒子发射器进行排序,然后单独对每个发射器中的粒子进行远近排序。但可能存在渲染次序错误的问题 。
半分辨率渲染
如果直接在全分辨率场景中突然渲染大量粒子,就会造成帧数锐减和性能消耗。为了避免这些不必要的消耗,可以将粒子渲染在长宽减半的降采样图片上,然后通过一些滤波算法升采样并混合至原图中。
GPU粒子
GPU擅长处理海量并发的东西,因此很适合用于管理粒子系统。GPU粒子系统解放了CPU,让它更专心地去处理游戏逻辑,而且也容易从GPU中获得深度缓冲。
框架概览
将粒子的生成、模拟和远近排序从CPU中搬到GPU,在计算着色器上执行相关任务。
基本流程
初始化
初始化阶段需要维护如下数据结构:
- 粒子池:是一个缓冲存储区,在里面存储所有粒子的信息(如颜色,位置等)。
- 凋亡列表:是一个列表,用于标识当前凋亡的粒子,默认是满着的。
- 存活列表:是两个列表,用于标识上一帧和当前帧中哪些粒子还活着。
生成粒子
假设当前生成了5个粒子,就要从凋亡列表中取出5个粒子,将其放入存活列表中。同时分配5个计算着色器线程用于粒子的生成。
模拟粒子
在此阶段,计算着色器为当前存活的粒子进行模拟,然后将数据写到我们维护的数据结构中。例如6号粒子凋亡后,需要将其从上一帧的存活列表中取出,塞入凋亡列表中,然后剩下存活的粒子再放入当前帧的存活列表中。
视锥体裁剪
GPU中还容易做视锥体裁剪操作,需要维护的数据结构如下:
- 一个新的存活列表,用于标识当前摄像机能看到哪些粒子;
- 一个深度缓冲区,用于标识粒子离摄像机的距离。
排序粒子
接下来会根据深度信息从远到近排序存活列表中的粒子,准备渲染。
在GPU上排序常用并行的归并排序,有两种思路:
- 从源数组角度考虑:对于归并前的源数组,每个元素开一个线程,让线程决定这个元素在目标数组的位置。这样做会带来效率降低,因为写操作是内存上不连续的。
- 从目标数组考虑:对于归并后的目标数组,每个元素开一个线程,让线程决定该元素应该来自源数组的哪个元素。这样做是内存友好的,效率较高。
渲染并交换存活列表
接下来就能按存活列表中的顺序渲染粒子了,然后用双缓冲思想交换存活列表,供下一帧计算使用。
环境交互
有的粒子还需要和环境交互,如果直接用物理引擎对全部粒子都算一遍碰撞的话,性能会很差。人们常用屏幕空间的深度信息来进行粒子碰撞的快速近似计算。
高级应用
粒子系统的高级应用。
人群模拟
在街道上走的一个个小人都可用粒子来表现,这需要准备一个支持动画的网格体来充当粒子。
此外,为了让粒子小人做不同的动作,需要在粒子系统中实现一个小型的状态机。
有了可动的粒子小人后,接下来需要给它导航,让它们行走在正确的道路上。这需要导航纹理的帮忙,导航纹理通常有两个:
- 符号距离场纹理,用于标识哪些位置是可通行的,哪些位置是障碍物。
- 方向纹理,用于标识人群行进的方向。
动态变换
例如有一个网格体粒子A,可通过动态程序生成样条曲线的方式,将A分割成其他小粒子,然后再组装成网格体粒子B。
和环境的交互
例如用粒子模拟鸟群,人走进时鸟群会避开人。
设计思想
基于样式预设
早期引擎粒子系统的设计很简单,只需预先设定好粒子发射器的样式等属性即可。但维护比较麻烦,添加一个新特性需要写新的代码。
基于图结构
现代引擎的粒子系统则基于图结构设计,模块化、逻辑清晰且容易维护。
二者混合
也能将二者的优点结合起来,例如虚幻引擎的Niagara粒子系统。
参考资料
- GAMES104 (boomingtech.com)
待阅读的资料
- Programmable VFX with Unreal Engine’s Niagara – GDC 2018
- The Destiny Particle Architecture – SIGGRAPH 2017
- Frostbite GPU Emitter Graph System – GDC 2018
- The inFAMOUS: Second Son Particle System Architecture – GDC 2014 https://www.gdcvault.com/play/1020367/The-inFAMOUS-Second-Son-Particle)
- Compute-Based GPU Particle Systems – GDC 2014
- The Visual Effects of inFAMOUS: Second Son – GDC 2014
- Mergesort - Modern GPU
- A Faster Radix Sort Implementation – Nvidia