본문으로 바로가기

7장 Audio - ② FMOD를 활용한 3D 위치 기반 오디오

오디오는 게임에서 중요한 역할을 차지한다. 게임 플레이 상황에 대한 오디오 신호를 제공한다든지 게임의 전반적인 분위기를 강화하는 역할 등 퀄리티 높은 사운드는 게임에 많은 생명력을 부여한다. 이번 장에서는 오디오 관련 강력한 기능을 제공하는 FMOD API를 활용하여 게임에서 다양한 오디오를 추가하는 방법을 다룬다.

💡 7장의 목차

  • 오디오 구축하기
  • 3D 위치 기반 오디오
  • 믹싱 및 이펙트

3D 위치 기반 오디오

3D 게임에서 대부분의 사운드 효과음은 위치에 기반한다. 유저가 벽난로를 향해서 걷고 있다면 벽난로의 소리가 앞에서 들려와야 하고, 벽난로를 등지고 걷는다면 벽난로 소리가 뒤에서 들려야와야한다. 만약 벽난로와 거리가 멀어진다면 감쇠함수에 따라 소리는 더욱 작아져야 한다.

위치 기반 오디오를 사용하기 위해서는 3개 이상으로 구성된 서라운드 사운드 시스템이 필요하다. 일반적으로는 5.1채널 구성의 사운드 채널을 사용한다.

▲ 5.1 채널 입체 음향 구성 예시

  • SW: 서브 우퍼
  • FL: 정면-왼쪽
  • C: 정면-중심
  • FR: 정면-오른쪽
  • SR: 후면 오른쪽
  • SL: 후면 왼쪽

리스너 설정

FMOD는 위치 기반 오디오 지원을 내장하고 있다. 이 기능을 활용하기 위해서는 리스너를 설정 해야한다. 리스너는 게임 속에서 소리를 듣는 가상의 마이크로폰 이다. 일반적으로는 카메라를 리스너로 사용한다. (물론 3인칭 카메라에서는 다른 방법을 사용해야한다) 카메라(리스너)의 위치와 방향 정보뷰 행렬의 역행렬로 구할 수 있다.

리스너를 설정하기 위해서는 위치와 방향 속도가 필요하다. 위치는 대상 사운드 이벤트와의 거리를 계산하며, 방향은 대상 음원이 위치한 방향을 계산하기 위해서 사용된다. 그리고 방향은 3차원이기 때문에 정방 벡터와 상향 벡터가 필요하다. 마지막으로 속도는 도플러 효과를 구현하기 위해서 사용된다. 리스너의 설정은 다음과 같이 한다.

void AudioSystem::SetListener(const Matrix4& viewMatrix)
{
    // 카메라의 위치는 뷰 행렬의 역행렬에서 구할 수 있다.
    Matrix4 invView = viewMatrix;
    invView.Invert();
    FMOD_3D_ATTRIBUTES listener;
    // 리스너의 위치와 전방 벡터, 상향 벡터를 설정
    listener.position = VecToFMOD(invView.GetTranslation());
    // 뷰 행렬의 역행렬에서 세 번째 행은 전방 벡터
    listener.forward = VecToFMOD(invView.GetZAxis());
    // 뷰 행렬의 역행렬에서 두 번째 행은 상향 벡터
    listener.up = VecToFMOD(invView.GetYAxis());
    // 속도를 0으로 설정 (도플러 효과를 사용할 시 수정)
    listener.velocity = { 0.0f, 0.0f, 0.0f };
    // FMOD로 보낸다 (0 = 리스너는 하나)
    mSystem->setListenerAttributes(0, &listener);
}

사운드 이벤트에 위치기반 기능 추가

사운드 이벤트는 자신의 세계 위치와 방향을 나타내는 3D 속성을 가질 수 있다. 사운드 아티스트가 FMOD 스튜디오에서 사운드 이벤트를 생성할 때 2D혹은 3D로 설정 할 수 있다. 만약 3D 사운드 이벤트라면 다음과 같이 사운드 이벤트의 위치, 정방 벡터, 상향 벡터, 속도를 설정하여 줄 수 있다.
사운드 이벤트의 위치와 방향정보는 엑터의 세계변환행렬에서 구할 수 있다. 즉, 엑터의 위치, 회전을 그대로 사용한다.

void SoundEvent::Set3DAttributes(const Matrix4& worldTrans)
{
    auto event = mSystem ? mSystem->GetEventInstance(mID) : nullptr;
    if (event)
    {
        FMOD_3D_ATTRIBUTES attr;
        // Set position, forward, up
        attr.position = VecToFMOD(worldTrans.GetTranslation());
        // 세계 공간에서 첫 번째 행은 전방 벡터
        attr.forward = VecToFMOD(worldTrans.GetXAxis());
        // 세 번째 행은 상향 벡터
        attr.up = VecToFMOD(worldTrans.GetZAxis());
        // 속도를 0으로 설정 (도플러 효과를 사용한다면 수정)
        attr.velocity = { 0.0f, 0.0f, 0.0f };
        event->set3DAttributes(&attr);
    }
}

3인칭 게임상의 리스너

카메라 위치와 방향을 직접 사용하는 리스너는 카메라가 플레이어 캐릭터의 시점에 있는 1인칭 게임에서는 잘 동작한다. 하지만 아래와 같이 카메라가 유저의 뒤를 따라가는 3인칭 게임에서는 간단하지 않다.

▲ 3인칭 게임에서의 사운드 이펙트

 

위와 같은 상황에서는 플레이어는 A 위치에서 발생한 소리가 가장 크게 들려야 한다. 그리고 B에서 발생한 소리는 상대적으로 작게들려야 할것이다. 그러나 카메라를 리스너로 설정하게 될 경우 플레이어와 거리가 먼 B의 소리가 더 크게 들리고, A 소리는 거의 들리지 않을 수도 있다.
반대로 플레이어를 리스너로 설정하게 되면 B 소리가 마치 뒤에서 들리는듯할 것이다. 하지만, B는 화면 정면에 있는 소리인데도 뒤에서 소리가 들리면 어색하게 들릴 수 밖에 없다.
이 문제를 개선하여 3인칭 시점에서 자연스러운 소리를 연출하기 위해는 소리의 감쇠는 플레이어 위치를 기반으로, 소리의 방향은 카메라를 기준으로 만들어야 한다. \(P\)가 플레이어의 위치, \(C\)가 카메라의 위치, \(S\)가 사운드의 위치일 때 우리가 기대하는 가상의 사운드 위치 \(VirtualPos\)는 다음과 같다.

\[PlayerToSound = S - P\]

\[CameraToSound = S - C\]

\[VirtualPos = ||PlayerToSound|| \frac{CameraToSound}{||CameraToSound||}\]

도플러 효과

거리에서 경찰차가 다가올때는 사이렌 소리의 피치가 증가하여  반대로 경찰차가 지나가면 소리의 피치가 감소하는 현상이 발생한다. 이를 도플러 효과라고 한다.

▲ 도플러 효과 (출처: 위키피디아)

게임에서 도플러 효과는 차량 같이 이동하는 오브젝트에 사실적인 효과음을 만들어낸다. FMOD는 자동적으로 도플러 이동에 따른 피치를 계산한다. setListenerAttributesset3DAttributes에 올바른 속도를 넘겨주는 것 만으로 FMOD가 제공해주는 도플러 효과를 사용할 수 있다. 저수준 API를 통해서도 도플러 효과를 사용할 수 있는데 저수준 API로 이를 사용하기 위해서는 다음과 같이 접근할 수 있다.

mLowLevelSystem->set3DSettings(
    1.0f,	// 도플러 스케일, 1 = 정상, 1보다 더 크면 과장된 소리를 낸다
    50.0f,	// 게임 단위의 크기 = 1미터
    1.0f	// (도플러와 관계없는 매개변수. 1로 남겨둔다.)
);

출처:

에이콘 출판 <Game Programming in C++>