简介
Impacter 是最近针对 Wwise 开发的一款撞击建模插件原型(详可参阅这篇博文)。在本文中,我将介绍如何使用 Impacter 来实现声音并整合到采用 Unreal Engine 构建的演示游戏中。接下来,我想先简要介绍一下 Impacter 的主要功能,然后再深入探讨怎么在 Unreal 中加以运用。在此,我们重点说说如何利用物理系统来驱动 Impacter 的 Mass 和 Velocity 参数。这里总结的要点具有普遍适用性,对其他游戏引擎来说也是一样的。
Impacter
作为源插件,Impacter 可交叉合成不同声音的分层,并向这些声音应用物理变换。如需进一步了解有关 Impacter 的详细信息,请参阅这篇博文或相关 SDK 文档。在此,我们不妨先简单看下它都有哪些主要功能:
交叉合成
用户可将多个声音加载到 Impacter 中,然后在内部加以分析并拆分为两个分层:Impact 和 Body。Impact 代表对象被撞击时的初始碰撞。Body 代表对象在初始撞击后的谐振。
您可以自由组合 Impact 和 Body 分层,并让 Impacter 在每次播放时随机选择。
物理参数
Mass 代表被撞对象的大小。
Velocity 代表对象被撞击的力度。
Position 代表对象被撞击的位置。此参数由声学模态现象启发而来。通过调节位置可让声音的谐振产生细微变化。
Roughness 代表声音的嘈杂度,其可通过向声音的谐振应用频率调制 (FM) 来实现。
Impacter Unreal Demo
Impacter Unreal Demo 旨在方便测试和演示 Impacter 在简单游戏环境中的不同用法。在后面章节中,我会简要介绍一些相关用例,并探讨如何利用游戏物理来驱动 Impacter。
基础应用 (Footstep)
演示游戏中 Footstep 的设置相当标准。Wwise 中有个包含不同地面材质对应脚步声的 Switch Container 以及用于控制该 Switch Container 的 "FootstepMaterial" State Group。Wwise 中的 FootstepMaterial 状态由游戏中的地面材质设定。
鉴于每个 Footstep Sound SFX 都包含 Impacter 实例,我们选择了借助交叉合成来在音频样本的基础上生成更多变化版本。不过,目前为止针对的都是典型的 Footstep 场景。
通过使用 Mass 和 Velocity 参数,可以获得更加复杂精细的效果。藉此,可确保脚步声与不同角色大小协调一致。里面专门设有一个 "CharacterSize" RTPC,用于驱动各个脚步声对应的速度和质量。在 Unreal 中,"CharacterSize" RTPC 由角色对象的大小设定。另外,角色对象的大小还会反过来影响角色的运动速度和跑动动画。藉此,可模拟从巨大笨拙角色过渡到小巧灵活角色的效果。通过将 "CharacterSize" RTPC 映射到 Impacter 中的 Velocity 和 Mass 参数,我们可以轻松地在脚步声中对此予以呈现。
对象碰撞
环境中的每个对象和表面都有对应的 Wwise Event。这些 Event 可触发一个或多个 Impacter 实例。各个 Impacter 实例会将 Velocity 和 Mass 参数分别与 "ImpactVelocity" 和 "ImpactMass" RTPC 挂钩。在发生碰撞时,游戏会驱动这些 RTPC。
在构建 Unreal Demo 并集成 Impacter 时,最重要的一项任务就是在碰撞过程中查询 Unreal 物理系统,来为 Mass 和 Velocity 参数提供合理的值,以此确保 Impacter 声音作出自然的回应。我们可以通过名为 ImpacterComponent 的自定义 SceneComponent Blueprint 来实现这一点。游戏中的每个 Actor Blueprint 类都包含 ImpacterComponent(一个或多个)。在两个对象发生碰撞时,它们的 ImpacterComponent 会调用 ImpactComponentCollision。这时会计算 "ImpactVelocity" 和 "ImpactMass" RTPC 值,以便在针对每个对象触发 Impacter Event 之前发送给 Wwise。
在此,我们有必要明确一些术语。比如,静态对象是指从不移动的对象,动态对象是指可在游戏世界中四处移动的对象。在两个对象发生碰撞时,Unreal 中会触发两个碰撞 Event,并分别用于每个对象的 ImpacterComponent。
这样的话每个对象都要处理碰撞。首先,"On Component Hit" Event 针对 ImpacterComponent 调用 CheckImpactComponentCollision。接着,CheckImpactComponentCollision 确认 Other Actor 是否包含 ImpacterComponent。若其包含 ImpacterComponent,则调用 ImpactComponentCollision。
为了阐明这一点,我们来看个枪弹与墙壁碰撞的具体例子。从墙壁的角度来说,撞击对象(另一对象)为枪弹。从枪弹的角度来说,撞击对象(另一对象)为墙壁。相较于枪弹撞击墙壁,认为墙壁撞击枪弹似乎有悖常理。不过,在本节余下部分牢记这两种情境会很有用。这里的关键在于每个对象都会触发与之对应的 Wwise Event。在计算 "ImpactVelocity" 和 "ImpactMass" RTPC 时,会同时使用自身和另一对象的物理特性。
因为游戏中的所有对象都会使用 ImpacterComponent,所以关键在于以合理且普适的方式实现由碰撞物理到 RTPC 值的映射。这样便可直接利用对象的物理特性来驱动声音,而无需声音设计师执行任何进一步的手动调节。只要声音设计师合理调节了 Velocity 和 Mass 曲线,关卡设计师就可放心地设计、放置并更改对象,声音自会作出恰当的回应。
讲明了这些,下面就来看下 ImpacterComponent 如何在碰撞 Event 期间使用 ImpactComponentCollision 函数计算 RTPC 值。
Mass 和 Velocity
"ImpactMass" 和 "ImpactVelocity" RTPC 值分别在 GetImpactMass 和 GetImpactForce 中计算。它们全部通过 ImpactComponentCollision 进行调用。
Mass (GetImpactMass)
Mass 直接从 Unreal 物理系统获取并用于动态对象(注意:我们需要为具有相应 Density 值的对象设置 Physical Material,以确保赋予对象以相应的 Mass 值)。静态对象本身没有 Mass 值,因为其并不需要模拟物理。对此,系统会赋予其一个明确的 Mass 值。对于墙壁和地板,在设置 "Mass" RTPC 值时,会依据碰撞过程中撞击对象的质量计算其 Mass 值。
Velocity (GetImpactForce)
我们当然可以将对象的速度直接映射到 Impacter 中的 Velocity 参数。但是,对于体积小、重量轻的对象,这样产生的撞击声过于响亮。比如,在一个柔软的小球以足够快的速度与金属表面发生撞击时可能会导致表面触发巨大的撞击声。为此,最好将撞击的力度映射到 Impacter 中的 Velocity 参数。所以,除速度外,我们还要使用动量(速度 x 质量)。这样的话,重量较轻的对象会产生较低的 "ImpactVelocity" RTPC 值。撞击声也会因而变得更加柔和。另外,在计算各自的 "ImpactVelocity" RTPC 值时,也要考虑两个对象的动量。最后,我们还可直接将密度引入到映射中,确保质地较软的对象产生更为柔和的撞击声。
跟质量一样,速度的计算会因撞击对象是动态还是静态而略有不同。在撞击对象为静态时,使用 ImpactedByStaticObject 函数计算力度。在撞击对象为动态时,使用 ImpactedByDynamicObject 函数计算力度。
ImpactedByStaticObject
在此,我们通过将被撞对象的动量乘以撞击对象的密度来获取 "ImpactVelocity" RTPC 值。记住,这里的撞击对象是墙壁或地板。
ImpactedByDynamicObject
在此,我们使用各个对象的动量之和,其依据密度当中的最小值计算。注意,在这种情况下,被撞对象有可能是静态的。这时的动量为 0,动量之和等于撞击对象的动量。
为了便于理解,不妨将各种情形列在表格中。图 1 显示了如何针对不同的碰撞情形计算 "Velocity" 和 "Mass" RTPC。注意,两个静态对象永远不会发生碰撞。
测试和微调
在完成这些映射设置后,接下来便可将枪弹发射到表面上并测试声音效果。
听起来效果很差!这是因为当枪弹在地板上滚动时 Unreal 物理系统连续发送了多个碰撞 Event。鉴于枪弹滚动并不一定具有独立的撞击 Event,我们当然可以直接针对地面禁用撞击声,或者使用不同的插件(如 SoundSeed Grain)来实现滚动声。不过,我们发现其实可以利用连续碰撞的优势,来模拟物体在大量轻微撞击当中滚动的声音(比如撞击地面并滚动的声音)。为此,我们不妨先来限制 ImpacterComponent 触发 Wwise Event 的速率。
现在听起来没刚才那么差了,但节奏太过紧凑,听起来不自然。为了解决这一问题,我们可以为 Wwise Event 间隔时段增添一些随机性。为此,我们来在每次重置计时器时向间隔时段施加随机偏置。
现在听起来好一点了,但枪弹滚动过程中的重复撞击对滚动运动来说有点过了。听起来好像是有人在连续撞击地板一样。为了解决这一问题,我们可以把运动的方向加入进来。具体来说,就是使用对象的速度矢量和从对象中心到表面上撞击点的矢量之间的点积。说起来有些拗口。这里有个示意图,各位可以看一下。
d = v.i。其中,v = 速度矢量,i = 从对象中心到表面上撞击点的矢量。
在此,我们依据 d 来计算 "ImpactVelocity" RTPC 值。
若对象直接朝着撞击点运动,则 d = 1。若枪弹在地板上滚动,则 d 相对较低。这时的 RTPC 值会减小。
现在的声音整合效果得到了进一步的改善。我们可以清楚地听出枪弹最初在地板上弹跳以及随后在地板上滚动之间的区别。
注意,d 因数的加入将改进其他情形以及在地板上滚动时的 RTPC 计算。比如,在快速移动的对象只是从另一对象的侧面擦过而非与其迎头相撞时。对于动态撞击(两个对象都在移动),每个对象都有 d 值。这时可以取这些值当中的最大值并使用其来计算两者的 "Velocity" RTPC 值。对于静态对象(地板和墙壁),d = 1(最大值)。所以,在撞击对象为静态而被撞对象为动态时,会默认使用动态对象的 d 值。
打破规则
Impacter 本来是专门为某一类特定的声音设计的。其算法假定振幅包络呈指数级衰减,而且这当中有一个主要的瞬态区域。也就是说,声音“看起来”是这样的:
不过,我们也可试着将其用于其他类型的声音。在 Impacter Unreal Demo 中,我加入了一些碎裂声和粉碎声。它们包含不止一个瞬态,因而打破了算法的假设。
这里的问题是不同声音的振幅包络存在明显的差异。如此一来,Body 分层(谐振部分)中便有可能会产生不自然的鸣响声。振荡器和滤波器组的振幅遵循原始声音的振幅包络。倘若与具有截然不同的振幅包络的声音的 Impact 分层结合在一起,听起来就会很假。
针对此类情况,我们专门在 Impacter 的 UI 中添加了“启用(inclusion)”复选框。藉此,可在针对一组声音尝试交叉合成时弃用存在问题的撞击/模型组合。
在 Unreal 中,我添加了可在发生力度足够大的撞击时破坏的 Mesh 来构建可粉碎的物块。跟撞击声一样,碎裂声也包含 Impacter 实例。这些碎裂声会将 Mass 和 Velocity 参数与 RTPC 挂钩。
除此之外,在实现这种破坏声时还可让各个碎片来触发 Wwise Impacter 声音,以此模拟物体碎裂成片并与环境以及彼此发生撞击的效果。这样的话,多个单独的撞击声会结合在一起,游戏中发生的物理交互会直接生成碎裂声/粉碎声。Unreal Demo 中可破坏的箱体便是如此。最初,我使用了碎裂声并设为在箱体碎裂时触发。
这个碎裂声存在问题,因为其具有截然不同的振幅包络且包含多个瞬态。通过以上片段可以听出,大箱体碎裂时能听到多个瞬态,但并没有与此对应的物理交互。为此,我决定将 ImpacterComponent 与箱体的各个碎片绑定。这些 ImpacterComponent 最初处于非活跃状态。在箱体碎裂时,各个碎片上绑定的 ImpacterComponent 会变为活跃状态,并开始对碰撞作出回应,进而触发 Wwise Event。如此一来,在箱体碎裂时,源自各个碎片的撞击便会自然生成碎裂声/粉碎声。
位置映射
在大多数情况下,会为 Position 参数应用随机化处理,来在整个 Unreal Demo 当中增添声音的多样变化。Position 参数可通过减小特定滤波器和振荡器的增益来改变谐振。同时,将基于频率的非线性增益斜线应用于声音的 Body 分层。Position 参数可以控制这些内部增益斜线的位置。也就是说,通过调节位置可让声音的谐振产生细微变化。在将位置设为 0 时,声音具有全谐振特性。这时不会应用任何增益衰减。在位置由 0 变为 1 时,内部增益斜线会逐渐减小和增大峰值频率的增益。
在 Impacter Demo 中,宽大的玻璃墙壁会将撞击位置和墙壁中心之间的距离映射到 "ImpactPosition" RTPC。这种映射是逆向的,所以角落和边缘的谐振最强,而且在撞击向中心移动时不同的峰值频率会被衰减。
(扫二维码观看视频)
要点总结
在充分考虑从游戏物理到 RTPC 的映射后,我们可以灵活运用 Impacter 这款工具来整合各种撞击声。下面是在利用 Impacter 整合声音时需要牢记的一些基本原则:
整体设计
结合 Mass 和 Velocity 参数测试声音,确保在与游戏物理集成时得到充分体现。
活用力度
Impacter 中的 Velocity 参数应当由碰撞的力度驱动。很多因数都会对此产生影响,包括两个对象的密度和动量。
变换位置
要想最大限度地增添声音的多样变化,最好对 Position 参数进行随机化处理。藉此,可让声音的谐振产生细微变化。这对弹击声或脚步声来说特别有用。
打破规则
虽说本来是专门为撞击类声音设计的,但不妨试试其他输入会有怎样的效果。不过,在添加特别长的样本时要注意。分析阶段对简单撞击来说很快,但较长的声音可能会比较费时。
稍后还会发布第三篇有关通过交叉合成实现多样变化的博文。敬请期待!
本文作者
肖恩·索拉汉 (Sean Soraghan)
软件开发工程师,Audiokinetic。肖恩·索拉汉 (Sean Soraghan) 目前就职于 Audiokinetic,是一名软件开发工程师,主要从事 Wwise 设计工具开发以及 Unity 和 Unreal 游戏引擎集成工作。在此之前,他拿到了工程学博士学位,重点研究音乐音色的表示法和视觉化。另外,还协助举办过大型声音视觉装置展会。他喜欢在闲暇之余开发各种游戏和工具。
更多内容,欢迎关注我们官方B站和新浪微博!
赛博朋克世界的声景塑造 — 以游戏《底特律:成为人类》为例 (第二部分)
赛博朋克世界的声景塑造 — 以游戏《底特律:成为人类》为例 (第一部分)
在 Wwise 中设定 Audio Object