原文链接:《UE4:Niagara 使用基础要点》 | 作者:Feilon
Edition 1.00. Base on UE Ver.4.26
第一章:概述
Niagara 作为 UE4 主打的次时代粒子系统,在 4.26 上带给了所有开发者惊人的表现力。UE 官方花了数年的时间沉淀这套系统,几乎每一个版本都在使用层面有巨大的改善,在功能层面有巨大的扩充。在 4.26 前,这些功能充其量只是原有 Cascade 粒子系统的延伸,让粒子系统能以脚本的形式书写逻辑并与蓝图交互。虽然这些功能也很诱人,但是使用 Cascade 系统我们也能以一些 Hardcode 的形式实现类似的效果,因此人们更愿意选择更加稳定的 Cascade 系统。而在 4.26 后,Niagara 粒子系统可谓正式脱胎换骨,产生了真正的质变,不仅自己粒子系统内部的功能有了极大的扩展,还能通过这些内容让原先其他系统实现起来是否复杂、需要各自底层 hack 的效果,变得十分简单直观。可以说即使不是专注于做特效的人,也十分有必要熟悉一下 Niagara 的内容,因为它已经不仅仅是一个粒子特效制作工具,更是能让整个游戏开发流程、架构脱胎换骨的核心系统,是真正意义上的次时代渲染系统。
因此我们这里开始 Niagara 系统的详细讲解与梳理。虽然 4.26 已经推出了 Niagara 的正式版,但是实际的使用体验是很多内容还有待进一步的完善,整个系统的体验有待优化,不是那么新手友好,很多使用的注意事项没有任何提醒,甚至错误了也不会报错。而网上教程的短缺更是使这个系统难以上手。
我们会从基本原理逐渐渗透到 Niagara 系统的每一个细节,每一个模块的使用。旨在让读者有一个全面的认识后,对每个简单、复杂模块乃至新特性都有使用上的指导。
由于 Niagara 涉及到的内容太多,因此介绍 Niagara 不会只限于这一篇文章。本文只是一个引子,讲解一些基础中的基础,帮助零基础的人能从粒子系统开始认知,了解 Niagara 的编辑流程。
由于 Niagara 还处于更新阶段,为了覆盖更多的新特性,本系列文章会一直跟进最新的版本进行讲解。如果今后的更新修改了之前的某些内容,本文也会做相应的更新跟进这些改动。
阅读本系列文章还是需要一定的数学基础、渲染知识基础与 HLSL 代码常识,本文不会去讲述这些内容的细节。当然大部分使用内容并不需要这些基础来理解,但是制作特效永远避不开这些数学公式的堆砌。而尚未完善的 Niagara 粒子系统还是需要很多的 HLSL 代码来更方便地实现一些复杂的效果的。
本文作为系列文章的开篇,不会去过多地讲解这些深层次的内容。但是今后涉及到一些复杂效果的讲解、示例的制作,可能就需要读者有一定的基础了。
第二章:粒子系统
为了方便零基础的人阅读,我们先来讲一下什么是粒子系统,为什么需要粒子系统。
一般在游戏行业,提到特效就会想到粒子系统,当然特效还包括材质特效、后处理特效等,但粒子系统一定是游戏开发中特效占比最大的。
粒子系统本身并没有创造任何全新的游戏实体,他也是在利用游戏开发中本身就存在的模型、面片都基本元素进行组合来实现效果。我们不使用粒子系统,也能在一般的开发环境下使用一些逻辑实现这些效果。
但是做特效的人往往希望自己做的特效能拥有无限的细节,有无尽的组成成分,来展现最惊人的品质,这与游戏开发相对节制的效果制作理念是相悖的。由于游戏引擎相对死板的渲染管线,即使我们制作的特效中存在大量相似的元素,以量来堆砌视觉感,但是渲染管线还是会将他们当做完全不同的内容逐个处理,因此我们很难使用一般的开发流程来堆量,没有量又很难出效果。
一般的开发流程可以有类似 Instance 的模式,来批量处理相同的模型与材质,大幅度提高运行效率,但是 Instance 的应用相对死板,不是那么容易处理多变的情况。
而粒子系统可谓是 Instance 思想的延伸,为了能够处理特效中充斥的大量类似的元素,我们给予其相同的运动规律,并用统一的公式来表述。通过给予不同的或随机的输入,来让其产生多变的效果。而这些类似的元素,都可以以一种 Instance 的形式进行统一批量的处理,并做针对性的优化,最大程度地节省效能,从而让我们能更随心所欲地在特效中堆量。每一个元素无论是模型、面片亦或是其他的什么东西,都被称作一个粒子。
然而不管再怎么使用 Instance 的思想优化,CPU 单线路的运行模式以及与 GPU 的通信开销都很难进一步节约,这也是 CPU 粒子的极限。一旦粒子数量过大,CPU 的数据准备开销、与 GPU 的通信开销都会显著提升,粒子数量很快达到瓶颈。
于是我们想到 GPU 超凡的并行计算能力非常适合粒子系统。首先粒子系统中的大部分同类型粒子都是不相干亦或是简单相关的,因此每一个单独的粒子随时间的演化都是相对独立的,可以单独使用 GPU 的一个运算单元解决。而 GPU 可以同时运行大量的运行单元,他们都执行相同的计算公式。我们以图形数据的格式批量给予其不同的输入,就能在 GPU 中批量演化这些例子。GPU 粒子带来的提升是十分显著的。对于移动端,尽管早期的架构并不适应这种 GPU 粒子的运算形式(因为移动端 GPU 上能保存一定时间的资源非常有限,带宽也有很大限制),但是最近的 GPU 架构也逐渐有所改观。
第三章:Niagara 粒子系统
我们这里先重点介绍 Niagara 粒子系统与之前传统的 Cascade 粒子系统的不同,让大家了解 Niagara 粒子系统的强大。
Cascade 粒子系统满足了我们上述所介绍的粒子系统的基本功能,他能够定义每一种粒子的基本行为模式,给予一定规律的输入使其产生变化,不同类型的粒子可以产生简单的交互。通过这些内容我们就能实现很多可期的不错的效果。
但是粒子效果往往很难单独呈现,其基本单元无论是面片(最常用)亦或是模型,都需要材质。很多的效果如火焰等并非一个个独立的个体单元组成,这就需要一些材质效果予以辅助,通过粒子系统生成一定规律的面片来实现。
我们已经有了这样强大的功能,为什么还需要升级我们的粒子系统呢?
官方最大的初衷是让所有的特效变得能与游戏中的实体进行更加深入的互动。我们不再是触发某个事件之后,把一大堆特效粒子释放出来就完事了。我们希望这些或多或少有些穿模的、似乎与游戏行为完全不相干的粒子们,能对我们的角色产生反应,让他们变得更加真实有感觉。
而粒子系统本身正如我们之前所说,是描述大量有重复规律的元素,是可能单独在 GPU 上单独进行运算的元素,是不方便编写复杂多变的游戏逻辑的元素。在渲染管线中也很难获得游戏中的、其他 Pass 的数据进行交互。因此官方就单独另起炉灶,重新搭建架构,将粒子系统的渲染靠后,使其能获得最充足的整个渲染流程中的信息(比如屏幕空间的渲染结果、光照信息等),并使其获得接受 CPU逻 辑层访问,并给予 CPU 逻辑层信息传递的接口。这些能力,都能让我们的特效变得是能与我们的游戏角色、场景产生交互的,更真实地存在于世界中的效果。
而虚幻官方并没有止步于此,使其变成一个需要底层能力的高大上的工具,而是为其提供了足够强大的前端蓝图编辑与设置系统,逐步完善与优化整个工作管线,让不了解代码逻辑的人也能更轻松地编写粒子的运行逻辑。
在最新的版本中,UE 官方更是大大加强了 Niagara 粒子对渲染底层的访问能力。本来在原有的材质系统中都无法在前端直接访问的 UAV
、ComputeShader
等高级功能,这些需要些底层 HLSL 代码的功能,在 Niagara 上都给予了充分的前端的支持。这使得 Niagara 本身成为了一个独立于材质系统的功能强大的 Shader
编写程序,可以实现材质系统几乎所有的运算能力,并且能帮助原有材质系统实现一些本来实现起来不是很直观的能力(比如多个不同的材质通过 RenderTarget
进行交互等)。以前 虚幻4 被诟病的一些渲染管线的弊端,一些需要通过 hack 底层才能实现的复杂 trick ,现在都能够通过 Niagara 粒子系统这一中介来以纯前端的形式完成。
甚至于 Niagara 粒子系统里可以没有任何粒子,而是作为一个纯粹的 ComputeShader
来为我们的游戏效果服务,甚至是用来加速一些 CPU 上处理起来比较复杂的逻辑运算。
可以说,Niagara 粒子系统的出现,打破了传统游戏开发的模块分割模式,让游戏开发进入了次时代。
当然,以上几乎所有的功能,都是支持移动端的,而不是一个只局限于 3A 大作的高大上的工具。
第四章:Niagara 的简单使用与初级原理
UE 官方做了十分大量的准备,来试图让 Niaraga 变得容易上手,然而由于其使用结构还迟迟未定,因此官方文档也很难大量讲解。加之系统本身集合了大量以前没有的新功能、新模块,不得不说目前的上手难度还是偏高。
而 UE 官方为了能让之前使用 Cascade 粒子系统的人能更快速地适应 Niagara ,在 UI 操作层面进行了最大可能的结构还原。其基本的配置思路与 Cascade 别无二致。因此能使用 Cascade 粒子系统制作的特效,在 Niagara 上上手还原还是很容易的。之前使用 Cascade 粒子系统制作的效果,官方也提供了工具做一键迁移。
最简单的,我们可以直接在 UE4 的内容浏览器中,右键菜单进入 FX 项内,找到 Niagara System 进行创建即可。其他 FX 下的 Niagara 组件内容我们会在之后的文章中讲解。
点击进行创建后,会弹出如下选框。我们可以选择已有的粒子发射器进行创建,可以复制已有的粒子系统,也可以选择创建一个粒子系统的模板(Niagara 粒子系统的模块化与抽象化也是其高级的地方之一,但是这些内容需要有代码功底才能理解,比较进阶,我们之后的文章会详细讲解),或者是创建一个空的 Niagara 系统。
选择使用已有的粒子发射器创建,我们就能看到下面的弹窗。这里列举了一些 UE 官方使用粒子系统创建的发射器的示例,几乎包含了所有之前使用 Cascade 粒子系统能够创建的基本粒子发射器的类型。我们选择其中一种进行简单的参数调整,就能还原之前的很多效果。这里我们为了讲解,还是从一个空的粒子系统的创建开始。
打开粒子系统的编辑器,我们能够看到一个蓝图背景的面板,其中只有一个简单的粒子系统的结构。在这里我们能够设置整个粒子系统全局的属性,包括是否循环、生命周期等。
我们先忽略其他的侧边栏,专注于这一主干。首先是系统设定,包括 用户参数 以及 系统属性 。用户参数是我们在外界(比如蓝图、材质)与 Niagara 进行数据交互的主要接口,此处点开会发现参数为空,我们暂且跳过。下一项系统属性是我们对全局属性的设定,选中后会在右边栏弹出对应的设置选项。
这里有很多粒子系统全局的性能参数,以及很方便的 Debug
选项功能。通常我们还不需要调整这些属性,我们先关注几个更加常用的选项。
首先是 Fixed Bounds
的设置,这设置了我们的粒子系统的可见范围。我们知道游戏引擎是会做可见性剔除的,看不见的东西不会去渲染甚至执行逻辑。而粒子系统的粒子是可能全世界乱飞的,我们希望能不让粒子本身消失,又希望完全看不到粒子时不去渲染它。对每个粒子都做可见性剔除是不现实的,我们最简单的方式就是为粒子系统设置其本身的体积,扩大其可见范围,使其在粒子运动的主要范围内都是可见的。只要我们的视野包含了这块体积的一小部分,整个粒子系统就会去执行其渲染逻辑,否则整个系统都不会被渲染。合理地设置这个体积十分重要,如果你的粒子都局限在很小的空间,应该设置较小的体积;若粒子运动范围很大,应该设置更大的体积。默认情况下不设置,我们看不到粒子系统本身的位置,所有的粒子就都会瞬间消失,这通常不是我们想要的结果。
我们还应该关注最下方的 Asset Options
,它是点击下拉框才会展开的选项,其中最重要的第一项,是我们 是否让我们的粒子系统能被其他粒子系统索引到 的关键选项,默认情况是关闭的。打开它我们才能在其他粒子系统中找到并使用当前的粒子系统。
之后我们回到核心模块。看 System Spawn
与 System Update
两行。 UE 制作的 Niagara 粒子系统的前端表现,其执行顺序都是 从上到下顺次 执行的。也就说首先会进行 System Settings
,进行所有内容的设置。之后进行 System Spawn
,目前这个栏目下面是空的,我们可以点击 +号 来想其中添加一些初始化时 只执行一次 的内容,其中所有内容的执行顺序也是从上到下排列。之后开始执行 System Update
。这个模块下面的所有子模块都是每帧会从上到下顺序执行一次,是 Tick
类型的逻辑,目前其下面只有一个默认的 System State
子模块,右面的对号表示其当前状态为开启。任何一个非设置类的子模块都可以通过简单地勾选对号,来方便地打开关闭,这是一个十分方便进行调试与 Debug
的功能,非常重要。
点击 System State
,我们能看到右边的详情页出现了具体的设置,其中就包括我们之前提到的关于一些生命周期相关的内容的配置。
这些内容基本都能通过名称来判断其含义,我们此处不详细说明。当想使用其制作一些效果时,其实有一些配置上的细节问题需要处理,我们之后的文章会详细讲述。
第五章:粒子发射器基础
我们这个 Niagara 系统目前是空的,不包含任何的粒子发射器。我们需要右键蓝图工作区任意空白处,创建一个粒子发射器 Emitter。此时也可以选择之前所有已有的粒子发射器直接创建,为了讲解我们还是先选择一个空的发射器创建。
一个 Niagara 系统可以有多个粒子发射器,默认情况下他们是从左到右排列的。但是他们的执行顺序并不一定是从左到右的。首先 Niagara 系统模块肯定是最先执行的。但是对于每一个粒子发射器,其执行顺序是根据我们模块中变量的引用关系来智能决定的。因此我们在编写逻辑时不需要考虑复杂的模块间交互,而是把这些问题丢给底层处理,这很方便。
注意看这些新生成的模块,我们同样能看到 Emitter Settings
、Emitter Spawn
、Emitter Update
,他们的含义与 System 相同,其执行顺序也是从上到下,Update 模块每帧执行。
在 Emitter Property
的详情页中,我们能看到一些与 System 类似的关键设置,比如 Fixed Bounds
、Asset Options
。Sim Target
中我们能够选择 CPU 还是 GPU 来计算粒子,其中 GPU 粒子必须设置Fixed Bounds
。而 Scalability
则是我们需要给粒子系统当前的 Emitter 配置哪些渲染等级。这也是 Niagara 的一个高级的地方,可以针对每个模块独立设置渲染等级,可以更方便地适配不同性能的各个平台。
Simulation Stages
是 4.26 版本推出的高级功能,我们之后的文章会详细展开这部分内容。
再回到核心模块继续往下看,可以看到 Particle Spawn
以及 Particle Update
。他们包含在 Emitter Update
模块下,都是每帧执行的。在 Particle Spawn
中我们可以设置 Particle 的生成规则,在 Particle Update
中可以设置每个粒子每一帧的更新逻辑,每个粒子都会独立执行这段逻辑。
再往下看 Add Event Handler
,点击加号可以添加多个 Event Handler
顺次执行,这些是当前粒子发生器对于某个事件的相应规则。
最后是 Render 模块。Render 模块放在了最后也意味着它是最后执行的,顾名思义它是用来添加我们的粒子对应的显示实体的,可以为空,也可以为多个。为空的情况意味着所有的计算不需要任何前端的显示,比如我们用粒子系统做一个纯粹的 ComputeShader
。目前 4.26 版本有 5 种不同种类的渲染实体,都能在示例的 Emitter 中找到对应的实例,除了常见的模型 Mesh、面片 Sprite ,还有高级的 Ribbon 、Light 以及 4.26 新出的 Component,我们会在之后的文章中详细展开每一种类型的 Render 的不同。
我们可以在一个粒子发射器中配置多个粒子模块,但是注意他们都会遵循同一套运行规则。如果 Render 依附于粒子,那么同一个粒子的位置会同时显示两个渲染实体,这通常也不是我们想要的结果。因此如果想要出现多个渲染元素,并且他们之间可能存在某种关系,最好新建一个粒子发射器。同一个粒子系统下的粒子发射器之间都能很方便地进行任何形式的数据传递。
第六章:结语
本文只是一个引子。我们之后会由浅至深讲解更多的内容,尽可能地覆盖其中的每一个模块、每一个实现细节,并做一些特效的示例。