Occlusion with Resonance audio

Hi,

For some reason it seems occlusion does not work when using Resonance audio with FMOD: I have set a room in Unity with a blocking wall, put a sound source with a studio event emitter, and a studio listener on my camera. Within FMOD I have one sound using Resonance Audio source with occlusion, and a Resonance audio listener on the master bus, prefader. Now when I start the game I do get immersive sound when moving around the audio source, but no occlusion when I get behind the wall. I should add that I do get occlusion when using Resonance audio directly inside Unity, so I suppose the feature should be working within FMOD as well - otherwise why put it?

One more thing, Unity considers the source as 2D panning for some reason (as soon as I put a Resonance Audio source instead of, say, an FMOD object spatializer). Can it have anything to do with my issue?

I am using FMOD Studio 1.10.09
Thank you for all you do, FMOD is truly inspiring!
Yann

So basically my question is: has anyone managed to make occlusion function within Unity using FMOD and Resonance Audio? And if so, does it include something I forgot? For instance there is no on/off button for occlusion inside the FMOD plugin, so maybe it is deactivated and is accessible somewhere else?

In FMOD the Events occlusion needs to be driven by a parameter using Automation.
https://fmod.com/resources/documentation-studio?page=/authoring-events.html#automation-and-modulation

You will need to create some custom scripts to control the amount of occlusion, similar to what would be used for the Unity-Resonance occlusion, but it isn’t something that is automatic.

So to make sure it means the occlusion knob is not functional by default on the Resonance Audio Source plugin within FMOD? But why include it in the documentation without a word of warning then? Something like “be aware that this function alone can’t be activated as such and requires scripting”. Thank you very much for your answer, highly appreciated!

The occlusion knob works just fine! It’s like any other effect property: It controls the amount of occlusion applied by the effect, and changing the value of the knob changes the amount of occlusion that’s applied. If you want to change the value of the occlusion knob dynamically, add automation to that knob, and use a parameter to control its value via that automation.

Unfortunately, there’s no way for FMOD Studio to magically know how much occlusion you want to affect any given event: No game engine tells FMOD Studio when there’s an obstacle between the listener and the event emitter by default, and even if game engines did tell us that information, FMOD Studio wouldn’t know how much occlusion that particular obstacle was supposed to apply.

If you want obstacles in your game world to occlude events in a dynamic way, your game’s code must track the relative positions of event emitters and occluding obstacles relative to the listener, determine when occlusion should occur and how much occlusion should be applied when it does, and then pass that information to FMOD Studio by setting the value of the parameter that controls your event’s occlusion.

2 Likes

ok that is clear now, I guess I was confused because this works automatically in Unity without having to use any ray casting or such: one just sets an occlusion value, which is only applied when an object gets in front of the direct source. So that there is no automation needed, same as near-field gain is set but can only be felt once an object gets near. I suppose that makes quite a difference with the FMOD version of the plug then, and I will now look into it with the useful info you provided. Thank you very much!

Hello again, now I have followed your advice and managed to use a linecast in order to get occlusion to work, so first thank you very much, BUT… two things: the amount of occlusion, when pushed to the max (10), is roughly equivalent to a value of 1 when the Resonance Audio plugin is directly used within Unity. And mostly, inside Unity two obstacles (like two walls) occlude more than one, meaning there is a huge difference in the way this is calculated, and this without any programming. So that, unless I am mistaken, the occlusion feature within the FMOD version of the Resonance Audio Source plugin is very different than the original one. I mean, I suppose there is a way to extend a linecast in order to detect multiple obstacles, but still the max occlusion value would be the same (10), so there would not be any audible difference. If anybody else has made any comparisons I am very much interested, as I am trying to get the best of both worlds - and again, thank you !

I know this post is a little bit old, but for anyone else who comes across this, you may find this page useful for adding an occlusion automation script for FMOD-Resonance with Unity.

https://alessandrofama.com/tutorials/fmod-unity/occlusion/#Preparation_in_FMOD_Studio

1 Like

I feel like I owe Alessandro a commission for all the time and anguish his tutorials have saved me.

3 Likes

thank you @super8ude, you are too kind :smiley:

1 Like

Hello! I’m getting an error following the tutorial posted by @Glitchbyte (@alexzzen) - basically when I try to edit the StudioListenerEditor script I get the following error:

Assets/Plugins/FMOD/src/Editor/StudioListenerEditor.cs(28,145): error CS0103: The name ‘InternalEditorUtility’ does not exist in the current context

Any ideas on how to fix this?

Thank you!

@MA_ORDON It looks like you’re getting a compiler error since the script can’t see ‘Internal Editor Utility’. Depending on your version of Unity it may have a different name for the Unity Editor now.
For Unity 2020.3.15 it’s using UnityEditorInternal

at the top make sure it says using UnityEditorInternal; if it’s a similar Unity version.

This is how I have the code inserted in StudioListenerEditor.cs

using UnityEditor;
using UnityEditorInternal;
namespace FMODUnity
{
    [CustomEditor(typeof(StudioListener))]
    [CanEditMultipleObjects]
    public class StudioListenerEditor : Editor
    {
        public SerializedProperty attenuationObject;

        private void OnEnable()
        {
            attenuationObject = serializedObject.FindProperty("attenuationObject");
        }

        /// <summary>
        /// Occlusion modification allows raycast to check which layers should be occluded
        /// </summary>
        public override void OnInspectorGUI()
        {
            serializedObject.Update();
            EditorGUI.BeginDisabledGroup(true);
            var index = serializedObject.FindProperty("ListenerNumber");
            EditorGUILayout.IntSlider(index, 0, FMOD.CONSTANTS.MAX_LISTENERS - 1, "Listener Index");
            EditorGUI.EndDisabledGroup();
            
            //Begin Occlusion Mod
            EditorGUI.BeginChangeCheck();
            var mask = serializedObject.FindProperty("occlusionMask");
            int temp = EditorGUILayout.MaskField("Occlusion Mask", InternalEditorUtility.LayerMaskToConcatenatedLayersMask(mask.intValue), InternalEditorUtility.layers);
            mask.intValue = InternalEditorUtility.ConcatenatedLayersMaskToLayerMask(temp);

            if (EditorGUI.EndChangeCheck())
                serializedObject.ApplyModifiedProperties();
            //End Occlusion Mod

            EditorGUILayout.PropertyField(attenuationObject);
            serializedObject.ApplyModifiedProperties();
        }
    }
}

I hope this helps

Hi @Glitchbyte ! Thank you for your help! This solved the error. I’m now getting another error when I try to create the AudioOcclusion script, specifically: Assets/AudioOcclusion.cs(46,51): error CS0103: The name ‘FmodResonanceAudio’ does not exist in the current context

Do you know whats going on here? I really appreciate your help!

Seems like a similar error and I need to add something to the top of the script but because of my lack of knowledge in scripting I just can’t figure out what that is…

Yeah, after one of the FMOD updates, I noticed it stopped working since FMOD no longer made some scripts accessible from outside their code, so instead I inserted the occlusion script directly into the
StudioEventEmitter.cs script right at the bottom of the script below the IsPlaying() function.

        /// <summary>
        /// Checks occlusion value returned by layers hit with raycast on update
        /// </summary>
        //Begin Occlusion MOD
        void Update()
        {
            if (instance.isValid())
            {
                if (!occlusionEnabled)
                {
                    currentOcclusion = 0.0f;
                }
                else if (Time.time >= nextOcclusionUpdate)
                {
                    nextOcclusionUpdate = Time.time + occlusionDetectionInterval;
                    currentOcclusion = occlusionIntensity * ComputeOcclusion(transform);
                    instance.setParameterByName(occlusionParameterName, currentOcclusion, false);
                    //Debug.Log("Value to FMOD =" + currentOcclusion);
                }
            }
        }

        public static Transform ListenerTransform
        {
            get
            {
                if (listenerTransform == null)
                {
                    var listener = GameObject.FindObjectOfType<StudioListener>();
                    if (listener != null)
                    {
                        listenerTransform = listener.transform;
                    }
                }
                return listenerTransform;
            }
        }
        private static Transform listenerTransform = null;

        // Pre-allocated raycast hit list for occlusion computation.
        private static RaycastHit[] occlusionHits = new RaycastHit[maxNumOcclusionHits];

        public static float ComputeOcclusion(Transform sourceTransform)
        {
            var listener = GameObject.FindObjectOfType<StudioListener>();
            occlusionMaskValue = listener.occlusionMask;
            float occlusion = 0.0f;
            if (ListenerTransform != null)
            {
                Vector3 listenerPosition = ListenerTransform.position;
                Vector3 sourceFromListener = sourceTransform.position - listenerPosition;
                int numHits = Physics.RaycastNonAlloc(listenerPosition, sourceFromListener, occlusionHits,
                                                  sourceFromListener.magnitude, occlusionMaskValue);
                //Debug.Log("Hits =" + numHits);

                for (int i = 0; i < numHits; ++i)
                {
                    if (occlusionHits[i].transform != listenerTransform &&
                        occlusionHits[i].transform != sourceTransform)
                    {
                        occlusion += 1.0f;
                    }
                }
                //Debug Raycast
                /*RaycastHit hit;
                if (Physics.Raycast(listenerPosition, sourceFromListener, out hit, Mathf.Infinity, occlusionMaskValue))
                {
                    Debug.DrawRay(listenerPosition, sourceFromListener * hit.distance, Color.yellow);
                    Debug.Log("Did Hit");
                }
                else
                {
                    Debug.DrawRay(listenerPosition, sourceFromListener * 1000, Color.white);
                    Debug.Log("Did not Hit");
                }
                */
            }
            return occlusion;
        }

        /// Maximum allowed number of raycast hits for occlusion computation per source.
        public const int maxNumOcclusionHits = 10;

        /// Occlusion layer mask.
        private static int occlusionMaskValue = -1;

        /// Source occlusion detection rate in seconds.
        public const float occlusionDetectionInterval = 0.2f;
        //End Occlusion Mod
    }

Also at the top of the script, right after public float OverrideMaxDistance = -1.0f;
I added the section with

//Occlusion Mod Begin
        public bool occlusionEnabled = false;
        public string occlusionParameterName = null;
        [Range(0.0f, 10.0f)]
        public float occlusionIntensity = 1f;
        public float currentOcclusion = 0.0f;
        public float nextOcclusionUpdate = 0.0f;
        //Occlusion Mod End

Basically instead of using the AudioOcclusion.cs component, I’m just using it through the fmod event emitter

Thank you so much @Glitchbyte ! This did the trick - now i have audio occlusion working :slight_smile:

1 Like

I’ve updated the tutorial, thanks @Glitchbyte

2 Likes