在沉浸式环境的构建中,除了视觉、交互设计外,音频同样是异常重要的一环。事实上,很多知名VR游戏和影视内容团队在分享中都会强调VR空间音频对于作品的重要性。但如何利用好VR空间音频,大家都处在不断摸索和尝试的过程中。在前不久的Unite 2017 Shanghai大会上,来自Unity的工程师孙志鹏分享了对于VR环境中音频构建的思考和建议。

因为360视频里没有了镜头的概念,所以当用360视频来叙述故事时,让观众看向剧情发展的地方变的非常重要,这也是空间化音效一个非常好的使用场景。我们可以靠声音引导观众看重哪些地方。

3D音源的空间化就是逆向的过程。这样做要从两方面来处理,一方面是从方向上,另一方面是从距离上。孙志鹏详细介绍了HRTF,头部关联的脉冲响应的原理和使用。以及如何用Unity在方向和距离上快速模拟空间音频。

以下是演讲内容:

用音频来引导玩家

孙志鹏:声音是一个一直不太受开发者重视的元素,我们一直都在讨论画面之类的东西,但在VR中创建沉浸式体验,声音也是非常重要的一环。玩家和观众在虚拟的环境当中听到一个声音来自于虚拟世界当中的位置,会变的非常重要,原因在于我们在沉浸式体验当中没有了屏幕的概念。如果说是叙事的环节的话,也没有了镜头的概念,我们要推动故事情节的发展需要引导玩家,声音是非常优雅而简便的,也是实用的选择。

音效如何实现空间化?首先要了解人类是如何定位空间中的声音的。这个过程可以简单地分为两个方面,人类是如何定位声音从哪个方向过来,离我们有多远。定位方向无非要知道声音是来自于前后左右还有多少角的方向。最主要的就是靠时间差ITD和强度差ILD来判断,这两个参数的判定优先级是与头部的大小有关。800赫兹以下的是靠时间差来判断的,高于800赫兹是靠强度差,中间是靠他它们两个之间的是共同判定的。

有时候在实际生活当中也会用侧耳倾听的方式把判断前后的问题改为判断左右的问题,值得一提的是人类对于前方的声音的精确度可以到1度,对于侧边的声音也可以到15度,人的听觉系统可以分辨10毫秒以内的时间差,所以我们要在开发过程中能够达到相应的精度才能让体验者不会感到声音是虚假的。

之前我们提到过800Hz以下我们就只能看时间差来分辨声音的左右了,以200Hz的声音为例,声音的半波长大约是85cm远大于我们的头的尺寸所以在这个情况下我们根本无法挡住声音,所以声音的强度差显然靠不住了,而在高于1500Hz的6000Hz声音的情况下,我们会完全的挡住声音,所以我们无法靠时间差来只能依赖于强度差,而对于前/后和仰角我们交给HRTF(下文有解释)。

声音越大离我们越近,对于直接声音和经过一次反射的声音到达耳朵的延迟的Initial Time Delay来说,延迟越大,说明声音里我们越近。对于我们听到直接声音和反射声音的比例来说,直接声音的比例约高,说明音源离我们越近。

如何用声音感知距离?

对于听觉的运动视差来说,越近的声音运动的会越快,想象我们站在铁路边一辆火车以恒定的速度从远处开来。我们听到火车的声音由远及近时一开始声音移动的很慢可到了近处后,火车呼啸而过,声音移动的很快。高频声音在传播的过程中会比低频声音衰减的更快。所以高频部分衰减的越少说明约近。

对于7.5和5.1的环绕立体声来说,依靠听者的位置和朝向的假设。这种情况下在家庭影院上是可以的,我们可以假设定者的地位和朝向的位置。

3D音源的空间化就是逆向的过程。简单来说就是我们处理我们的声音,让它听起来像是发生在空间当中的那个位置。这样做自然要从两方面来处理,一方面是从方向上,另一方面是从距离上。方向上主要是可以靠HRTF加上头部跟踪里处理,具体就是利用刚才讨论的那些关于声音距离方面的特征属性来逐一地处理。

HRTF的应用(模拟方向)

首先我们要明白什么是HRTF,头部关联的脉冲响应。HRIR是一个与音源和耳朵位置相关的脉冲响应,对任意音源做某一特定HRIR的卷积就是把该声音处理成像这个脉冲响应关联的耳朵和声音的位置,音源在这个HRIR相关联位置发出声音的过程。我们现在已经知道什么是HRIR了,它可以把我们的声音处理成像那个位置发出的声音,在这个过程中可以做卷积。傅立叶变换可以把卷积变成乘法,乘法变成卷积,这里我们显然希望使用乘法来代替卷积运算从而减小性能的开销。所以我们需要HRTF。

HRTF是因为散射导致的基于音源位置的选择性的加强和衰减某些特定频率的现象。耳朵在这个过程中扮演了一个声音探针的角色。这个效果可以在数十分贝的数量级上影响某些特定频率声音的大小。这个图可以让我们对HRTF有一个更直观的认识,它横轴是频率,大概能到40000赫兹的范围,纵轴的加强的分贝数。它对于一个特定的角度、特定的距离对于特定频率的声音加强和衰减。

值得一提的是几乎每个人的HRTF都因为不同的头部大小和不尽相同的身材,独特的耳部特征,所以每个人的头部模型都是一个独一无二的个人属性。

HRTF是在一个完全不会发生反射的房间用这样的一套设备来测量的。wiki有一些能搜到的数据,我们的开发者可以不用自己去模拟HRTF开发方式,可以直接做声音的处理软件。

现在让我们来看看HRTF是怎么使用的。假设空间中的一个特定位置的扩音器发出的声音为X1,X1经过扩音器的transfer function传播到听者所在的位置,这个时候要经过有人体特征构成的天然滤波器,就是HRTF到鼓膜被我们听到。

假设这个时候在鼓膜处有一个话筒,可以把我们听到的声音给记下来,记录下来为Y1.那么当我们想在耳机里直接模拟出这个过程时,我们就需要有一个transfer function涵盖LF/H的整个过程,是的最后同样在鼓膜里听到的Y2和之前的Y1完全一样。

在L和F的过程当中,头显追踪到头部,声源在虚拟场景的更新都要应用到相应的LF工具中去,这个时候我们才能进行声音的处理。

距离的处理

距离相对比较好处理,每一条都有符合认知的
的感觉。声音从远到近,会因为距离而衰减。直接声音和初次反射声音的时间差,从远到近,会由小到大。直接声音和反射声音的比例,从远到近,直接的声音的比例也会逐渐增高。声音的移动上来说,正如之前讲到火车的例子,从远到近,音源的移动速度也会由慢到快。我们把声源绑在虚拟的场景中的物体上,获得了天然的优势。

当然,我们需要在考虑声音基于距离衰减的基础上,让高频的声音衰减的更快。

如何在Unity中快速模拟空间音频?(方向部分)

Unity为我们的开发者提供了一个空间化音效的SDK,要讨论这个SDK我们不得不提到Unity里的Native Audio Plugin SDK,因为它是我们空间化音效SDK的基础。

Unity里让开发者创建Audio effect,任何一个Audio Effect都可以用这个SDK完成,它可以像一个graph一样去构建复杂的音效。

举两个音源的做一个混合的效果的例子。我们可以创建音效,指明它在音源里面有输出,在callback函数中最关键的就是process call back函数,我们的开发者可以在其中对音源的每一个channel和sample做处理。

相对Native Audio Plugin SDK,Spatialization Audio SDK唯一的不同是,在整个声音特效的graph中设计空间化音效,对场景当中每一个功能的音效都会创建唯一的音效instance在graph里与之相连。这个时候只要空间化音效打开,无论场景中的Audio Graph多么复杂,它都会加入唯一过滤的声音。

接下来进入具体的实现环节。首先是通过Capturing HRTF,当然我们也可以选择不用自己Capture,而使用一些现成的数据。

在unity提供的sample中,我们使用MIT EDU提供的数据。它的数据结构是这样,有14个仰角,对于每一个仰角有一定数量的方位角。例如对水平向下40度的角度,提供了16个Sample点,每个点之间的差是4.63,这样的一组数据是以人为圆心的不封闭的圆,刚才采集HRTF的半径是1.4M。

然后我们来讨论一下在SDK里Apply HRTF的过程。之前讨论的是在ProcessCallback中,根据方位角和仰角,我们找到对应的HRTF,处理我们音源中的sample data。

它的信号是一个时域空间的信号,声音是跟时间有关的数据,我们在时域空间需要对它进行做卷积。卷积的运算量是非常大的,对于这种数据我们就要用这里提供的Unity Audio SDK进行傅立叶变换,将离散空间的数字转化到频率空间,这样我们就可以用复数乘法代替卷积运算,从而可以减少性能的开销。

在处理完对特定频率的增强和衰减以后。我们再把audio data的转回时域空间,写入audio effect的output buffer,这样的节点可以传给下面的节点去做操作。

上述过程中的计算量仍然比较大,且像这样的没有太多逻辑关系的纯计算任务也比较适合在GPU上完成,所以现在的一些GPU厂商也相继为这样的需求提供GPU的硬件加速支持。比如AMD提供的TrueAudio NextSDK,它提供了一套与Unity Audio SDK里很相似的API来进行傅立叶变换和复数乘法,甚至也有卷积运算的函数。其实它API的使用方式和Unity里面的API使用方式非常相似,但是它的内部是用GPU加速的,所以这样可以让我们在声音计算的过程中也享受到GPU带来的运算性能。

然后在Head Tracking的部分我们需要将头显的方位数据apply到场景当中带有audio listener组件的camera上,使得我们在process call back中通过listener matrix变成头显的位置。对于支持Unity SDK的硬件这个过程是自动完成的。

Unity控制空间音频距离方法

对于声音的衰减和声源的移动,我们可以在CreateCallback中注册一个距离相关的衰减回调,然后在这个回调函数中可以得到当前声音和听者的距离,通过增加一个距离声音的衰减并把它应用到音源数据上。

对于Initial Time Delay 和 Direct/ Reflection ratio,牵涉到声音在三维场景中的反射。牵涉到复杂的三维场景,计算量会变的非常复杂。我们可以用一个近似的方法来模拟复杂的3D场景,使用一个cube来模拟3D场景,让大家有反射的感觉。

可以在Create Callback里创建注册一些描述近似模拟场景的cude的属性(长宽高),然后Unity可以自动生成相应的UI,通过audio effect调整参数。随后在process call back中根据这些数据来计算反射,避免引入复杂的三维场景当中声音的反射。

对于高频的衰减,在之前HRTF的处理过程中,转化到频率空间后,我们可以查看高频的声音在哪里,定义什么是高频。在Callback函数中我们也可以拿到一些值,所以我们可以在这个阶段设定一个频率相关的距离衰减。

我这里有一个简单的Demo,是关于360视频。因为360视频里没有了镜头的概念,所以当用360视频来叙述故事时,让观众看向剧情发展的地方变的非常重要,这也是空间化音效的一个非常好的使用场景。我们可以靠声音引导观众看重哪些地方。

在Mac的笔记本上如果是20帧的消耗,跑到120帧的画面场景中20个HRTF音源性能消耗大约时3%左右。300个HRTF音源性能消耗大概到27%,但是这时候还有很长的时间发在WaitForTargetFPS。因为把帧数定在120,如果我们增加到600个HRTF,苹果笔记本也是可以接受的。

这个技术在andriod设备上。同样的360视频,300个HRTF音源,在小米四上大概有20几帧,至少可以证明它是可以使用的技术,在移动设备上也是可以跑起来的。