《杀手 2*》:在现代 CPU 上增强混响效果

签署人: IDZSupport KS

已发布:07/26/2019   最后更新时间:07/15/2019

简介

六核和八核 CPU 的日益普及将给游戏提供更出色的处理能力,玩家的音频体验将迈上新台阶。为充分利用额外的内核,开发人员需积极实施代码执行并行化,确保在不同的 CPU 上适当地扩展游戏特性。本文将以 IO Interactive* 与英特尔的合作成果为基础,介绍我们如何使用多核 CPU 和并行化提升《杀手 2*》音效的沉浸感。具体来说,我们将介绍如何改进游戏音频中一个极为重要且计算成本高昂的要素 —— 混响。

关于《杀手 2*》

隐蔽类动作冒险视频游戏《杀手 2》由位于哥本哈根的 IO Interactive 开发,于 2018 年 11 月发行,是拥有全球数百万粉丝的《杀手™》系列游戏的续作。假设我们的玩家控制了标志性杀手 Agent 47,开始探索各种大型、刺激的关卡,设法顺利干掉处于保护之中的重要目标。

screencapture of Hitman2 001

无论是伪装成另一个游戏角色小心翼翼地接近目标,还是精心策划一场意外,或是靠真枪实弹完成任务,这一切都取决于玩家。游戏为玩家提供了充足的机会,玩家可以采取任何手段,也可以采用合作-对抗多人模式,在游戏中与其他玩家合作或对抗。《杀手 2*》展现的世界真实有趣,可通过定期更新添加新的地点、任务和内容,不断壮大。

screencapture of Hitman game

游戏在开发过程中以专有 Glacier 游戏引擎和 Audiokinetic Wwise* 作为音频中间件。因此,本文介绍的许多实施细节将仅限于 Wwise,但也可以轻松适应任何声音引擎。

简单的混响系统及缺点

声波被周围表面反射的现象叫做混响。声源停止发声后,声波要经过多次反射和吸收,持续一段时间。在视频游戏中,混响是增强沉浸感的重要工具,可从听觉上帮助玩家充分融入精彩的游戏动作世界。因为混响声音中包含无数次反射,因此精确模拟这种现象具有较高的计算成本;所以游戏开发商必须采取各种近似法。

常见的方法是将游戏空间划分为独立的区域,为每个区域分配一种混响预设。本文将这些区域称为“房间”,与 Glacier 和 Wwise 术语保持一致,即使它们代表的不一定是封闭空间。本文中的房间属于游戏关卡的一部分,具有类似的声学特性。因此,房间大致模拟关卡的物理结构,代表室内和室外环境,甚至是开放地形的一部分。在《杀手 2》中,它们通过 3D 形状进行定义,例如边界框、圆锥形和凸多边形,如图 1 所示。

example of sound geometry
图 1.关于声音几何体的简单示例。在本示例中,整个小屋由一个红色的声室表示。

混响预设是包含预定义设置的混响效果,可以进行微调来模拟特定房间的声效。所使用的声效可以是合成混响(如 Wwise RoomVerb),也可以是卷积混响。《杀手 2》主要使用后者。

合成声效使用延迟线和滤波器(如梳状滤波器和全通滤波器)通过算法模拟混响效果。这些声效使用的 CPU 资源较少,但听上去往往不如卷积混响自然和舒服,声学特点复杂、混响时间长的房间尤其如此。设置比较难,无法达到令人满意的效果。

相反,卷积混响使用预先录制的真实地点的脉冲响应。通过播放短音(如开枪)并录制房间内的声学响应,产生脉冲响应。最终的音频样本可通过卷积运算,应用于游戏音效,使其听起来像在该房间中产生的音效。卷积混响对 CPU 资源和内存的需求较大,还需要您购买、发现和记录脉冲响应,不仅成本高昂,而且非常耗时。然而,如果脉冲响应良好,最终的音效几乎不需要调整,也能重现复杂的声学环境,令人惊叹、令人信服。

游戏要不断确认音频听者在哪个房间,并将分配给该房间的混响预设应用到目前所有正在播放的 3D 音效上。听者通常定位在玩家控制的角色的头部或游戏摄像头上。这种方法易于实施且非常有效,因此许多游戏都采用这种方法,包括《杀手》系列。但这种方法非常简陋,而且存在几个严重缺点。

我们可以简单地举例说明其中一个缺点。假设您自己是一个游戏角色,躲在小小的混凝土碉堡内,击退从丘陵地带进攻的敌人。简单解决方案会将碉堡的混响应用到敌人的枪声和您的武器发出的声音上。敌人的枪声没有与开阔丘陵地带相关的混响效果,而包含一种金属撞击声,像是您自己从混凝土碉堡内发出的枪声。这就无法给玩家准确呈现环境中的声音,会让人感觉不自然。在现实生活中,战场上的声音首先会深度掺入来自山上的反射和回声,而来自玩家碉堡墙壁的反射对声音的影响相对会弱一些。

为了解决这个问题,每种声音的混响都应该模拟声音传播到听者时,来自表面的反射。同时,这种模拟需使用大量 CPU 资源。但我们可以弄清楚声音传入听者耳朵之前穿过了哪些房间,并将这些房间的混响预设链接到声音上,从而进行粗略的估计。在许多情况下,相比将听者所在房间的混响运用到所有声音上,即使只是将声音发射器所在的房间的混响运用到每个单独的声音上,也能提高空间逼真度。

请观看图 2 中的视频,了解不同的混响,以及如何通过不同的方法将这些混响运用到《杀手 2》中的声音上。

图 2.音频特性介绍:混响视频解释了本节中的要点。

使用声音传来或穿过的位置的混响会带来一些挑战:

  • 渲染混响效果的 CPU 成本
  • 检测房间
  • 链接混响
  • 混响的方向性
  • 在不同 CPU 上扩展

下面我们逐一介绍这些挑战。

渲染混响效果的 CPU 成本

混响效果往往需要消耗较多的 CPU 资源,它需要游戏最大限度地减少同时运行的声音实例数量。因此,混响效果通常不应用在单个声音上。相反,需要某种混响效果的声音将路由至辅助总线,辅助总线会对这些声音进行副路混合,并一次性运用所需的音效。声音在总线中进行副路混合时,只需按照一定音量(即 send value)调整声音,就可以控制每个声音的混响强度。这是大多数游戏的标准做法。在 Wwise,使用 SetGameObjectAuxSendValues API 在声音发射器上完成路由。

《杀手 2》有几十条代表典型环境的混响总线,如图 3 所示。音效设计师会为游戏中的每个房间分配一条总线。

reverb bus hierarchy in our Wwise* project
图 3. Wwise* 项目中的混响总线层次结构。

只有当总线处于活跃状态,也就是有播放声音路由至该总线时,总线上的音效才会进行渲染。没有播放声音的总线处于休眠状态,其音效通常不使用 CPU。唯一的例外是,所有路由至总线的声音已经结束,但总线仍处于活跃状态,以播放完音效(如混响)产生的尾音。

传统解决方案的 CPU 占用率

传统解决方案的 CPU 占用率不大。将所有声音路由至听者所在房间的总线,意味着我们只需渲染一种混响效果。如果听者站在房间门户附近(如门和其他入口),将会有多个总线同时激活。在这种情况下,声音不仅要路由至听者所在房间的总线,还要路由至门口对面的房间的总线。这样可确保听者从一个房间走到另一个房间时,混响转换平滑。即便如此,活跃总线数量也不会超过 2 条或 3 条,这样可以降低所用音效的 CPU 成本。

新解决方案的 CPU 占用率

如果我们将声音路由至它们所在房间的总线上,情况将完全不同。鉴于《杀手 2》中同时播放的声音最多(声音上限)为 64,如果每个声音都在自己的房间中,有自己独有的总线,那么理论上最终可能会有 64 条活跃的混响总线。尽管实际上不可能出现这种最糟糕的情况,但活跃混响总线数量仍然比传统解决方案多,平均每次为 8-10 条总线。例如,图 4 Wwise Advanced Profiler 截图显示了常见关卡中活跃混响总线的数量以及混入每条总线的声音数量(Mix Count 栏)。

active reverb busses
图 4. 活跃混响总线。请注意,即使 Mix Count 为零,也有一些总线处于活跃状态。它们正在播放已结束声音的混响尾音。

8-10 个混响似乎与传统解决方案的 2-3 个差别不大。然而,我们使用卷积混响,它的 CPU 成本较高,而且该成本会随着混响数量的增加而直线上升。因此,迁移至新系统会使总音频帧渲染时间增加超过 50%,取决于混响设置。如果硬件用完待播放声音数据之前,渲染没有完成,这可能会造成卡顿,至少会对帧速率 (fps) 带来不利影响。

实施音频渲染并行化

好消息是,混响总线彼此独立,它们的音效写入总线自己的副路混合缓冲区。因此,非常适合并行执行。在这方面,现代多核 CPU 可以发挥巨大作用,因为在不同的硬件线程上并发渲染混响效果会降低甚至完全抵消混响数量增加所带来的负面影响。

因为我们使用 Wwise,所以我们可以免费实施这种渲染。2018.1 版本之后的标准 Wwise 软件开发套件 (SDK) 包含英特尔工程师实施的基于任务的音频渲染。它将音频渲染管道分成多个可以并行执行的精细任务,并要求游戏执行这些任务。我们将任务提供给自己的任务调度程度,它负责在可用的工作线程上执行这些任务,如图 5 所示。使用 Wwise 但没有任务调度程序的游戏可受益于 SDK 提供的示例实施(见 TaskScheduler 示例)。该示例使用起来非常简单,而且可显著缩短音频帧渲染时间。

screenshot of the Glacier profiler shows parallelized rendering
图 5.Glacier 分析器截屏显示《杀手* 2》中典型音频帧的并行化渲染。“JqWorker”横线表示工作线程,红色和黄色条目表示在这些线程上执行的单个任务(在 Glacier 中称为 “jobs”)。该视图经过过滤,仅显示音频任务。

性能比较

我们来看看实施与未实施并行化音频渲染之间的性能区别。为进行性能对比,我们测量了图 6 中所示位置静止时的平均音频帧渲染时间。

the spot in the Miami level used for performance measurements
图 6.《杀手* 2》的迈阿密关卡中一处用于性能测量的地点。

在第一个测试案例中,我们启用旧的混响系统。测试位置位于门户附近,因此有两次有效混响。在第二个测试案例中,我们换成新解决方案,产生了 8 次有效混响。最后,我们将新系统和音质最佳,但速度最慢的最高质量混响预设相结合,看会发生什么。

在所有案例中都是使用的 Wwise 卷积混响效果。对于熟悉该插件的用户来说,前两次测试中使用的预设包含单脉冲响应 (IR) 和 -50 dB 的阈值,而在最后一次测试中换成了包含立体 IR 和 -144 dB 阈值的预设。常规预设和高质量预设中所使用的 IR 一样长,不同的预设会有 1-3 秒的变化。

图 7 显示了在搭载六核 CPU 的电脑上进行这些测试的结果。


图 7.实施和未实施并行化的音频帧渲染时间。

您可以看到,与非并行化版本相比,在第一次测试中,使用并行化音频渲染时实现了 1.45 倍提升,在第二次测试中实现了 2 倍提升,在第三次测试中实现了 1.9 倍提升。更重要的是,随着混响以及每个混响的 CPU 成本的增加,缩放也会得到改进。

在测试 PC 上,每项任务的平均时间为 60 微秒,这种任务粒度非常好。卷积混响任务是一个明显的例外,它需要长达 1 毫秒 (ms) 的时间以最高质量预设渲染音效。然而,即使在最昂贵的包含 8 种高质量混响的测试案例中,在所有内核上执行音频任务的每游戏帧总时间平均也只有单核帧时间的 17%。这意味着音频任务只占每个内核的一小部分时间,而大部分 CPU 资源用来执行其他游戏任务。

如果要大致了解相同的测试在 Xbox One* 和 PlayStation 4* 上的运行结果,可将图 7 中的时间乘以 4。

图 8 比较了在内核数量不同的 CPU 上实现并行化所带来的性能提升。图中清楚地表明,即使在四核和六核的旧 CPU 上,并发执行也能带来性能提升,而现代八核 CPU 可实现最大的性能提升。


图 8.在内核数量不同的英特尔® 酷睿™ 处理器上实现并发性所带来的性能提升。对于各测试案例和 CPU,同步音频帧渲染时间除以并行渲染时间,计算所得的结果就是性能增益系数。

检测房间

如果要确定声音应馈送至哪条混响总线,需要找到声音所在的房间。为此,我们将房间存储在一个空间划分数据结构中,它可以让我们快速找到声音点(声音发射器位置)。2015 年欧洲游戏开发者大会 (GDCE) 上的一篇演讲“《杀手》中的声音传播”详细介绍了这一算法。

算法实施很简单,唯一需要注意的是确保在嵌套或重叠的环境中选择正确的环境。分配房间的优先级可以帮助选择正确的环境。最外面的房间优先级最低,最里面的房间优先级最高。优先级应自动计算,并在编辑器中手动覆盖选项以进行微调。

另外需要考虑的是,当声音发射器从一个房间移到另一个房间时,要防止混响突然改变。获取最近的门户,并将声音发射器路由到门户另一侧的房间的总线,可以防止混响突然改变。在这种情况下,send value 是指声音发射器与通向另一房间的最近入口之间的一段距离。

为此,我们需要有效获取某点一定距离内的门户列表。因此,我们将表示门户的几何图形存储空间划分结构中,该结构与用来存储房间的一样。

我们要为每帧的所有声音查询房间及附近的门户,并将其路由至相应的总线。假设几何图形没有变化,位置没有发生变化的声音不需要这样做。但仍然有少数操作需要执行,虽然是轻量级操作,但加在一起可能会使主线程产生一些不必要的开销。因此,最好是将它们转移到工作线程。

一种方法是将所有查询分为子集,每个子集在自己的任务中处理。任务的数量与工作线程的数量相对应,选择子集大小要记住这一点。游戏逻辑更新完成后立即调度任务,这样执行查询时就不会产生新的发射器。任务调度应在开始处理音频命令队列,或调用 RenderAudio(如果使用 Wwise)之前完成。

还有一种更好的方法,使整个声音发射器更新例程成为一项单独的任务,在工作线程上并行执行这些任务。此处的更新例程是一个集合名称,表示针对每个播放声音发射器,在主线程上为每一帧执行的所有计算,可能包含遮挡、衍射、体积、多普勒和混响计算。

链接混响

声音穿过房间传到听众耳朵,应用这些房间的混响需要链接房间的总线。假设 A 房间中有一些声音,听众在 C 房间。A 房间和 C 房间之间是 B 房间。我们首先将声音路由至 A 房间的总线,后者路由至 B 房间的总线。因此,声音会受到三间房间混响的影响。

性能说明:无论这三个房间有多少声音,活跃总线数量仍然为 3。但如果所有播放的声音在不同的房间或通过不同房间传播,活跃总线很快就会增多。在这种情况下,必须实施逻辑以减少总线数量;例如,如果不会对总体混音带来很大影响,可以选择不将声音路由至总线。

上面提到的 GDCE 演讲中所介绍的传播系统可进行扩展,用来跟踪多条传播路径以及每条传播路径中的房间。有了这些信息后,总线路由的设置将会变得非常简单。

Wwise* 中的总线路由设置

Audiokinetic 在题为在 Wwise 2017.1 中使用全新 3D 总线架构:模拟音频监控系统的博客中详细介绍了设置。尽管其中的示例是实施音频监控系统,但同样的原则也适用于设置链接混响。您需要启用混响总线上的 Listener Relative Routing(Wwise 2018.1 之前称为 Enable Positioning)、为每件房间注册声音发射器,然后使用 SetListeners API 将声音从一个房间发射器路由至另一个。最后,使用 SetGameObjectAuxSendValues API 设置常规声音发射器的混响总线路由时,将 AkAuxSendValue::listenerID 设为常规发射器所在房间的发射器 ID。

并行化传播系统

为《杀手》(2016) 编写的初始传播系统仅用于计算遮挡和衍射;因此它只注重发现从任何房间进入听者房间的最佳传播路径。因此,更新传播系统的时间成本较低,每帧约 0.1 毫秒,我们曾在主线程上做到过。但跟踪多条声音到达听者耳朵的路径成本更高,因此最好将其转移到工作线程。

这样做通常是为了避免等待随时在其他线程上完成的更新。有两个简单的技巧可帮助实现这一目标:

  • 定时:更新摄像头和游戏角色位置后,将传播系统更新安排在帧的早期。那时就能知道听者的位置了。这样在帧结束时需要传播数据进行音频系统更新之前,就有足够的时间来完成传播任务。
  • 双缓冲传播数据:游戏循环产生的新声音可以在传播系统更新时查询,因此我们缓存上一帧的传播数据并将其提供给当前帧。音频系统更新之前,传播数据在帧的末尾交换。

混响的方向性

传统单混响解决方案存在一个重要问题,但对新的单混响解决方案来说这个问题更为重要,那就是游戏中使用的大多数混响效果并不保留声音的方向性,因为它们是单声道混响。这样做是出于性能原因。结果是,无论声音来自何方,所有扬声器都能发出混响。例如,假设一个非玩家角色 (NPC) 朝您的游戏角色的左边开了一枪。尽管您可以清楚地听到声音由左边传来,但其混响似乎来自四面八方。如果声音和听者在同一个封闭空间中,这没有问题,但实际上在任何情况下,我们都希望控制混响的方向和扩散。许多混响同时激活时,这一点尤其重要。虽然在一两种混响的情况下可以容忍缺乏方向性,但多个全向混响可能会导致声音混淆刺耳。

平移混响总线

要解决方向性问题,有一个简单的方法,那就是将每个房间的混响输出视作独立的 3D 声音,使用平移和扩散为该声音提供一个方向和音量。

在 Wwise 中,实施步骤与“Wwise 中的总线路由设置”一节中介绍的步骤基本相同。您需要启用混响总线上的 Listener Relative Routing,然后在为该房间的常规发射器设置混响路由时,将该房间的发射器 ID 分配给 AkAuxSendValue::listenerID,关联该总线和房间发射器。现在,您可以将房间发射器定位在房间中央或通往听者房间的门户上(更真实,但实施起来较难),平移混响。甚至您还可以使用 RegisterBusVolumeCallback API 实施自定义混音,这种方法非常实用,因为它是 Wwise 中根据发射器与听者之间的距离控制声音扩散的唯一方法。

这种方法有一个显著缺点,不能以这种方式平移听者房间的混响。如果房间代表的是一个大型开放空间,可能会遇到这个问题。您可以通过编程将大型空间分成几个小空间,为每个小空间创建一个声音发射器,并将声音路由到各自的空间,这个问题就能迎刃而解。这种方法同样适用于大型的非听者房间。分割几何体可以作为几何体生成过程的一部分离线进行。

另一个缺点和性能有关。通过启用混响总线的定位功能并使用每个房间的声音发射器,可以有效地创建每个房间的活跃总线实例。以前,使用同一条混响总线的两个房间共享活跃总线实例,只产生一种混响效果,现在每个房间都拥有自己的总线实例。这同样适用于可分成小几何体的几何体,如上所述,每个小几何体也会生成一个总线实例。这可能是个问题,也可能不是,取决于您的几何体。

使用多声道混响效果

保持混响方向性的另一种方法是使用多声道混响。四个声道应该足够了,除非需要支持垂直度。每个声音都可以混合到其定位与听者相关的混响总线通道。所有声音完成副路混合后,混响效果将在每条通道上单独渲染,不包含该帧可听内容的通道除外。之后,混响总线通道上混到最终的输出音频总线中,该总线的声道配置对应于玩家的实际扬声器设置(立体声、5.1、7.1 等)。

采用这种方法时,通道数量会导致混响成本增加。因此,在所有活跃混响总线上使用这种效果可能极其昂贵。最佳的方法是针对非听者房间使用单混响效果和混响总线平移,针对听者房间使用多声道混响效果。听者接近通向相邻房间的门户时,但该房间的单混响应随着多声道混响交叉变化,以确保声音平稳过渡。

Wwise 不支持多声道混响效果。Wwise 卷积混响具有滤波器模式,功能与上述逻辑接近。但它的各种声道配置不够灵活,因此不能用于生产。该模式也无法用于模拟混响。因此,只有那些愿意自己开发卷积混响插件的人选择使用多声道混响。实施这种插件时,尽量利用并行化 —— 渲染每个声道的音效应该成为在工作线程上执行的单个任务。这些任务在执行期间相互独立。

在不同的 CPU 上扩展

在《杀手 2*》中,我们保留了对旧系统的支持,并在游戏机上附带这种支持。但在 PC 上,玩家可以在三种质量水平(统称为“音频模拟质量”)中进行选择:

  • Base:没有音频增强功能。物理内核数小于四个的 CPU 默认该选项。
  • Better:新混响系统。物理内核数大于四个的 CPU 默认该选项。
  • Best:和 Better 一样,但包含最高质量混响设置。物理内核数大于六个的 CPU 默认该选项。

玩家可以在“音频选项”中控制音频模拟质量,如图 9 所示。

audio simulation quality setting
图 9.音频模拟质量设置。

Wwise 实施

通过复制图 10 所示的混响总线层级结构,可以在 Wwise 中支持不同质量水平的混响效果,并将 better 效果用于高质量总线。代码将根据模拟质量值自动选择正确的总线(标准或 HQ)。

high-quality busses mirror the regular reverb bus hierarchy
图 10.高质量总线镜像常规混响总线层级结构。

另一种更易于维护的方法是保持一个层级结构并将两种效果添加至每条混响总线,然后在运行时使用通过代码设置的全局实时参数控制 (RTPC) 绕过其中一种音效。

无论选择哪种方法,都需要将音效媒体存储在不同的声音库中,并根据模拟质量值加载正确的媒体,以避免不必要的内存使用开销。我们将不同的卷积混响声音库用于不同的质量水平,如图 11 所示。

sound bank structure in Hitman 2
图 11.《杀手 2*》的声音库结构。

结论

本文介绍了在利用现代 CPU 的多个内核如何支持改进游戏音频,打破之前的高计算成本所带来的局限性。《杀手 2*》的玩家注意到混响系统所发生的变化,获得了更出色的体验。提升音频系统的并行性释放了大量 CPU 资源,音频团队和其他部门很好地利用了这些资源。

尽管该示例仍然会受到低端多核 CPU 的局限性的影响。但随着八核以上系统的普及,游戏开发商将努力实现计算并行化,音频中间件生产商将充分利用矢量化(如英特尔® 高级矢量扩展指令集 2 (英特尔® AVX2)或英特尔® 高级矢量扩展指令集 512(英特尔® AVX-512)),显著提升游戏声音的细腻度和沉浸感。

关于作者

Stepan Boev 是 IO Interactive 的首席音频程序员。他投身视频游戏行业 14 年,一直致力于打造《杀手》、《Far Cry*<》和《World in Conflict*》游戏系列。他热衷于打造声效、沉浸感和细腻度俱佳的游戏音频,以及解决复杂的编程问题和实施性能优化。

参考资料

《杀手 2》

IO Interactive

2015 年欧洲游戏开发者大会 (GDCE) 演讲,“《杀手》中的声音传播”

在 Wwise 2017.1 中使用全新 3D 总线架构:模拟音频监控系统

产品和性能信息

1

性能因用途、配置和其他因素而异。请访问 www.Intel.com/PerformanceIndex 了解更多信息。