我是假标题
前面十三提到了Protoform音效设计——体积声源,接下来由我给大家详细的介绍一下。
反正需要开始标题
实现一个简单的体积声源,是可以快速提高游戏听觉Level的事情,像我们《Protoform》中有很多的这样 的需求,举个栗子,玩过我们Demo(悄悄告诉你们新Demo快出了)的朋友可能会对这两个物体有印象。
这两个物体呢就是比较典型的需要立体声源的应用,第一个是一个很规则的矩形,第二个是一个立方体,这两个物体在我们设定中运行的时候是整体发声,下面我们以这个立方体的升降装置为例,简单分析一下。(感谢美术童鞋 @LANKA 供图)
- 如果立方体的声源是一个点(也可以看作小球体),不考虑其他环境因素,如图一,声波传播和衰减后形成的形状会是一个球体,类似于绿色线框描述的区域
- 如果立方体声源是一个体积声源,不考虑其他环境因素,如图二,声波传播和衰减后形成的形状会是一个近似圆角立方体的一个立体形状,如图二蓝色线框描述的形状
很明显,图二比计较接近真是情况,符合我们预期的。
转化为程序问题
在 FMOD 插件里面,每一个声源都是从一个 3D 的位置点发出的,怎么让一个点变成一个立方体呢?
想象你在嘈杂的人群中,虽然每个人都哔哔啵啵的讲个不停(很多很多声源),但你听到的是所有声音合成后的声音(一个 Listener)。如果此时给你带上一个隔绝环境音的耳机,耳机里播放的是在你的耳朵的位置采集的环境声,你能区别声音来自耳机还是环境吗?(假如耳机使用的是火电站发的电)
OK,你无法区分。所以,用一个耳机来代替无数的环境声源是可行的。
这就是体积声源的原理,我们仅使用一个点来代替一个有体积的发声物体。为了正确的计算3D 声音衰减,这个点应该总是与 Listener 最近的物体体积内的一点。
其实这与传统游戏中的环境音实现比较相似,只是有更细节的体积控制了。
代码实现
FMOD 插件会收集所有的声源点然后进行 3D 属性信息的更新,根据我们项目的需求,我们定制了自己的声源组件,每个声源组件根据自己的形状以及 Listener 的位置计算和更新3D 信息,支持 6 种形状,下面是更新 3D 信息属性的实现方法:
void Update3DInfo() { Vector3 nearListenerPos = Vector3.zero; //将Listener的位置转化为AudioSource的本地空间位置 Vector3 listenerPos = transform.InverseTransformPoint(FmodAudioListener.position); //根据不同形状计算离listener最近的点 switch (_shape) { case Shape.Line: nearListenerPos.x = Mathf.Clamp(listenerPos.x, -_halfSize.x, _halfSize.x); break; case Shape.Circle: listenerPos.y = 0f; nearListenerPos = Vector3.ClampMagnitude(listenerPos, _halfSize.x); break; case Shape.Rect: nearListenerPos.x = Mathf.Clamp(listenerPos.x, -_halfSize.x, _halfSize.x); nearListenerPos.z = Mathf.Clamp(listenerPos.z, -_halfSize.z, _halfSize.z); break; case Shape.Sphere: nearListenerPos = Vector3.ClampMagnitude(listenerPos, _halfSize.x); break; case Shape.Cuboid: nearListenerPos.x = Mathf.Clamp(listenerPos.x, -_halfSize.x, _halfSize.x); nearListenerPos.y = Mathf.Clamp(listenerPos.y, -_halfSize.y, _halfSize.y); nearListenerPos.z = Mathf.Clamp(listenerPos.z, -_halfSize.z, _halfSize.z); break; case Shape.Cylinder: float y = Mathf.Clamp(listenerPos.y, -_halfSize.y, _halfSize.y); listenerPos.y = 0f; nearListenerPos = Vector3.ClampMagnitude(listenerPos, _halfSize.x); nearListenerPos.y = y; break; } _attributes3dTemp.forward = transform.forward.ToFMODVector(); _attributes3dTemp.up = transform.up.ToFMODVector(); //将离listener最近的点的位置转化为世界空间位置,并存储到3D属性结构体 _attributes3dTemp.position = transform.TransformPoint(nearListenerPos).ToFMODVector(); //设置3D属性信息 _eventInstance.set3DAttributes(_attributes3dTemp); }
效果 Show
谢谢观看!
暂无关于此日志的评论。