探索 PHASE:基于物理几何的空间音频引擎
Discover geometry-aware audio with the Physical Audio Spatialization Engine (PHASE)
2021年6月10日
一句话判断
PHASE 是苹果给游戏和 AR 开发者准备的”穷人版 Wwise” —— 几何感知的空间音频、材质过滤、距离衰减全部内置,但和 AVFoundation 完全不互通,是一个独立的音频世界。
这场 Session 讲了什么
PHASE(Physical Audio Spatialization Engine)是苹果在 iOS 15 / macOS 12 中引入的全新音频引擎,专为需要物理级空间音频的游戏和 AR 体验设计。
核心概念是把声音源(Sound Event)放置在 3D 空间中,引擎会根据听者(Listener)的位置、朝向以及场景中的几何体(墙壁、障碍物)自动计算声音的传播路径。这包括:距离衰减、遮挡过滤(Occlusion,声音被墙壁挡住会变闷)、材质吸收(不同材质表面吸收不同频段)以及多普勒效应。
Session 演示了如何用 PHASE API 定义声源、配置空间化参数、添加几何体作为遮挡物,以及如何在游戏循环中实时更新听者和声源的位置。
值得深挖的点
几何感知的实现机制。PHASE 不是简单地算距离,而是会做射线追踪(Ray Tracing)来判断声源和听者之间有没有障碍物。你需要在 PHASE Engine 中注册场景的几何体(PHASEGeometricSpreading),引擎会在每帧更新遮挡关系。这意味着你有声源遮挡,但忘了注册墙壁几何体,声音会直接穿墙。
材质过滤的声音设计。PHASE 支持给场景几何体关联材质(PHASEMaterial),不同材质对声音的吸收系数不同。混凝土墙会高频全部挡掉,木板只吸收部分频段。这不是简单的音量调节,而是真实的频率过滤(Low-pass filter),听感上很自然。材质参数需要你在代码中手动配置,苹果提供了几个预设。
代码片段
初始化 PHASE Engine 和声源:
import PHASE
// 创建 PHASE 引擎
let engine = try! PHASEEngine()
// 定义一个声源
let soundAsset = try! engine.assetRegistry.addSoundAsset(
fileName: "explosion.wav",
identifier: "explosion"
)
// 创建空间化参数
let spatialMixer = PHASESpatialMixerDefinition()
spatialMixer.spatialMode = .channelBased // 使用声道空间化
// 创建声音事件
let eventDefinition = PHASESoundEventNodeDefinition(
soundAssetIdentifier: "explosion",
mixerDefinition: spatialMixer
)
设置距离衰减和空间化:
import PHASE
func configureSpatialization(for engine: PHASEEngine) {
// 配置几何扩散参数
let geometricSpreading = PHASEGeometricSpreadingParameters()
// 基于反平方定律的距离衰减
geometricSpreading.mode = .inverseSquareLaw
// 设置参考距离(在这个距离内音量不衰减)
geometricSpreading.fadeOutParameters = PHASEDistanceFadeOutParameters(
cullDistance: 100.0, // 超过 100 米直接静音
cullSleepDistance: 80.0
)
// 应用到空间混音器
let spatialDefinition = PHASESpatialMixerDefinition()
spatialDefinition.geometricSpreadingParameters = geometricSpreading
}
实时更新声源位置:
import PHASE
class GameAudioManager {
let engine: PHASEEngine
var soundSource: PHASESource?
init() {
engine = try! PHASEEngine()
engine.start()
}
// 每帧更新声源位置(在游戏循环中调用)
func updateSourcePosition(_ position: SIMD3<Float>) {
// 更新 PHASE 声源的世界坐标
soundSource?.transform = Transform(
translation: position
).simdTransform
}
func updateListenerPosition(_ position: SIMD3<Float>, rotation: simd_quatf) {
// 更新听者(通常是摄像头/玩家)的位置和朝向
engine.defaultListener.transform = Transform(
translation: position,
rotation: rotation
).simdTransform
}
}
最佳实践
- PHASE 和 AVAudioEngine 不要混用。它们是独立的音频管道,同时运行会争抢音频资源。如果 app 同时需要音乐播放和游戏音效,用 PHASE 管游戏音、AVPlayer 管音乐,但要测试后台音频中断的处理。
- 几何体注册要精简。射线追踪的计算量和几何体复杂度成正比。用简化的碰撞盒代替精确的 3D 网格作为 PHASE 遮挡体,每帧多省几毫秒。
- 声源数量控制在 16 个以内。PHASE 没有官方声源数量上限,但实际测试中超过 16 个活跃声源后,中低端设备会出现掉帧。
还有什么值得关注
- PHASE 支持多普勒效应,移动的声源会自动产生频率偏移,适合赛车类游戏。
- 引擎内置了 Ambisonics 声场渲染,可以配合 AirPods Pro 的空间音频使用。
- PHASE 目前只在 Apple 平台可用,没有跨平台支持,如果游戏需要多平台发布要考虑替代方案。