编者按
本文来自 GDC 官方 Youtube 频道,原内容形式为视频演讲,indienova 对其进行了编译整理,以图文形式分享。原链接见文末。
- 演讲人:诺兰·卡纳汉(Nolan Carnahan)
- 译者:dxmi
正文
大家好,我是诺兰·卡纳汉。我在开发了《凯娜:精神之桥》(Kena: Bridge of Spirits,下称《凯娜》)的 Ember Lab 工作室担任引擎和图形程序设计师。在本次演讲中,我将介绍用以实现游戏中死亡区域(Deadzone,下称“死区”)净化以及腐灵(Rot) 技能的部分视觉特效(VFX)和技术美术(Tech Art)工作。
先介绍一下我们的背景,Ember Lab 是一个半路转行做游戏的小规模动画工作室,以前一直在为商业电影与独立短片制作视觉特效,曾经在网上爆火的同人短片《恐怖命运:梅祖拉的假面》 (Majora's Mask - Terrible Fate) 就是我们的作品,《凯娜》是我们制作的第一款大型游戏。
我是《凯娜》开发团队的一员,我即将展示的内容中有许多都与我当时做的工作有关,但我必须要强调,接下来展示的一切都是艺术、程序和设计团队共同努力的结果。
简单介绍一下这部作品,《凯娜》是一款第三人称动作冒险游戏,玩家将操控灵魂向导凯娜与她圆滚滚毛茸茸的灵魂伙伴“腐灵”一起展开冒险,你将遭遇腐化大地的死区,因生前悲剧而备受折磨的灵魂就徘徊在这些区域之中。在这个世界中,腐灵可以通过净化死区来恢复世界的平衡,是生死轮回中关键的一部分。
凯娜可以通过操控腐灵来摧毁腐化的源头——死区之心(Dead Zone Hearts),以此修复被死区腐化的环境,这一过程被称为死区净化。本次演讲将分享我们在开发过程中为死区净化所制定的目标,展示一些早期原型,以及逐层分析为了实现视觉以及叙事目标而用到的视觉特效和技术美术工作,最后分享我们在此过程中总结出的经验教训。
目标
首先是目标。我们从一开始就希望死区能令玩家感受到威胁,并与其它和谐的自然环境形成鲜明对比。死区不仅应该遍布荆棘、肿瘤和浓雾,这类区域恢复健康的过程还要足够明显,环境变化也应该尽可能地实时发生。在一些原型设计中,大部分环境变化都发生在转场动画之后,这会导致其与玩家的行动略显脱节。如果能将之与游戏玩法更紧密地联系起来,整个过程都将变得更加有趣,玩家也会乐此不疲。
由于腐灵是对抗死区的关键要素,我们希望游戏中的视觉特效反馈也可以增强其在故事中的地位。随着凯娜解锁了各种腐灵能力和特殊技能,我们希望这些技能在净化死区的过程中也能带来一些小规模场景反馈。
原型展示
上图是一个早期版本的死区,其面积更大,同时还充满了足以在物理上阻碍玩家前进的巨大腐化团块。但是出于艺术和设计考量,这一版本很快就被否决了。
在这个版本中,玩家可以通过投掷诱饵来引导腐灵,或是像正式版本中的腐灵云一样直接控制腐灵。但这一版本显然有些过于粗糙了,摧毁巨大腐化团块的过程毫无正反馈可言。
这个后期原型使用了一个渲染目标来遮蔽被腐灵净化过的区域,相关材质会直接对其采样。这种对地形材质的清理方法比之前要成功多了,也与最终成品相似。但是这些巨大的‘死区柱’将其顶点压入地形的视觉效果可能会让正在穿过这些网格的玩家感觉非常奇怪。
除开奇怪的视觉效果,这一版本还有很多问题。这些网格需要极高的多边形数量来支持其被净化时的变形效果,之后重新弹出的草地更是加剧了这一问题。处理碰撞(Collision)也是一项非常有挑战性的工作,因为我们不想根据模型被清理的部分动态调整碰撞效果。作为替代,我们只好禁用碰撞,并让直接进入的玩家遭受伤害,但实际效果也不太好。由于碰撞问题也给导航网格生成等方面造成了麻烦,我们最终放弃了该方案,以确保这些区域的战斗能够顺利进行。
上图展示了一个尚不能实时完成的死区净化过程。你或许已经注意到了,这些蓝色和紫色的花是在镜头转场之后才出现的。这朵在过场动画中长出来的小花意在传达万物重新复苏的感觉,但还是无法与正式版本中的环境变化相提并论。
死区构建
接下来,我将介绍我们是如何在引擎中构建最终版本的死区的。它大致分为四层,每一层都有独特的问题以及对应的特殊解决方案。首先是地形,地面的基础层,之后是植被,我将介绍如何在净化地形的基础上构建它们,接下来,我将介绍针对大型死区网格所做的改进,最后介绍为死区设计的光效和氛围系统,以及如何在死区被净化之后关闭它们。
底层地形
我们的地形材质支持在底层材质上绘制两层附加层,在虚幻引擎中,这需要在地形图层中勾选‘无权重混合’复选框(No Weight Blend Checkbox)。第一层是死亡遮罩(Dead Mask),只是用来让基底材质看起来充满死亡气息。第二层是能够对玩家造成伤害的死区变种,会减缓玩家的速度,且玩家在上面站太久可能会死亡。死亡蒙版遮罩在调整基础颜色的同时还会降低其饱和度,并通过修改其粗糙度来使其看着更加光滑。会造成伤害的死区变种在基底材质上额外加了一层平铺材质,以使两者的边界更加明显,这一层同样修改了物理材质,方便检测玩家是否进入了该区域。
为了实现净化效果,我们在以玩家为中心的位置设置了一个自上而下进行渲染的渲染目标。此处材质的红色通道编码了净化状态。举例来说,当玩家摧毁了一个死区之心,一个圆形结构便会由内向外扩展,并标记出已被净化的区域。这一材质使用了像素世界空间位置(Pixels World Space Position)以计算从纹理中采样的正确位置,采样的值被用来淡化附着在地形材质上的两个死区附加层。
由于玩家的视野很广阔,我们设置了两个渲染目标,类似级联阴影贴图(Cascaded Shadow Maps)。高分辨率的渲染目标覆盖玩家周边的区域,低分辨率的目标覆盖更广大的区域,以便玩家看到远处的死区净化过程。就像在级联阴影贴图中一样,渲染目标也可以被最近的纹理单元(Texel)捕捉,以保证玩家在短距离移动时不会遇到奇怪的采样失真现象。
但这样的方法也有一些限制,其中最明显的限制之一就是目标渲染是自上而下的,因此我们无法让多个死区叠加在一起,但这也并没有给实际的开发工作带来问题。此外,这种方案只能用于单向度的死区净化过程,因此,虽然我们觉得动态变化的死区可能会带来有趣的玩法,但从未做过相关尝试。
植被系统
接下来讨论地面覆盖网格,其中包括增加了地形深度的草地,三叶草和花朵,以及蕨类等其它大型植物。我们使用了虚幻引擎自带的内置实例植被系统(Built-in instance foliage system)来放置和渲染这些资产。对于需要在死区中使用的资产,我们为每种植物设置了两种植被类型,一种为净化版本,一种为死区版本,在装饰关卡时需要使用适当版本。所以当艺术家们制作死区场景时,他们需要确保使用了死区版本的植被资产。
很明显,我们需要两套材质,死区植被也因为类型的不同需要多种材质。同时使用两种植被类型和材质确实带来了一些小问题,例如如何在改变材质参数时保持二者同步。但总的来说,我认为这种处理方式是正确的。我们曾尝试使用能够检测自己是否在死区的单一植被类型,但这带来了如何检测网格下方地形图层的额外复杂任务,这可不是能简单解决的。此外,即使多数网格都不会在净化过程中被改变,它还是提升了网格的着色器复杂度。因此,考虑到目标中的资产密度,我们不觉得单一植被类型值得采用。
因此,这些植被类型的净化方式大体上和地面一样,即通过对之前提到的渲染目标和红色通道采样,并使用采样值来调整颜色和其它材质参数。你可能也注意到了,一些死区植被在净化后会缩小并直接消失,这是通过在顶点着色器(Vertex Shader)中缩放网格来处理的,它也基于从纹理采样的相同值。
这留下了一个问题:如果移除了大量死区植被,其中有些区域可能会直接露出光秃秃的地形,我们通过让其它植被重新生长并填补这些空地来解决这一问题。某些材质最初的缩放比例确实为零,并且基于蓝色通道而非红色通道的采样值增长。我们将其分为两个通道的一大原因就是希望花和其它植物的生长相对于死区的被净化过程有一定延迟。因此,将其划分为两个通道可以让它们成为两个独立事件,并且使二者看上去略微有些时间差。
从艺术总监的角度来看,这样的处理方式非常成功,因为它允许艺术家们用生机勃勃的植被和花朵装点空间,即使调整了饱和度,这些植被也与死区区别明显。所以每当死区被净化,植被就会快速生长以使整个空间变得生机勃勃。我们无需再在关卡中加载资产,从而令上述一切得以在游戏中实时发生。
在顶点着色器中缩放植被也有一个明显的缺点,那就是缩小的死区植物和重新生长的植物可能会在某些区域同时出现。为了优化这一点,理论上可以编写一些代码修改实例植被系统,比如尝试零缩放的实例(Zero Scale Instances)。但我们没有时间进行任何重大更改,只能确保这种效果没有导致过于明显的问题。
任何零缩放的实例生成的三角形面积都是零,因此它们至少在栅格化过程中会被剔除。最后,我们对覆盖地面的素材进行了一些视觉润色,内部称之为过度生长。我们给净化渲染目标的绿色通道编码了一个额外的值,用它来临时提高颜色饱和度并让植物长得比平时更大。当摧毁死区之心或以腐灵之云形态在草地上移动腐灵时,你都可以看到这种效果。这种效果在死区净化过程中提供了一些额外的动态感,令一切看起来更加生动。
最后,你可以在死区净化过程中看到所有这些植被着色器被结合在一起。与调试渲染目标的视图相比,此处展示的过程要清晰得多。但因为蓝色通道比红色通道稍微延迟,你可能会注意到在所有草变绿之后花朵才只长出来一点。此外,绿色过度生长通道是唯一在净化后不保留的通道,其效果只是临时的。
大型网格
接下来是空间中较大的死区网格,包括球状的死区荚囊和树上那些较大的死区增生组织,它们非常适合用于阻挡玩家,并有助于传达死区正在蔓延的感觉。每当死区被净化时,这些网格就会消散(Dissolve),为玩家腾出道路。这些网格实际上是蓝图(Blueprints),而不是植被实例。这样做的一个主要原因是我们不想使用之前提过的净化渲染目标,因为这会带来与之前的原型相同的问题,即净化发生时玩家可能正在穿过这些网格。
另一个问题是在死区净化过程中,净化速度太快,以至于一瞬间就覆盖了这些较大的资产,这样的效果也不好。所以我们让这些资产按照自己的时间轴消散,通过给它们更多时间,玩家得以有机会感受到这一切。与原型版本不同的是,这些网格只有两种状态,已被净化或还没被净化。如此一来,这些网格的碰撞可以轻松地在净化时被禁用。为了使玩家感觉消散与死区之心被摧毁有所关联,我们制作了一个工具来调整每个网格开始消散的时间。因此,当死区净化效果扩散时,这些网格并不会在净化效果到达它们时就开始消散,而是会在净化范围实际蔓延到它们时才开始消散。这让它们有了足够的消失间隔,会根据自己的时间轴逐一消散,最终的呈现效果也得以保证。
消散着色器采用了典型的基于噪音的消散效果,它通过增亮边缘来掩盖过渡。许多资产还会生成粒子效果,比如小水花、断根和一点扭曲以帮助掩盖过渡内容。树木之类的资产也会在两种漫反射纹理之间切换,以提供更多纹理变化。如果仔细看,你会发现树背后有一些苔藓生长。需要注意的是,对于像树木这样在清理后仍然存在的少数资产,我们并没有禁用其碰撞效果。
像大多数与蓝图相邻的东西一样,我们在这些资产的初始设置中遇到了一些性能问题。最初我们使用动态材质实例来设置这些蓝图,以便训练实例的消散参数,但后来我们放弃了这种做法,因为它会破坏实例。在 4.22 版本后,虚幻引擎会自动将共享相同网格和材质的绘制调用实例化,这可以显著减少它们向 GPU 提交的绘制调用数量。但由于动态材质实例可能具有不同的着色器参数值,它们需要作为单独的绘制调用进行渲染。
另一个问题是,更新大量材质参数对 CPU 来说成本高昂。更新材质参数的默认接口会按名称查找参数,这涉及大量字符串比较。在关卡流加载期间尤其糟糕,因为我们需要更新分布在数百个死区蓝图上的少数参数。在一些关卡上,我们花费了大约 30 毫秒的时间,仅仅是为了创建动态材质实例并更新这些资产上的所有参数,以确保它们能够在关卡加载时进入正确状态。
为了解决这些问题,我们转而使用了虚幻引擎中一项名为自定义基元数据(Custom Primitive Data)的功能,它允许我们访问每个基元级别上的 32 个浮点数,可以像材质参数一样进行修改。但是它不需要手动创建动态材质实例,只需要在基元级别设置这些值。
如上图所示,在着色器中,我们不再通过参数名称来读取参数值,而是直接索引到该浮点数数组。因此,我们只需指定想使用的索引编号。由于许多着色器参数实际上来自材质函数而非材质本身,我们建立了一个约定,以便为特定效果预留特定的索引,从而避免在将不同的材质函数随机放入不同材质时出现冲突。
例如,索引 0 可能始终被预留来存储消散量,索引 1 可能始终用于存储褪色值,诸如此类。由于不再需要动态材质实例,渲染器便能够实例化所有死区网格。另一个好处是,由于参数是通过索引而不是名称引用的,因此在 CPU 端更新这些值变得更加容易,因为我们避免了字符串比较。但自定义基元数据也有一些缺点,其中一个大问题是它会同时影响到每个基元的设置,这意味着如果在同一个网格上有多个材质,它们的着色器参数将无法被设定为不同的值。所以,每当遇到这种情况,我们只能回到为这些网格使用动态材质实例的老办法上。
光照氛围
死区的最后一个关键组成部分是光照和氛围。《凯娜》中的所有光照都是实时的,这使我们能够精细调整包括死区在内的每个区域的外观。光照艺术家通过使用自定义的后处理体积(Post-Process Volumes)来控制不同区域的光照效果。我们的自定义体积和虚幻引擎内置的后处理体积类似,可以根据摄像机位置实时调整光照和雾气参数,并随着摄像机的接近逐渐改变相应数值。艺术家可以修改各种属性,比如阳光或天空光照强度、雾气密度,以及在光线和雾气组件上曝光的其它深度参数。籍此,他们得以精细调整整体空间的视觉效果。
与后处理体积类似,所有参数覆盖都是可选的,因此体积只需要为它想要覆盖的东西指定一个值。对于死区,我们通常只是降低太阳强度,增加雾气密度,再加入一点色差。所有这些参数并不存储在体积自身中,而是在数据资产配置文件中。在编辑器中更换配置文件非常容易,而且在设置空间的照明时,先找到一个与需求近似的配置是一个好方法。将光照配置文件独立出来的好处是,它避免了很多源代码控制的冲突,从而避免了许多必须检查源代码才能解决的问题。
因此,对于每个死区,我们都设置了一个后处理体积,用以控制死区的光影效果。在死区净化的过程中,我们会逐渐消除后处理体积的影响,以避免光影效果突然改变。艺术家还可以通过在编辑器中切换两种状态来预览体积的影响,这样就可以在设置光照时轻松比对死区净化前后的变化。下图展示了之前提到的四种层级被组合在一起的最终效果。从光秃秃的地形开始,逐渐加入植被和场景中的其它元素,再加入一旦被净化就会逐渐淡出的死区网格,最后加入会在净化开始时就立刻变化的光照与雾气,整个场景就组合完成了。
腐灵能力
现在讨论我们如何在游戏中实现腐灵的特殊能力。我们有两个主要目标,首先是希望展现腐灵的集群特性及其在净化死区中的作用。虽然腐灵的动画效果通常是由骨架网格体(Skeletal Mesh)编辑,但不同能力的情况也会有差别,例如快速变形时的 Alembic 几何缓存(Alembic Geometry Caches),以及聚集在一起时的尼亚加拉(Niagara)粒子。
几何缓存
首先,几何缓存用于难以同时为复数腐灵制作动画的情况。为了创建几何缓存,我们让低多边形数的腐灵网格跟随动画曲线(Animation Curves)运动。通常只需要几个基本曲线,稍作调整即可为其它腐灵创造不同的路径变化。这确保了腐灵在聚成一团时能够产生视觉上的凝聚力。随着速度增加,网格也会在确保其整体体积不变的前提下进行拉伸。你可以在腐灵箭矢,腐灵炸弹或是腐灵之锤技能的起手动作中看到这些效果。对于集群行为,我们使用了尼亚加拉系统,其中每个腐灵都由一个粒子表示。通常情况下,骨架网格体版本的腐灵会消耗大量资源,因此我们会根据不同平台的机能限制减少屏幕上可见的腐灵数量。
粒子系统
然而,我们希望玩家能在腐灵之云形态中看到他们收集到的所有腐灵。因此,我们选择在尼亚加拉粒子系统中使用静态网格(Static Meshes)来展现它们,以降低动画和渲染成本。尼亚加拉模拟程序在 CPU 上运行,以便我们轻松地使粒子位置与腐灵角色保持同步,这使我们可以相对顺畅地从粒子版本切换到骨架网格体版本。尼亚加拉模拟将腐灵按沿样条线(spline)分布。当绑定敌人时,样条线是通过添加遵循动画路径的点来动态生成的。下图展示的就是只有样条线的版本。
另一方面,当处于腐灵之云形态时,样条线实际上是跟随骨架网格体中的骨骼的。这确保了样条线长度一致,在腐灵之云静止时的分布状态也观感良好。运动的粒子被吸引到围绕这个样条线旋转的点上,单个粒子的吸引点半径和旋转角度具有一定随机性。这些设置也受曲线调节的控制,可以定义数值沿样条线长度变化的方式,比如在前端粒子密度更大,在尾端则较小,所有这些参数都由可调节的标量值控制。例如,当玩家收集了更多腐灵时,腐灵之云整体的半径也可以被放大。
这些尼亚加拉参数也可以随时间由动画曲线驱动,这使动画师可以调整模拟的行为,以更好地匹配动画。例如,如果腐灵之云的移动速度很快,吸引强度可能需要更高,以防止粒子滞后。为了实现这一点,我们只需使用一些蓝图代码按名称采样特定动画曲线,并将这些值传递到尼亚加拉系统中。进行动画处理时,我们将这些值记为标量而非绝对值,以免中断正在传递的任何其它全局变化,例如根据腐灵数量调整腐灵之云的半径。所以,技能“腐灵之锤”可以被分解为三个阶段,最初,腐灵以几何缓存形式成群结队旋转,当它们飞向目标时则转变为尼亚加拉粒子系统,当撞击地面并散落各处时又变回了骨架网格体形式。
环境反馈
如前所述,这些能力也与死区净化系统相关联,它们都会作用于净化渲染目标,以便同时影响地形和植被,但并不会影响到较大的死区网格。然而,并非所有净化过程都是以圆形扩散的,因此,渲染目标必须要能支持轨迹绘制。虽然这套系统最初是为了在腐灵之云移动时提供反馈而构建的,但它最终被用在了各种地方,对敌人的绑定,腐灵之锤的撞击,或者腐灵箭矢留下的净化路径。
我们遇到的一个问题是,净化渲染目标的渲染是自上而下的,所以并没有简单方法来编码特定高度值处的净化过程。因此,我们在开发早期遇到了一个问题:如果你向空中射箭,虽然箭飞得越来越高,但它给地面带来的净化效果却并没有随之逐渐减弱。于是,我们通过简单地追踪来搞清箭到底飞了多高,然后根据它与地面的距离调整净化强度,并最终反应在着色器上,产生了逐渐淡化的效果。综上所述,我们认为这些能力的动态反馈给玩家留下了这样的印象:每次在战斗中使用腐灵都是在对抗死区的腐化。
总结
希望本次演讲能让大家简单理解我们如何在《凯娜》中实现死区转换和腐灵能力。在此过程中,我们有两个重要收获,首先,在理想情况下,视觉特效应该被用于强化世界构建,而不仅仅是为了看起来很酷。我们在确立腐灵到底如何清理死区的问题上遇到了很多困难,但最终发现,良好的视觉特效反馈可以有效帮助玩家理解和接受游戏世界的运行机制。
其次,分层构建各种系统产生了巨大价值。虽然死区的每个层级单独来看都不复杂,但每一层都提供了与上一层不同的反馈,它们的共同运作最终产生了总体大于部分之和的效果。通过让不同层级在时间上分阶段生效,也避免了它们杂乱无章地混成一团的糟糕结果。
如果你们对演讲中的内容有任何疑惑,欢迎随时通过工作邮箱联系我,或是直接联系我们的工作室。感谢你们认真听完整场演讲,也感谢每一个与我一起完成了相关内容开发并帮助我准备这场演讲的同事。谢谢!
原链接:https://www.youtube.com/watch?v=bpFdfrg8SvY
*本内容系编译整理,不代表 indienova 立场。未经授权允许,请勿转载。
暂无关于此文章的评论。