粒子系统概览

From Valve Developer Community
< Zh
Jump to: navigation, search
English (en)Русский (ru)中文 (zh)Translate (Translate)

粒子系统粒子系统是使用大量小物体的组合来模拟一些伪3D视觉效果的系统,例如云、射线束。从求生之路起,起源引擎还加入了全屏的后处理特效。

粒子系统通常包含了不止一类的嵌套子系统,每种都是由控制粒子系统行为的各类组件组成。因此,粒子系统得以生成极其简洁或复杂的效果。

粒子是使用粒子编辑器(en)制作的。你必须把粒子编辑器创建的所有 PCF 文件添加到 <游戏目录>\particles\ 文件夹中的 particles_manifest.txt(en) 清单文件,游戏引擎才能识别到它们。

示例

Particle effects from Alien Swarm

剖析

内存字段
粒子系统的设计是用较少的内存占用换取更多运算资源。为了达到这一目的,粒子系统中的每个粒子只使用了少量字段来描述,例如位置、持续时间、颜色、旋转等。基于这些属性字段,粒子系统的组件得以得以控制粒子系统,计算每个粒子在每一帧的状态。
挂点(粒子)(en)
挂点是粒子系统的基础外部输入机制,每个粒子系统最多有64个挂点。挂点包含一个位置、朝向、和一个它们允许引用的实体对象。默认情况下,挂点0表示的是粒子系统的原点和朝向。
如果把挂点和一个实体对象关联起来,它就可以被绑定到该对象,或该对象的附着点上。同理,粒子系统中每个额外的挂点都能设置它的位置、朝向和绑定的实体。这样一来,军团要塞中 Medic 的治疗射线束就能够从治疗枪射向被治疗的队友。
这也让单个的粒子系统能够在多个点或对象间生效,例如制作出蔓延的火焰的效果。
同时,粒子系统的各种元素都可以获取挂点信息,如此一来,粒子系统可以同时受到多个动态元素的影响。挂点在默认情况下表示空间中的一些位置点,同时,它们还能存储其他附加信息。因此,我们可以通过外部代码或实体来向粒子系统输入一些通用信息,并通过内部的处理转换成系统自身的属性信息。
基础属性
在基础状态下,一个粒子系统不包含任何组件,只有一些基础属性。这些属性是所有系统的通用属性,包括影响整个系统的属性、每个粒子中不变且无法适配其他接口的属性,这些组成了一个系统。
组件
除了基础属性之外,粒子系统还可以包含各类可集成的组件。通常来说,每一类组件的添加数量没有上限,即使同时集成了多个相同组件也没有问题,只要能够满足你的需求。这些组件可以在每个粒子的属性字段上起作用,以便设置或修改它们。
渲染器(en)
渲染器用于指定粒子的绘制方式。包括精灵贴图(Sprite,一种始终面朝玩家相机的贴图/实体类型)、绳索、条带等等。如果你想的话,每个粒子都能以不同的方式被多次绘制。
发射器(en)
发射器用于指定在什么时间段内生成多少数量的粒子。如上所述,一个粒子系统可以包含多个发射器,分别指定不同的发射类型,来构成粒子系统的整体图形,
初始化器(en)
初始化器用于设置每个已生成粒子的初始状态,初始化它们的属性字段。例如,一个粒子的初始空间位置、颜色、尺寸和透明度。初始化器只会在每个粒子生成时设置它们的初始状态,之后将无法对粒子产生影响。
操作器(en)
一旦粒子生成时的初始状态由初始化器设置完毕,操作器便会接管粒子,并在每一帧根据设定的函数更新粒子的状态,直到粒子消失。
作用力(en)
作用力是操作器的一类变体,可以影响粒子的运动状态。
约束器(en)
约束器用于约束粒子的运动,例如使用碰撞约束或是基于控制点的距离约束。
子系统(en)
简而言之,子系统就是嵌套到你的粒子系统中的其他粒子系统。部分数据,例如挂点信息,可以向下传递到子系统中。并且子系统可以相互嵌套很多层。

创建我们的第一个粒子系统

通常来说,我们最后还是会直接使用现成的粒子系统,复制几份,简单地修改一下来满足作图的需要。然而,创建一个新的粒子系统并不难,只要点击“创建”按钮,再设置它的名称就完事了。


这样一来,你就创建了一个空空如也的粒子系统。没有包含任何组件的话,它什么事情也做不了。以下将要介绍的是一些必须添加的基础组件。下面我们开工吧。

  1. 添加一个 Animated Sprite Renderer(动态精灵贴图渲染器) 以实现渲染功能。
  2. 我们需要确定粒子创建的位置,因此找到初始化器,并添加一个 Position Within Sphere Initializer(球体初始化器)。将它们的初始最小、最大速度都设置为64。如果没有设置初始位置,粒子将无法正确地生成。
  3. 添加一个Continuous Emitter(连续发射器)。现在你应该会得到一个白色的球体。实际上,现在你正在发射巨量的拥有初始纹理的粒子。
  4. 我们会注意到,即使你赋予了它们一个初始速度,这些粒子并没有移动。同时巨量的粒子还在堆叠中。这是因为没有操作器来控制它们。因此,即便我们设置了它们的初始位置和速度,在之后的状态更新中它还是什么也做不了,除了渲染这些粒子。因此我们需要两个操作器:
    1. 一个是 Movement Basic(基础运动) 操作器。它允许粒子运动。
    2. 另一个是 Lifespan Decay(结束生命周期) 操作器。它能够在粒子的持续时间结束时移除它们。
  5. 现在你的粒子应该会运动并在一秒后消失。粒子的持续时间默认条件下为一秒。
  6. 粒子的持续时间是在生成时初始化的,因此如果要修改它们的持续时间,我们需要添加 Lifetime Random(随机持续时间) 始化器。几乎所有的初始化器都允许设置随机范围的数值。因此如果我们希望每个粒子出现2到4秒时间,分别设置该初始化器的最小、最大值为2和4就行了。
  7. 接下来修改粒子的纹理,让它不再是白色球体。找到基础属性,点击material(材质)打开材质浏览器,并选择一个合适的粒子材质。
  8. 最后,添加一个 Fade Out Random 操作器。它的作用是让粒子在其持续时间的某个时间范围内渐变消失,默认是粒子最后25%的持续时间中。需要注意的是,由于每个粒子的持续时间在2到4秒不等,而粒子渐隐的时间是按比例的后25%,因此其时间也是不固定的。(也就是最后的0.5到1秒时间不等)

性能

最大粒子数量
默认情况下,每个粒子系统支持的最大粒子数是1000(尽管它的说明写的是最大1004个)。不论使用与否,游戏都会为它分配这么大的内存。因此在创建粒子系统后,最好检查一下粒子计数器,看看你使用了多少粒子,然后把最大粒子数设置成这个数。这样做可以避免你的粒子系统占用了丧心病狂的内存大小。
多线程
粒子系统会多线程地处理粒子。因此如果有多个粒子系统,它们会被尽可能多地分配到不同的线程中。因此表面上来看,把一个复杂的粒子系统拆分成多个系统能提高整体性能。尽管如此,每增加一个系统都会增加一份开销,因此,拆分粒子系统所获得的性能提升较为有限。尤其是对于简单的系统而言,拆分一个只有为数不多粒子的粒子系统,基本可以确定是负优化行为。不过,如果你的粒子系统包含了上千个粒子,不妨试试将它拆分成几个,以利用多线程提升性能。对于同一个粒子系统来说,它自身与它嵌套的子系统只能运行在同一个线程上,这样方便它们相互传递数据。
单指令多数据流(SIMD)
目前,大多数粒子操作器、初始化器等等,都是以单指令多数据流(SIMD)方式工作的。意思是在现有硬件上,它们实际上在同时对一组4个粒子数据进行数学运算。理论上在未来的不同类型的硬件上,可以支持更多数量粒子的同时运算。重点在于,如果你保证粒子数是4的倍数,就能很好地利用这个机制。将粒子系统的数量削减到最近的4的倍数,就能获得少量的性能提升。尽管只有一星半点的提升,但知道这件事没什么坏处。
过度绘制
过渡绘制是同一个像素点被多次反复绘制造成的,原因是有许多图层,而显卡需要一层一层绘制图像。粒子通常是过度绘制的罪魁祸首,并且很容易就陷入最坏的情况,即大量精灵贴图互相堆叠,占满了整个屏幕。粒子系统自身提供了一些对抗过度绘制的方法。
最基础的做法就是遵循通用的解决方法,例如使用数量更少、尺寸更小、不透明的粒子,而不是许多半透明的粒子。然而,考虑到粒子系统的运用场景,这种做法不太现实。
下面介绍一些有帮助的 .vmt 材质设置参数。其中一对属性是 $minfadesize 和 $maxfadesize(字面意思是最小消失尺寸和最大消失尺寸)。这些属性会让粒子根据自身屏幕占比的大小逐渐消失。例如某个材质的 $minfadesize 为 0.25,$maxfadesize 为 0.5,它将会在占据 25% 屏幕大小时开始消失,一旦占据 50% 屏幕大小时完全消失(并不再渲染)。在步行穿过粒子特效实现的烟雾时,上述参数的设置与否,足以决定你的机器是上帧率天堂还是下过度绘制地狱。
另一个有帮助的参数是 $maxsize(字面意思是最大尺寸)。它决定了单个粒子在屏幕空间上绘制的最大尺寸。粒子最大只能保持在这个尺寸,无法超出。例如,你需要血液来实现命中反馈效果时,显然不希望它过近时消失(使用$maxfadesize参数)。但你可以设置它的最大屏幕占比,以此减少潜在的过度绘制问题。
顺带一提,$maxsize 还有一个与之相反的参数 $minsize。它与性能提升关系不大,主要是视觉上的考虑。它可以限制粒子在屏幕上的最小尺寸,不会比该值更小。还是以出血的命中反馈来举例,我们希望它在屏幕上的显示不会小到看不见,这样在命中远距离的敌人时依旧能获得视觉反馈。设置 $minsize 可以达到这一目的
上述两个参数也可以用于实现其它有意思的效果。例如,在使用尘埃粒子时我们会将它的尺寸限制在一个非常小的范围。这样一来,在远处你看到的是空气中漂浮的像素级大小闪烁的尘埃颗粒,而凑近看它们也不会变得跟网球一样大,产生糟糕的视觉效果。
深度混合(depth blending)可以在粒子表面互相重叠时依旧细致且平滑。然而,它同时也是填充率(和过度绘制正相关的一个性能指标)杀手。因此如果有需要,可以在 .vmt 中设置 $depthblend 0 来提升性能。
除此之外,对于动态纹理来说一个有用的 .vmt 参数是 $blendframes 0/1(字面意思是混合帧)。默认情况下动态纹理会将两个动画帧混合插帧来提高动画帧率。然而,这样也会提升场景的绘制次数。禁用帧混合会降低动画的流畅度,但能提高性能。
数据共享
有多种方式在主系统和子系统之间共享数据。在一些复杂的初始化器和操作器中,我们可以将某些通用参数、运算结果保存到挂点上,子系统就可以直接访问并使用,而不用重复计算。
碰撞
通常来说,碰撞计算是个开销极大的操作。默认的模式0会在每一帧都检查一遍所有粒子。这样做代价很高,也限制了粒子数量。然而,Collision(碰撞) 约束允许使用其它的碰撞模式,用碰撞精度换取运行速度。Collision Mode 3 对于难以预测粒子位置、运动的粒子系统来说,是精度与性能之间最佳平衡点。

制作

制作好的粒子必须被添加到 GAME\particles\particles_manifest.txt 文件中。关于制作 DX8 图形接口的备选粒子系统,文件名中必须附加 "_dx80",例如 “rockettrail.pcf”就要改成 “rockettrail_dx80.pcf”。在最高仅支持 DirectX 8 的机器上,将会加载该文件作为替代。不过这样做的必要性不大,毕竟绝大多数人的机器都支持 DX9 或更高。

另见

[[Category::Category:Particle System/zh]]