버전

menu_open
Wwise SDK 2023.1.2
리스너 통합하기

소개

리스너는 게임에서 마이크 위치를 나타내는 게임 오브젝트입니다. 리스너를 게임 오브젝트로 지정하면 스피커에 할당된 3D 사운드를 통해 실제 3D 환경과 같이 구현할 수 있습니다. 마찬가지로, 이미터 게임 오브젝트는 가상 스피커를 나타내며, 이 이미터 게임 오브젝트가 리스너에 할당되면 이미터의 위치 정보가 리스너의 좌표 시스템에 매핑되어 3D 사운드를 렌더링합니다. Wwise의 게임 오브젝트는, 이미터로 동작하든 리스너로 동작하든 (혹은 두 경우 모두에 해당하든), 트랜스폼 즉, 위치 벡터나 앞쪽과 위쪽 방향 벡터가 할당됩니다. 게임 오브젝트의 트랜스폼은 반드시 각 프레임마다 업데이트돼야 사운드가 올바른 스피커를 통해 렌더링됩니다.

리스너 등록하기

소리가 들리도록 하기 위해서는 최소 한 개의 게임 오브젝트가 등록되어 리스너로 할당돼있어야 합니다. AK::SoundEngine::SetDefaultListeners 를 사용해 한 리스너를 다른 모든 게임 오브젝트에 할당하거나, AK::SoundEngine::SetListeners 로 한 리스너를 특정 게임 오브젝트에만 할당할 수도 있으며, AK::SoundEngine::SetDefaultListeners 로 설정된 내용을 오버라이드할 수 있습니다. 게임 오브젝트를 등록하고 기본 리스너로 할당하는 방법에 대해 살펴보겠습니다.

AkGameObjectID MY_DEFAULT_LISTENER = 0;
// 주요 리스너를 등록합니다.
AK::SoundEngine::RegisterGameObj(MY_DEFAULT_LISTENER, "My Default Listener");
// 리스너 하나를 기본으로 설정합니다.
AK::SoundEngine::SetDefaultListeners(&MY_DEFAULT_LISTENER, 1);
// 사운드 재생을 위해 게임 오브젝트를 등록합니다.
AkGameObjectID MY_EMITTER = 1;
AK::SoundEngine::RegisterGameObj(MY_EMITTER, "My Emitter");
// 여기서 "My Emitter"는 한 개의 리스너, 즉 "My Default Listener"를 갖습니다. 이를 기본 리스너로 지정해놓았기 때문입니다.
AkGameObjectID MY_LISTENER_NO2 = 2;
AK::SoundEngine::RegisterGameObj(MY_LISTENER_NO2, "My Listener #2");
// "My Emitter"에만 해당하는 리스너로 변경하려면 다음과 같이 처리합니다.
AK::SoundEngine::SetListeners(MY_EMITTER , &MY_LISTENER_NO2, 1);
// 여기서 "My Emitter"는 "My Listener #2"를 갖습니다. 다른 모든 게임 오브젝트들은 여전히 "My Default Listener"를 각각의 리스너로 갖습니다.

Wwise 저작 도구에서 Advanced Profiler의 Emitter-Listener 탭을 확인하면 코드에 할당된 이미터-리스너 관계를 확인할 수 있습니다. 간단한 게임에는 기본 리스너로 하나의 게임 오브젝트만 사용하는 경우가 많지만, 복수의 리스너를 이용해 단일 출력 장치로 출력할 수도 있습니다. 아래 단일 출력 장치에서 여러 리스너가 있는 경우 를 참고하세요. 서브믹스의 3D 위치 지정에도 리스너를 사용할 수 있습니다. 그러기 위해서는 게임 오브젝트에 리스너를 할당해줘야 합니다. 여기서 이 게임 오브젝트는 이미터-리스너 관계로 연결된 게임 오브젝트 그래프를 생성하는 리스너이기도 합니다.

리스너의 위치 정보 설정하기

리스너 위치를 설정할 때에는, 모든 게임 오브젝트와 동일하게 AK::SoundEngine::SetPosition() 함수를 사용합니다. 리스너 위치나 방향 벡터가 변할 때마다 이와 같이 처리돼야 합니다.

AkTransform listenerTransform;
(... 여기에 listenerTransform의 위치와 방향 멤버를 설정합니다...)
AK::SoundEngine::SetPosition( listenerPosition );

AkTransform 클래스에는 게임의 3D 공간에서 리스너의 위치와 방향 정보가 있습니다. 리스너의 위치(Position)인 OrientationFront 및 OrientationTop 벡터는 AkTransform 클래스의 getter와 setter를 이용해 접근할 수 있습니다.

참고: OrientationFront 벡터는 리스너의 머리가 향하는 방향을 정의합니다. OrientationFront 벡터는 OrientationTop 벡터와 직각이 돼야 하며, 리스너 머리의 기울기를 정의합니다. 리스너가 사람인 경우 OrientationFront 벡터는 리스너의 코를 나타내며 (얼굴에서 바깥쪽을 향해 뻗음), OrientationTop 벡터는 리스너의 두 눈 사이 코에서부터 시작해 이마를 지나 수직 방향으로 뻗어 올라가는 값을 나타냅니다.

Wwise 사운드 엔진에서 X, Y, Z를 정의하는 방법에 관한 정보는 X-Y-Z 좌표 시스템 을(를) 참고하세요.

오디오가 올바르게 렌더링되기 위해서는 방향 벡터가 반드시 정의돼있어야 합니다. 이 방향 벡터는 영벡터가 될 수 없으며 반드시 단위 벡터여야합니다. 또한 올바른 각도로 정의돼야 합니다.

참고: 리스너의 위치는 최대 프레임당 한 번 업데이트됩니다. AK::SoundEngine::SetPosition() 함수를 여러번 호출하도록 설정해놔도 AK::SoundEngine::RenderAudio() 가 호출될 때 마지막 값만 처리됩니다.
작은 정보: 예를 들어 왼쪽 스피커에서 나와야 할 소리가 오른쪽 스피커에서 나오는 것과 같이 잘못된 사운드 렌더링이 발생한 경우, 사운드 엔진에 AK::SoundEngine::SetPosition() 함수로 제공된 리스너의 위치 정보를 확인해보세요. 알고 있는 상수 리스너의 위치를 설정하고 렌더링이 올바른지 확인하여, 이런 경우 X, Y, Z 축에서 올바르지 않은 값을 제외시키세요. 이와 관련한 더 자세한 정보는 X-Y-Z 좌표 시스템 을(를) 참고하세요.

3인칭 게임에서 Listener(리스너) 작업하기

3인칭 시점(TPP)을 사용하는 게임이나 시뮬레이션에 오디오를 구현할 때 Listener Game Object를 배치할 위치가 항상 명확한 것은 아닙니다. 어떤 경우는 카메라 위치를 사용하기도 하고, 또 어떤 경우는 플레이어가 제어하는 캐릭터의 위치를 사용하기도 합니다. 각기 다른 위치를 사용한다 하더라도, 카메라든 캐릭터든 그것을 통제하는 '플레이어'가 주체라는 점은 동일합니다. 메인 카메라 Listener와 Distance Probe를 연결시키면 이 두 위치를 각자의 방법으로 사운드 계산에 포함시키게 됩니다. 이 방법을 이해하기 위해서는 구현할 시뮬레이션의 다양한 방면을 분석해야 합니다.

패닝(panning)과 공간화(spatialization)

대부분의 경우, Wwise의 확산(spread)과 초점(focus)을 비롯한 패닝과 공간화는 카메라의 위치와 카메라가 향하고 있는 방향에 기반해야 합니다. 시뮬레이션 안의 카메라와 관련 사운드의 방향(최종 스피커 배열과 관련)이 연결되지 않으면 사운드의 몰입도가 떨어지게 됩니다. 예를 들어, 카메라가 사운드를 똑바로 마주보고 있다면 사운드는 중앙 스피커 채널에서 나와야 합니다. 또한 카메라의 왼쪽 사운드는 왼쪽 스피커에서 나와야하죠.

이와 같은 구현을 위해서는 Listener Game Object가 액티브 카메라에 위치되어야 하고, 이와 일치하도록 Listener의 방향이 업데이트되어야 합니다.

거리 기반 감쇠(attenuation)

3D 사운드는 보통 Emitter와 Listener Game Object 사이 거리에 따라 감쇠되며, 사운드의 감쇠 곡선에 적용되어 볼륨, 하이패스 및 로우패스 필터 값을 구합니다. 결과적으로, 사용자에게 더 중요한 가까운 소리일수록 크게 들리게 됩니다.

하지만 3인칭 시점 게임에서는 주의를 기울여야하는 부분이 카메라가 아니라 플레이어가 제어하는 캐릭터가 됩니다. 이런 이유로 Emitter - 카메라의 거리가 아니라 Emitter - 플레이어 캐릭터의 거리에 따라 소리가 감쇠될 때 사용자의 몰임감이 극대화되는 것이죠.

원리를 이해하기 위해서는, 거리 감쇠가 카메라 위치에 기반한 경우 의도치 않게 볼륨이 높고 낮은 경우를 살펴볼 필요가 있습니다. 3인칭 시점 게임에서 횃불이 켜진 복도를 걸어가는 플레이어 캐릭터를 카메라가 따라가는 경우을 예로 들어보겠습니다. 각 횃불은 빠르게 작아지는 약한 소리를 내고 있습니다. 카메라를 돌려 다른 방향을 바라보려면, 캐릭터 주변으로 카메라가 빙 둘러 돌아야 합니다. 이때 카메라는 하나 이상의 횃불에 가까이 가게 되고, 이에 따라 횃불 소리는 커졌다가 다시 작아집니다. 플레이어 캐릭터는 움직이지 않았고 씬 안에서 횃불의 상대적 중요도도 변하지 않았지만, 횃불의 볼륨 크기 변화는 다른 양상을 띠게 됩니다.

플레이어 캐릭터와의 거리에 따라 감쇠되는 소리를 구현하기 위해서는 Game Object가 플레이어 캐릭터의 위치에 배치되어야 하며 주요 Listener의 Distance Probe로 지정돼야 합니다.

Distance Probe

Distance Probe는 선택적 Game Object로, Listener Game Object에 상응하여 지정됩니다. Distance Probe가 Listener에 할당되면 Listener로 라우팅되는 모든 사운드에 적용되는 감쇠 거리는 Distance Probe와 Emitter Game Object 사이의 거리를 기반으로 합니다.

패닝(Panning), 공간화(spatialization), 확산(spread) 및 초점(focus)은 Distance Probe가 할당되었는지 여부에 관계 없이 항상 Listener Game Object의 위치와 방향을 기반으로 합니다.

추가 정보:

  • 각 Listener Game Object는 Distance Probe로 설정된 0개 또는 1개의 Game Object를 가질 수 있습니다.
  • 하나의 Game Object는 여러 Listener에 대한 Distance Probe가 될 수 있습니다.
  • Listener를 리스너 자신의 Distance Probe로 지정하면 Distance Probe를 없음으로 설정하는 것과 같습니다.

Distance Probe는 AK::SoundEngine::SetDistanceProbe API를 이용해 Listener Game Object에 할당됩니다.

Distance Probe 프로파일링

모든 할당된 Distance Probe는 Advanced Profiler의 Listener 탭에 나타납니다. 다음 그림은 'Distance Probe'라는 게임 오브젝트가 AK::SoundEngine::SetDistanceProbe API를 이용해 'Listener L'에 할당된 것을 보여줍니다.

해당 Distance Probe는 Game Object 3D Viewer에서 아이콘 형태로 나타납니다. 편의성을 위해 Game Object 3D Viewer에 뜨는 Distance Probe는 Listener가 어떻게 표시되는지를 따르게 돼있습니다. 즉, Listener에 적용된 모든 필터는 Distance Probe에도 동일하게 적용됩니다.

추가 구현 정보

Distance Probe는 3인칭 시점에서 반드시 플레이어 캐릭터와 똑같은 위치에 놓일 필요는 없습니다. 원하는 결과를 얻을 때까지 다양한 위치 지정을 시험해보세요. 다음과 같은 경우를 시험해볼 수 있습니다. 카메라와 캐릭터 사이에서 다양한 비율로 Distance Probe를 배치시켜봅니다. 이 비율은 디자이너가 캐릭터 위치와 카메라 위치 사이를 보간하는 값을 조정할 때 사용할 수 있습니다. 컷씬이나 시네마틱 장면에서는 다른 Game Object로 Distance Probe를 이동시키거나 끄거나 옮겨야할 수 있습니다. Distance Probe는 고정된 스태틱이 아니어도 됩니다.

Distance Probe가 Spatial Audio Listener에 할당됐을 때 다양한 Spatial Audio 기능이 어떻게 작동하는지 자세히 알아보려면 3인칭 시점과 Spatial Audio(공간 음향) 를 참고하세요.

단일 출력 장치에서 여러 리스너가 있는 경우

게임 화면이 언제나 하나의 시점만 보여주는 싱글 플레이어 게임에는 리스너 한 개면 충분합니다. 그러나 여러 플레이어가 한 시스템에서 게임을 플레이할 경우나 동시에 여러 시점을 보여주는 경우, 각 시점에는 각각의 리스너가 따로 있어야 그에 맞는 오디오를 적절히 렌더링할 수 있습니다.

여러 리스너를 구현할 때 가장 어려운 점은, 음원의 위치가 플레이어가 보는 내용과 항상 일치하지는 않는다는 점입니다. 여러 플레이어에 대해 3D 환경을 재현하는데 스피커 세트를 하나만 사용하는 게임에서 주로 이런 어려움이 발생합니다.

다음 그림은 이런 문제를 간략히 나타내고 있습니다. Listener 0에게는 왼쪽 스피커에서 음원이 들려야하는 반면 Listener 1에게는 오른쪽에서 들려야하기 때문에 이 음원이 어느 스피커에서 재생돼야 하는지 결정하기가 매우 어렵습니다.

그림: 동일한 음원을 서로 다른 스피커를 통해 듣는 두 리스너

Wwise는 리스너 숫자에 제한이 없으며, 기본 설정으로 모든 리스너가 주요 출력 장치에서 혼합됩니다. 단, 다음의 경우는 예외가 됩니다.

다음 섹션은 모든 리스너가 동일한 출력 장치로 병합되는 경우와, 프로그래머가 Wwise 사운드 엔진을 이용해 이 리스너들로 원하는 동작을 만들어내는 과정을 다루고 있습니다.

참고: 복수의 리스너와 관련된 모든 작업은 게임 프로그래머가 SDK를 이용해 구현해야 합니다. Wwise 저작 애플리케이션에는 복수의 리스너에 대해 게임 내 음원의 위치를 관리하는 특별한 옵션이 없습니다.

복수의 리스너: 음원 캡처하기

각 리스너는 믹싱 그래프를 스폰합니다. 각 음원과 거리, 원뿔 감쇠(cone attenuation)는 각 활성화된 리스너에 따라 개별적으로 계산됩니다.

볼륨 감쇠 관리

여러 리스너가 음원을 캡처할 때, 그 음원은 각각의 리스너에 해당하는 각 버스 인스턴스에 연속적으로 믹싱됩니다. 믹싱되면 해당 감쇠 볼륨이 각 리스너에 개별적으로 적용됩니다.

LPF 감쇠 관리

감쇠 볼륨과는 반대로, 감쇠 LPF와 HPF는 음원에 직접 적용됩니다. 따라서 Wwise는 주어진 음원의 모든 이미터-리스너 관계를 기반으로 하나의 값을 선택해야 합니다. 다음은 사운드 엔진이 최종 Low Pass Filer(저역 통과 필터)를 각 음원에 계산해 넣는 과정입니다.

  1. 해당 음원이 활성화돼있는 각 리스너에 대해
    1. 음원과 리스너 사이 거리를 기반으로 LPF를 계산합니다.
    2. 음원과 리스너 사이 각도를 기반으로 LPF를 계산합니다.
    3. 두 값 중 더 큰 값을 남겨둡니다.
  2. 모든 리스너들 중 가장 낮은 값을 취해 오브젝트의 LPF를 추가합니다 (일반값 및 RTPC).

다음 표에 나온 자세한 예시를 보면, 리스너 0의 값은 max( 10, 40 ) = 40이고, 리스너 1의 값은 max( 50, 10 ) = 50입니다. 두 값 중 가장 낮은 값은 40이므로 여기에 오브젝트 값 5를 더하면 최종 값 45가 나옵니다.

리스너 0
리스너 1
오브젝트
음원에 대한
최종 LPF
원뿔 LPF
반경 LPF
원뿔 LPF
반경 LPF
10 40 50 10 5 45

볼륨 상쇄와 공간화

3D 공간화(Spatialization)는 리스너와 관련된 사운드의 위치 정보를 기반으로 다양한 스피커에 소리를 패닝합니다.

그러나 두 플레이어가 게임 화면을 둘로 나눠 플레이하는 경우, 왼쪽 스피커에서 리스너 1(첫 번째 플레이어)의 소리를 내고 오른쪽 스피커에서는 리스너 2(두 번째 플레이어)의 소리를 내야 할 수 있습니다. 즉, 각 리스너의 위치에 따라 스피커에 대한 사운드의 일반적인 위치 지정을 사용하지 않을 수 있습니다.

Wwise는 제어의 폭을 넓히기 위해, 게임 프로그래머가 주어진 리스너에 대한 공간화(spatialization)를 비활성화할 수 있게 했으며, 필요한 경우 각 채널에 사용자 지정 볼륨 상쇄를 설정할 수 있어 해당 리스너가 캡처하는 사운드가 각 스피커에서 어떻게 들릴 것인지를 지정할 수 있습니다.

이러한 설정은 AK::SoundEngine::SetListenerSpatialization() 를 호출해 각각의 리스너에 대해 변경할 수 있습니다.

AkGameObjectID in_uListenerID, // 리스너 게임 오브젝트 ID
bool in_bSpatialized, // 공간화 토글 (True : 공간화 활성화, False : 공간화 비활성화)
AkChannelConfig in_channelConfig, // in_pVolumeOffsets의 볼륨과 연관된 채널 환경 설정. in_pVolumeOffsets이 NULL이면 무시함.
AK::SpeakerVolumes::VectorPtr in_pVolumeOffsets = NULL // 스피커당 볼륨 상쇄 (단위: dB). 이 벡터를 다루는 방법은 AkSpeakerVolumes.h 를 확인하세요.
) = 0;

첫 번째 매개 변수는 리스너 ID입니다. 두 번째 매개 변수는 True 로 설정해야 해당 리스너에 대한 공간화를 활성화하며, 반대로 False 는 비활성화합니다. 끝으로, 마지막 두 매개 변수는 해당 리스너의 각 채널에 대한 감쇠를 갖고 있는 벡터를 dB로 나타냅니다. in_bSpatializedFalse 이면 각 채널의 볼륨을 기본 0 dB로 설정합니다. in_bSpatializedTrue 이면 기본 3D 공간화 계산으로 각 채널에 주어진 값에 의해 계산된 볼륨을 상쇄합니다.

볼륨 벡터는 채널 환경 설정 in_channelConfig 에 따라 좌우됩니다. 만약 in_channelConfig 가 5.1이면 볼륨 벡터는 6 개의 값을 갖습니다. AK::SpeakerVolumes::Vector 네임스페이스에 정의된 함수를 이용해 볼륨 벡터를 설정하세요. 채널 순서는 AkSpeakerConfig.h 에 정의된 채널 마스크 비트 순서대로입니다. 단, LFE는 항상 맨 뒤에 옵니다.

두 플레이어가 한 화면을 분할해서 사용하는 경우, 프로그래머는 다음 코드를 사용할 수 있습니다.

// 모든 이미터에 대해 리스너 1과 2를 기본 리스너로 등록합니다.
// 만약 AK::SoundEngine::SetListeners 를 사용하면 어느 이미터가 어느 리스너에게 소리를 방사할 지 명시적으로 선택할 수 있습니다.
AkGameObjectID listeners[2] = {1,2};
AK::SetDefaultListeners(listeners[0],2);
// (플랫폼이 지원하는 경우) 7.1 스피커 설정을 이용해 스피커 상쇄를 정의합니다.
vVolumes[0] = 0.f; // 왼쪽
vVolumes[1] = -96.3f; // 오른쪽
vVolumes[2] = -6.f; // 중앙
vVolumes[3] = 0.f; // 왼쪽 뒤
vVolumes[4] = -96.3f; // 오른쪽 뒤
vVolumes[5] = 0.f; // 왼쪽 측면
vVolumes[6] = -96.3f; // 오른쪽 측면
vVolumes[7] = 0.f; // LFE
AK::SoundEngine::SetListenerSpatialization( listeners[0], false, cfg, vVolumes );
vVolumes[0] = -96.3f; // 왼쪽
vVolumes[1] = 0.f; // 오른쪽
vVolumes[2] = -6.f; // 중앙
vVolumes[3] = -96.3f; // 왼쪽 뒤
vVolumes[4] = 0.f; // 오른쪽 뒤
vVolumes[5] = -96.3f; // 왼쪽 측면
vVolumes[6] = 0.f; // 오른쪽 측면
vVolumes[7] = 0.f; // LFE
AK::SoundEngine::SetListenerSpatialization( listeners[1], false, cfg, vVolumes );

사운드가 라우팅된 버스가 7.1이 아닌 다른 채널 환경 설정으로 돼있는 경우, 사용자가 정의한 채널 환경 설정에 따라 벡터가 사운드에 적용되기 전에 기본 다운믹스 방식을 이용해 내부적으로 다운믹스됩니다.

일반 공간화로 돌아가려면 다음을 호출하세요.

// 리스너 0와 1에 대해 일반 공간화를 활성화합니다

볼륨 작업 과정

아래 그림은 각 스피커의 최종 볼륨을 계산하기 위해 각 리스너에 대한 모든 음원에 적용되는 다양한 작업을 순서대로 보여줍니다.

그림: Wwise 사운드 엔진의 볼륨 작업 과정
참고
AKSOUNDENGINE_API AKRESULT SetDefaultListeners(const AkGameObjectID *in_pListenerObjs, AkUInt32 in_uNumListeners)
AkUInt32 uNumChannels
Number of channels.
AkUInt64 AkGameObjectID
Game object ID
Definition: AkTypes.h:142
AKRESULT
Standard function call result.
Definition: AkTypes.h:213
AKSOUNDENGINE_API AKRESULT RegisterGameObj(AkGameObjectID in_gameObjectID)
AkForceInline AkUInt32 GetRequiredSize(AkUInt32 in_uNumChannelsIn, AkUInt32 in_uNumChannelsOut)
Compute size (in bytes) required for given channel configurations.
#define NULL
Definition: AkTypes.h:46
AkReal32 * VectorPtr
Volume vector. Access each element with the standard bracket [] operator.
#define AkAlloca(_size_)
Stack allocations.
AKSOUNDENGINE_API AKRESULT SetListenerSpatialization(AkGameObjectID in_uListenerID, bool in_bSpatialized, AkChannelConfig in_channelConfig, AK::SpeakerVolumes::VectorPtr in_pVolumeOffsets=NULL)
#define AK_SPEAKER_SETUP_7_1
AKSOUNDENGINE_API AKRESULT SetListeners(AkGameObjectID in_emitterGameObj, const AkGameObjectID *in_pListenerGameObjs, AkUInt32 in_uNumListeners)
AKSOUNDENGINE_API AKRESULT SetPosition(AkGameObjectID in_GameObjectID, const AkSoundPosition &in_Position, AkSetPositionFlags in_eFlags=AkSetPositionFlags_Default)
AkForceInline void SetStandard(AkUInt32 in_uChannelMask)
Set channel config as a standard configuration specified with given channel mask.
uint32_t AkUInt32
Unsigned 32-bit integer
Position and orientation of objects in a "local" space
Definition: AkTypes.h:613

이 페이지가 도움이 되었나요?

지원이 필요하신가요?

질문이 있으신가요? 문제를 겪고 계신가요? 더 많은 정보가 필요하신가요? 저희에게 문의해주시면 도와드리겠습니다!

지원 페이지를 방문해 주세요

작업하는 프로젝트에 대해 알려주세요. 언제든지 도와드릴 준비가 되어 있습니다.

프로젝트를 등록하세요. 아무런 조건이나 의무 사항 없이 빠른 시작을 도와드리겠습니다.

Wwise를 시작해 보세요