Niagara 粒子系统
Niagara VFX System
Niagara 是 UE5 的 VFX 系统,由系统 (System) → 发射器 (Emitter) → 模块 (Module) 的栈式结构组成:发射器按 Emitter Spawn/Update、Particle Spawn/Update、Event、Render 分组,模块是自上而下执行、基于 HLSL 的「读-算-写」单元。继承、动态输入、事件和数据接口 (Data Interface) 提供复用与外部数据访问;发射器可选 CPU 或 GPU 计算着色器模拟。官方 5.8 文档未找到 CPU/GPU 对比页,相关取舍按通识标注。
四个核心组件
官方把 Niagara 拆为四个核心组件:系统 (System)、发射器 (Emitter)、模块 (Module)、参数 (Parameter)。(Niagara Overview)
flowchart TD S["Niagara System 系统 效果的容器/时间线"] --> E1["Emitter 发射器 A"] S --> E2["Emitter 发射器 B"] E1 --> ES[Emitter Spawn] --> EU[Emitter Update] --> PS[Particle Spawn] --> PU[Particle Update] --> EH[Event Handler] --> R["Render 渲染器"]
- 系统:构建一个效果所需一切的容器,系统级行为作用于整个效果;时间线面板管理其中的发射器。
- 发射器:粒子生成的地方,组织为一个栈 (Stack),分组为:Emitter Spawn(发射器创建时,在 CPU 上)、Emitter Update(每帧,在 CPU 上)、Particle Spawn(每个粒子诞生时一次)、Particle Update(每粒子每帧)、Event Handler(生成/监听事件)、Render(精灵、网格等渲染器)。
- 模块:基本构件,在组内自上而下顺序执行;「一个做数学的容器」——读入数据、计算、写回,供后续模块使用。模块以 HLSL 为基础,可在图中可视化搭建,也可用 CustomHLSL 节点内联代码。多个模块对同一属性的贡献累积在临时命名空间中。
- 参数类型:Primitive、Enum、Struct、数据接口 (Data Interface)。(以上均见 Niagara Overview)
这与 粒子系统 中「发射 → 逐粒子更新 → 渲染」的通用结构一致;Niagara 的特点是把每一步变成可组合、可继承的脚本模块。
扩展与复用
| 机制 | 作用 |
|---|---|
| 继承 (Inheritance) | 子发射器可覆盖父级任何东西,也可还原;避免平铺资产导致重复造轮子 |
| 动态输入 (Dynamic Inputs) | 与模块同法构建,但作用于一个值类型而非参数映射;可链式组合,减少模块膨胀、改善性能 |
| 微表达式 (Micro Expressions) | 把任意内联值改成一小段 HLSL 表达式 |
| 事件 (Events) | 用结构体载荷在粒子/发射器/系统间通信;事件处理器有独立执行栈 |
| 数据接口 (Data Interfaces) | 可扩展地访问任意数据:网格、音频、外部 DDC 信息、代码对象、文本容器;可作为插件编写;骨骼网格数据接口可取骨骼网格的任意数据 |
(Niagara Overview)数据接口是 Niagara 与引擎其他部分交互的主通道,例如 Chaos 破碎的断裂/碰撞事件可被 Niagara 读取来生成粒子(见 Chaos 物理与碰撞,Physics in Unreal Engine)。
CPU 模拟 vs GPU 模拟
官方 API 层面,发射器的模拟目标 (Sim Target) 为枚举 CPU_SIM / GPU_COMPUTE_SIM;在 UE 5.8 Python API 中,sim_target、gpu_compute_script、max_gpu_particles_spawn_per_frame、simulation_stages 等旧属性已标为弃用,改从 GetEmitterData() 返回的结构访问。(NiagaraSimTarget / NiagaraEmitter (Python API),枚举页版本为 5.6)
官方取舍(UE 5.8「Scalability and Best Practices for Niagara」,Scalability and Best Practices for Niagara):
- Niagara 在 CPU 上用一个模仿 GPU SIMD 的虚拟机运行脚本,所以同一套脚本默认在 CPU 和 GPU 上都能跑。代价是每个发射器都有固定开销:即使粒子在 GPU 上模拟,System 和 Emitter 脚本仍在 CPU 上运行(以便与游戏线程交互),降低这部分开销的唯一办法是减少发射器数或关卡里的实例数(5.4 起有实验性的 Lightweight Emitters)。
- 切换 Sim Target 只改变 Particle Spawn、Particle Update 和模拟阶段 (Simulation Stages) 脚本的运行位置。粒子脚本最容易并行,所以受益最大,多数情况下 GPU 模拟更快、能承载更多粒子。
- 粒子很少时 CPU 模拟可能更合适,因为 GPU 资源不能细分:具体取决于硬件,但 1 个粒子的模拟可能占用和 64 个粒子一样的资源(一个线程组/波的粒度,对照 GPU 计算调度与占用率)。
- 瓶颈决定选择:GPU 是瓶颈的项目和平台(尤其是显存较小的移动端)可能更适合 CPU 模拟;反过来,CPU 瓶颈的项目即使粒子不多,也可能值得移到 GPU。官方强调要实际分析验证。
- 包围盒:GPU 模拟时系统读不到效果有多大,所以必须设置固定包围盒 (Fixed Bounds),否则会报警告(How to Create a GPU Sprite Effect in Niagara)。一般来说固定包围盒比动态包围盒更快,例外是小而远距离移动的效果(如横穿关卡的弹道),它的固定包围盒会很大,看不见时也仍被视为相关。
- 其他建议:避免预热 (Warmup)(每帧需顺序求值,易卡顿);用属性读取数据接口代替事件;排序默认关闭,开启后在 GPU 上排序,会增加开销和索引缓冲内存。
下表把官方要点和仍属通识的部分(碰撞方式、与玩法交互)分开标注:
| 维度 | CPU 模拟 | GPU 模拟(计算着色器) |
|---|---|---|
| 粒子规模 | 少量粒子时可能更合适(官方) | 大量粒子更快(官方) |
| 包围盒 | 可动态计算 | 必须固定包围盒(官方) |
| 平台倾向 | GPU 瓶颈/移动端(官方) | CPU 瓶颈项目(官方) |
| 与玩法交互、碰撞 | 方便,可用物理场景碰撞(通识) | 回读昂贵,碰撞常用深度缓冲/距离场近似(通识) |
注意官方明确 Emitter Spawn / Emitter Update 组在 CPU 上执行,所以即使选 GPU 模拟,发射器级逻辑仍在 CPU。(Niagara Overview)
性能与坑
- 官方提醒:每个模块、发射器、系统都占用资源;先找现成模块,善用动态输入,而不是新建模块。(Niagara Overview)
- 粒子渲染多为半透明叠加,过度绘制 (Overdraw) 是常见 GPU 瓶颈,渲染材质的混合模式见 材质系统;定位方法见 UE5 性能分析(通识)。
出处
- Niagara Overview UE 5.8 文档
- NiagaraSimTarget / NiagaraEmitter (Python API) UE 5.6/5.8 Python API
- FVersionedNiagaraEmitterData API UE 5.8 API 参考
- Scalability and Best Practices for Niagara UE 5.8 文档
- How to Create a GPU Sprite Effect in Niagara UE 5.8 文档