Files
game_engine/docs/superpowers/specs/2026-03-25-phase6-2-3d-audio.md
2026-03-25 11:37:16 +09:00

4.2 KiB

Phase 6-2: 3D Audio — Design Spec

Overview

voltex_audio에 3D 공간 오디오를 추가한다. 거리 기반 감쇠와 스테레오 패닝으로 소리의 공간감을 표현한다.

Scope

  • Listener (위치, 방향)
  • SpatialParams (이미터 위치, min/max 거리)
  • 거리 감쇠 (inverse distance)
  • 스테레오 패닝 (equal-power)
  • play_3d, set_listener API

Out of Scope

  • 도플러 효과
  • HRTF
  • Reverb/Echo
  • 다중 리스너

Module Structure

voltex_audio (수정/추가)

  • crates/voltex_audio/src/spatial.rs — Listener, SpatialParams, 감쇠/패닝 순수 함수 (Create)
  • crates/voltex_audio/src/mixing.rs — PlayingSound에 spatial 추가, mix에 spatial 적용 (Modify)
  • crates/voltex_audio/src/audio_system.rs — Play3d, SetListener 명령 추가 (Modify)
  • crates/voltex_audio/src/lib.rs — spatial 모듈 등록 (Modify)

Dependencies

  • voltex_math — Vec3 (위치, 방향 계산)
  • crates/voltex_audio/Cargo.toml에 voltex_math 의존 추가

Types

Listener

#[derive(Debug, Clone, Copy)]
pub struct Listener {
    pub position: Vec3,
    pub forward: Vec3,
    pub right: Vec3,
}
  • default() → position=ZERO, forward=-Z, right=X

SpatialParams

#[derive(Debug, Clone, Copy)]
pub struct SpatialParams {
    pub position: Vec3,
    pub min_distance: f32,
    pub max_distance: f32,
}
  • new(position, min_distance, max_distance) — 생성
  • at(position) — min=1.0, max=50.0 기본값으로 간편 생성

Functions (spatial.rs)

distance_attenuation

pub fn distance_attenuation(distance: f32, min_dist: f32, max_dist: f32) -> f32
  • distance <= min_dist → 1.0
  • distance >= max_dist → 0.0
  • 사이: min_dist / distance (inverse distance, clamped)

stereo_pan

pub fn stereo_pan(listener: &Listener, emitter_pos: Vec3) -> (f32, f32)
  1. direction = (emitter_pos - listener.position).normalize()
  2. pan = direction.dot(listener.right) — -1.0(왼쪽) ~ 1.0(오른쪽)
  3. Equal-power panning:
    • left_gain = cos(pan * PI/4 + PI/4) — pan=-1일 때 1.0, pan=1일 때 0.0
    • right_gain = sin(pan * PI/4 + PI/4) — pan=-1일 때 0.0, pan=1일 때 1.0
  4. 이미터가 리스너 위치와 동일하면 (1.0, 1.0) 반환

compute_spatial_gains

pub fn compute_spatial_gains(listener: &Listener, spatial: &SpatialParams) -> (f32, f32, f32)

Returns (attenuation, left_gain, right_gain). 편의 함수: distance_attenuation + stereo_pan 결합.

Mixing Integration

PlayingSound 변경

pub struct PlayingSound {
    pub clip_index: usize,
    pub position: usize,
    pub volume: f32,
    pub looping: bool,
    pub spatial: Option<SpatialParams>,  // NEW
}

mix_sounds 변경

시그니처에 listener: &Listener 파라미터 추가:

pub fn mix_sounds(
    output: &mut [f32],
    playing: &mut Vec<PlayingSound>,
    clips: &[AudioClip],
    device_sample_rate: u32,
    device_channels: u16,
    frames: usize,
    listener: &Listener,  // NEW
)

spatial이 Some인 사운드:

  1. compute_spatial_gains로 (attenuation, left, right) 계산
  2. 볼륨에 attenuation 곱하기
  3. 스테레오 출력에 left/right gain 적용

spatial이 None인 사운드 (2D):

  • 기존 동작 유지 (전체 볼륨 균등)

AudioSystem API 변경

새 명령

AudioCommand::Play3d { clip_index, volume, looping, spatial: SpatialParams },
AudioCommand::SetListener { position: Vec3, forward: Vec3, right: Vec3 },

새 메서드

pub fn play_3d(&self, clip_index: usize, volume: f32, looping: bool, spatial: SpatialParams)
pub fn set_listener(&self, position: Vec3, forward: Vec3, right: Vec3)

Test Plan

spatial.rs

  • distance_attenuation: at min_dist (1.0), at max_dist (0.0), between, zero distance
  • stereo_pan: right side → right gain > left, left side → left gain > right, front → roughly equal, same position → (1.0, 1.0)
  • compute_spatial_gains: combines both correctly

mixing.rs (spatial integration)

  • 2D sound unchanged (spatial=None, listener 무관)
  • 3D sound at max_distance → near-silent output
  • 3D sound at min_distance → full volume
  • 3D sound on right → right channel louder