使用Visual Effect Graph提升FPS示例项目的视觉效果
在去年的Unite LA上,我们正式发布了FPS示例项目和Visual Effect Graph,FPS示例项目旨在向开发者展示如何开发第一人称射击游戏。
FPS项目第一个版本的视觉效果使用Unity传统粒子系统制作,此后我们使用了全新的Visual Effect Graph工具,升级视觉效果,从而发挥出高清晰渲染管线HDRP的优势。
本文将分享我们升级FPS示例项目视觉效果过程中所学习到的经验。
资源准备
安装Visual Effect Graph
我们可以通过Unity 2018.3或更高版本编辑器中的资源包管理器安装Visual Effect Graph。
点击Unity编辑器菜单Window > Package Manager,点开Advanced并选中“Show preview packages”,然后找到Visual Effect Graph,单击Install按钮即可。
FPS项目
如果你要获取FPS项目,首先确保安装使用Unity最新版本,然后从Github库下载FPS示例项目,并在项目自述文档找到使用指南。
下载FPS示例项目:
https://github.com/Unity-Technologies/FPSSample
如何转换系统
Visual Effect Graph提供了从模板运行效果的简单组件,它和粒子系统的解决方案不同,粒子系统会保存所有效果的数据到场景组件。Visual Effect Graph则将多个组件保存到单个效果视图中,这样Unity仅使用一个组件就能够渲染多个粒子系统。
由于我们让C#接口更容易处理粒子系统的使用,因此它们的行为非常相似,所以从一种系统更新为另一种系统的方法很简单。
对于FPS示例项目,转换效果到新的系统很简单:
在C#代码只有少量对粒子系统的引用。
使用了几乎完全相同的C# Component API。
游戏代码结构很简单,方便进行额外的优化。
我们首先在预制件和代码中找到所有粒子系统的引用,然后替换所有实例为占位符。所有游戏效果都保存在Assets/Effects下的专用项目文件夹内,过程非常简单。
使用层级窗口的筛选器可轻松找到场景中的其它效果,大多数效果保存在预制件中,因此把它们替换为占位符也很简单。替换后,我们便可以自由地改进和迭代效果。
优化射击系统
射击系统依靠很多效果实现,这些效果的性能取决于游戏节奏、玩家数量、游戏区域拓扑和当前游戏状态。这意味着游戏会生成很多对象,例如中枪时的碰撞效果,而且会让场景视图充满大量Draw Calls。
FPS的实现情况使用了64个碰撞效果组成的对象池制作,它由ECS实体组件系统来管理,并且使用了回收系统,每次碰撞会使用1个粒子系统和1个音频源。
该系统的主要缺点是:碰撞效果的每个实例都会对每个系统产生一次Draw Call。如果限制每个效果仅使用少量Draw Calls,最坏的情况是每种类型的碰撞会产生N乘64次Draw Calls,N代表组成每次碰撞效果的粒子系统数量。
共享池系统的实现
我们想到的改进方法是对所有武器类型的碰撞效果使用通用的模拟效果,这样所有渲染过程都会在相同模拟效果中执行。
我们考虑了把所有碰撞系统归为一类的缺点,而不是对每种碰撞系统单独分类,这样会让我们:
对每个系统渲染更多粒子,这对GPU模拟过程来说更高效。
使用粒子的更多系统(图层),从而实现更丰富的效果。
它仍会计算与摄像机之间的距离,因此我们可以使用LOD。
为此,我们使用VFX SendEvent() API和VFXEventAttribute Payload载荷来放置碰撞源和碰撞法线。
碰撞定义仅引用一个资源模板,场景关卡管理器负责处理主要模拟对象和发送事件:进入VFXSystems。
VFXSystem是我们为FPS示例项目开发的顶层类,它负责处理所有混合效果,具体行为如下:
玩家会触发需要由VFXSystem处理的混合效果。
VFXSystem会获得该效果的相关属性,例如碰撞效果的位置和法线。
VFXSystem会获取效果的模板。
如果模板还没有生成,我们会生成模板。
我们会发送事件到该系统,它带有相应的属性载荷,例如碰撞位置和法线。
关卡变化时,我们会重置混合效果。
虽然自定义混合系统对性能有提升效果,但也存在缺点:
使用事件属性设计的效果需要使用C# API触发,因为它带有属性,这样的处理方法不适合预览和制作。
需要使用大型边界框来自定义效果。在FPS示例项目中,我们使用静态方法来设置它,使它包围整个关卡。因此,我们可以用C#代码管理这个边界框,使它随着实际碰撞效果而增大和缩小。
效果的LOD会在生成时计算,因此远处的碰撞效果会随时被简化,该行为是针对性能特别设置的。
VFX Event Tester
VFX Event Tester可以处理混合效果,由于需要使用事件属性来启动某些效果,因此我们使用C#代码来保存这些属性。事件属性包括:碰撞位置、法线、子弹击中扫描源和目标位置。
首先,我们的解决方案是使用一个临时的Timeline和VFX Activation Tracks激活轨道,该轨道会发送参数化效果来预览效果。但缺点是需要在场景保存一个预制件以用于编辑,然后将其移除,所以这并不是理想的解决方案。
然后,我们开发了一个名为VFX Event Tester的SceneView工具窗口,它可以发送事件和属性载荷到当前选择的资源。
我们可以点击Edit > Visual Effects > Event Tester菜单来启用和禁用该工具,工具的源代码可以在以下位置查看:
Assets/VFX/VisualEffectGraph-Extras/Editor/Utility/VFXEventTester/VFXEventTesterWindow.cs
自定义粒子LOD系统
对于所有碰撞效果,没有必要随时生成所有效果组件,特别是很小的粒子效果。
为了处理和深度有关的渲染过程,我们实现了一个基础实用的粒子筛选系统Cancel By Distance。它会在某些条件下,无效化粒子的生成过程。
使用这个简单的LOD系统有助于避免生成几乎不可见的粒子,它也可用于阻止远处效果的生成过程,例如远处钻机效果中的石块。
该自定义Spawn属性块的源代码的位置在:
Assets/VFX/Script/CustomSpawners/CancelByDistance.cs
VFX Volume Mixer
在短时间的帧内配置效果很麻烦,因此我们决定在玩家摄像机周围使用一个效果,并使用SRP Volume系统来根据玩家位置混合周围的效果。
Volume Mixer体积混合器利用了SRP Core的Volume功能,这些类可以使用保存在体积系统的参数,例如渲染参数和后期处理,根据摄像机在关卡的位置来重写设置。
该系统的源代码位置在:
Assets/VFX/Script/VFXVolumeMixer
使用该系统是为了配置符合玩家摄像机情况的环境效果,它会在室外和熔炉室产生炎热效果,在室内和洞穴中产生烟尘效果。
为了使用该系统,我们需要在项目设置修改很多数值,该系统能控制多达8个浮点值,8个向量和8个颜色值。我们通过设置选择使用的变量数量,并为这些变量命名。
然后,我们添加VFX Volume Mixer的Volume组件到场景体积上,它会提示数值,让我们可以在关卡中对数值进行局部重写,这是实现局部重写很方便的一种方法。
最后,如果我们想从这些体积采样数值,可以使用VFX Volume Mixer的Parameter Binder脚本。
Level_01中的其它主要效果
FPS示例项目会显示Level_01中使用的一些粒子系统,它们不属于通用系统。下面是我们解决的一些情况。
捕捉点圆圈
捕捉点圆圈是很有趣的效果,因为它要求粒子遵循具有16个控制点的蒙皮管道组成的变形路径。
由于Visual Effect Graph目前无法处理蒙皮网格上的生成效果,我们仍有骨骼链的信息。为了对该效果实现平滑的3D插补过程,我们使用了Sample Bezier算子。
为了在多个位置组成的路径上生成粒子,最简单的方法是烘焙位置列表到位置属性图,然后采样纹理的像素,决定粒子需要生成的位置。
我们编写了一个名为Multiple Position Binder的Parameter Binder脚本,它会获取游戏对象,把每个游戏对象的世界空间位置写入到纹理中,然后设置在特定视觉效果视图中暴露的点数量和纹理参数。
采样位置图的方法很简单,我们考虑使用2个像素的分组作为4个贝塞尔点,贝塞尔点通过计算2个中间贝塞尔切线得到,然后插补所有内容,使所有粒子都会使用贝塞尔混合通过所有位置。
该效果位于Small_emitter游戏对象中,你可以在预制件查看该示例,预制件位于:
Assets/Prefabs/Gameplay/Capturepoint_A
Multiple Position Binder脚本源代码的位置在:
Assets/VFX/Script/ParameterBinders/VFXMultiplePositionParameterBinder.cs
岩石研磨机
岩石研磨机使用在峡谷终点的简单装饰性效果,它用于突显磨钻机的能力。
游戏的峡谷场景中,钻头被卡住了,它正在研磨左侧的岩层,而另一个钻头正在研磨用作捕捉点的Terraformer。
灰尘和管道蒸汽
在关卡中,有很多光源需要使用简单的蒸汽,烟尘和空气流效果进行装饰。虽然大多数效果是静态的光源,但也有部分是动态的,例如下图的旋转管道。
为了解决这种情况,我们特别使用了受光粒子,从而同步显示蒸汽和体积光束。为了保持合适的帧率,这些粒子需要在摄像机远处淡化,因为它们的着色器是根据像素受光,对整个屏幕渲染会消耗过多性能资源。
其它效果位于整个关卡中,我们把这些效果设置为非自发光粒子,以便节省性能。
大型垂直管道也可以接受光线,但它们的大小会对性能产生较多问题。光束也不够锐利,因此我们最后使用了简单的无光效果。所有实例都在场景中通过设置淡出距离和颜色进行直接配置。
熔炉
熔炉特效会在圆形的范围生成气泡。该效果使用了实验性GPU Events功能,用于生成气泡破裂的Flipbook效果,在每次气泡破裂时发出闪光。它使用非局部闪光,蒸汽和热量效果来进行装饰。
玩家可以近距离观察该效果,我们使用了Camera Fade属性块处理蒸汽和热量,从而缓和Overdraw的情况,保持可控帧率。
结语
将FPS示例项目的视觉效果转换为使用Visual Effect Graph实现的过程比我们预期的更顺利。Visual Effect Graph资源包仍处于预览阶段,它的系统拥有一些灵活性,可以根据项目需求自定义体验效果。
FPS示例项目的结构非常简洁,使用很少工作区和自定义实现,因此非常适合测试应用Visual Effect Graph。
但我们仍然知道,在更复杂的项目中,情况会完全不同且更难处理。但无论如何,这次工作让我们了解正式制作的具体情况,便于改进Visual Effect Graph的后续版本。
本文的部分功能是特别针对集成的需求开发的,我们会把它们视为参考内容,在未来开发出更通用的工具,敬请期待。
更多Unity精彩技术经验分享和谈论,尽在Unity Connect平台(Connect.unity.com)。
推荐阅读
Unite Shanghai 2019
5月10日-12日上海,Unite大会强势回归。技术门票正在热销中,购票即获指定Asset Store资源商店精品21款资源的5折优惠券。
购票请访问:Unite2019.csdn.net
点击“阅读原文”访问Unity Connect