{"id":2485,"date":"2019-08-09T08:57:03","date_gmt":"2019-08-08T23:57:03","guid":{"rendered":"https:\/\/blog.criware.com\/?p=2485"},"modified":"2019-08-09T09:00:00","modified_gmt":"2019-08-09T00:00:00","slug":"audio-occlusion-with-adx2-and-unity","status":"publish","type":"post","link":"https:\/\/blog.criware.com\/index.php\/2019\/08\/09\/audio-occlusion-with-adx2-and-unity\/","title":{"rendered":"Audio Occlusion with ADX2 and Unity"},"content":{"rendered":"<h2 style=\"font-size: 150%; font-weight: bold;\">Introduction<\/h2>\n<p>Occlusion refers to the obstruction of a path between two points. This phenomenon can be calculated in games by drawing a line between two points and detecting whether this line gets broken by an object. This information can then be used to inform the engine to do something, like unloading 3D objects from the world to save on processing. In audio, we can use this information to mimic the effect that objects have on sound sources, by making them appear quieter when they are occluded.<\/p>\n<div style=\"max-width: 800px; margin: 0 auto; margin-bottom: 20px;\">\n<div style=\"width: 800px;\" class=\"wp-video\"><video class=\"wp-video-shortcode\" id=\"video-2485-1\" width=\"800\" height=\"450\" preload=\"metadata\" controls=\"controls\"><source type=\"video\/mp4\" src=\"https:\/\/blog.criware.com\/wp-content\/uploads\/2019\/08\/1908_Occlusion.mp4?_=1\" \/><a href=\"https:\/\/blog.criware.com\/wp-content\/uploads\/2019\/08\/1908_Occlusion.mp4\">https:\/\/blog.criware.com\/wp-content\/uploads\/2019\/08\/1908_Occlusion.mp4<\/a><\/video><\/div>\n<\/div>\n<h2 style=\"font-size: 150%; font-weight: bold;\">Atom Craft<\/h2>\n<p>The first step in setting up our occlusion system is creating an AISAC in Atom Craft. I will be using the same project from the <a href=\"https:\/\/blog.criware.com\/index.php\/2019\/07\/17\/creating-immersive-3d-audio-with-adx2-and-unity-part-2\/\">second part of our 3D audio series<\/a>. We can set up a global <strong>AISAC Control<\/strong> and apply this to any sound which we would want to be affected by occlusion.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/blog.criware.com\/wp-content\/uploads\/2019\/08\/13-1.png\" alt=\"13-1\" width=\"1456\" height=\"292\" class=\"alignnone size-full wp-image-2490\" srcset=\"https:\/\/blog.criware.com\/wp-content\/uploads\/2019\/08\/13-1.png 1456w, https:\/\/blog.criware.com\/wp-content\/uploads\/2019\/08\/13-1-300x60.png 300w, https:\/\/blog.criware.com\/wp-content\/uploads\/2019\/08\/13-1-768x154.png 768w, https:\/\/blog.criware.com\/wp-content\/uploads\/2019\/08\/13-1-1024x205.png 1024w\" sizes=\"auto, (max-width: 1456px) 100vw, 1456px\" \/><\/p>\n<p>To do this, we can create an <strong>AISAC Control<\/strong> named \u201cAISAC_Occlusion\u201d and drawn in a linear graph for both <strong>Volume <\/strong>and <strong>Bandpass \u2013 Cutoff High <\/strong>from 1 to 0. In practical terms, this means that the graph indicates \u201cno occlusion\u201d, to \u201cmaximum occlusion\u201d. This can then be applied to the \u201cboxhum\u201d Cue, and the project can subsequently be built.<\/p>\n<h2 style=\"font-size: 150%; font-weight: bold;\">Unity<\/h2>\n<p>Once we have a <strong>Cri Atom Source <\/strong>script attached to our sound source and a <strong>Cri Atom Listener <\/strong>script attached to the relevant camera, we can add the following script to our audio source to create a simple audio occlusion effect:<\/p>\n<pre>\r\n<code>\r\n\r\nusing System.Collections;\r\nusing System.Collections.Generic;\r\nusing UnityEngine;\r\n\r\npublic class Cri3DOcclusion : MonoBehaviour\r\n{\r\n    \/\/ Gameworld Variables\r\n    Transform listenerTransform;\r\n    public LayerMask OcclusionLayer = 1;\r\n\r\n    \/\/ Internal Variables\r\n    private CriAtomSource atomSource;\r\n\r\n    public float minOcclusion = 0.0f;\r\n    public float maxOcclusion = 0.75f;\r\n    public float transitionTime = 0.5f;\r\n    private float curOcclusion = 1.0f;\r\n\r\n    void Start()\r\n    {\r\n        listenerTransform = FindObjectOfType<CriAtomListener>().transform;\r\n        atomSource = gameObject.GetComponent<CriAtomSource>();\r\n\r\n        atomSource.Play();\r\n    }\r\n\r\n    void Update()\r\n    {\r\n        Physics.Linecast(transform.position, listenerTransform.position, out RaycastHit hit, OcclusionLayer);\r\n\r\n        if (hit.collider.tag == \"Player\")\r\n        {\r\n            Debug.DrawLine(transform.position, listenerTransform.position, Color.blue);\r\n            if (curOcclusion != minOcclusion)\r\n                StartCoroutine(MoveTo(minOcclusion));\r\n        }\r\n        else\r\n        {\r\n            Debug.DrawLine(transform.position, listenerTransform.position, Color.red);\r\n            if (curOcclusion != maxOcclusion)\r\n                StartCoroutine(MoveTo(maxOcclusion));\r\n        }\r\n    }\r\n\r\n    IEnumerator MoveTo(float targetOcclusion)\r\n    {\r\n        curOcclusion = Mathf.MoveTowards(curOcclusion, targetOcclusion, Time.deltaTime \/ transitionTime);\r\n\r\n        atomSource.SetAisacControl(\"AISAC_Occlusion\", curOcclusion);\r\n        yield return null;\r\n    }\r\n}\r\n\r\n\r\n\r\n<\/pre>\n<p><\/code><\/p>\n<h3 style=\"\n    font-size: 110%;\n    font-weight: bold;\n\">Variables<\/h3>\n<p>First, we have our variables. The <strong>LayerMask <\/strong>variable allows us to define what counts as obstruction. Setting this to \u201c1\u201d will give us the \u201cDefault\u201d value, which will work fine for our purposes. You can set this to public to see what the other native values are if you want to find out more about this type.<\/p>\n<p>Next up, we have float variables which are used to control our AISAC. These are set to public so that they can be tweaked in the Inspector as needed.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/blog.criware.com\/wp-content\/uploads\/2019\/08\/13-2.png\" alt=\"13-2\" width=\"592\" height=\"113\" class=\"alignnone size-full wp-image-2491\" srcset=\"https:\/\/blog.criware.com\/wp-content\/uploads\/2019\/08\/13-2.png 592w, https:\/\/blog.criware.com\/wp-content\/uploads\/2019\/08\/13-2-300x57.png 300w\" sizes=\"auto, (max-width: 592px) 100vw, 592px\" \/><\/p>\n<p>Setting the <strong>Max Occlusion<\/strong> to less than 1 means that the sound will still be slightly audible behind cover.<\/p>\n<h3 style=\"\n    font-size: 110%;\n    font-weight: bold;\n\">Start<\/h3>\n<p>In the Start function, we are defining our listener and sound source. We could expose the <em>listenerTransform <\/em>as a public variable and set the listener in the Inspector, but by using the <em>FindObjectOfType <\/em>method, our code becomes much more flexible and efficient, as we can simply drag this script onto any object, without needing to define the listener each time manually.<\/p>\n<p>For the sound source, since it is going to be attached to the same object as our occlusion script, we can simply get the relevant <strong>CriAtomSource<\/strong> component.<\/p>\n<h3 style=\"\n    font-size: 110%;\n    font-weight: bold;\n\">Update<\/h3>\n<p>By using descriptive variable names, I\u2019ve tried to make the Update function as legible as possible.<\/p>\n<ol>\n<li>First, we use the <em>Linecast <\/em>between the position of the sound source, and the position of the listener, which returns a <em>RaycastHit<\/em> (hit).<\/li>\n<li>We then see IF the line from the sound source \u2018hits\u2019 the \u201cPlayer\u201d, and IF the value of our occlusion level is NOT currently equal to the minimum value (the latter IF is an optimisation which prevents the program from updating the AISAC if it does not need to).<\/li>\n<li>Finally, we move from our current occlusion value to the minimum occlusion value.<\/li>\n<\/ol>\n<p>The ELSE portion of this statement is just doing the reverse, which is, if the \u201cPlayer\u201d is NOT hit, then move to a state of maximum occlusion.<\/p>\n<p>Within the IF statements, we can also call the <em>Debug.DrawLine<\/em> method and change its color to display the current occlusion state.<\/p>\n<h3 style=\"\n    font-size: 110%;\n    font-weight: bold;\n\">MoveTo<\/h3>\n<p>The <em>MoveTo <\/em>block is what is known as a Coroutine. This is essentially like an efficient function which works well for updating values over time. Here, we can <em>MoveTowards <\/em>our target value, from our current value, over some time. Since our <em>transitionTime<\/em> variable is public, we can tweak this speed to preference in the Inspector. Finally, the AISAC is updated to reflect the change.<br \/>\n&nbsp;<\/p>\n<h2 style=\"font-size: 150%; font-weight: bold;\">Conclusion<\/h2>\n<p>In just 52 lines of code, and some minor setup in Atom Craft, we have created an efficient and reactive occlusion script. Of course, this is quite a simple implementation of this idea, but it could be expanded to check different surface types or optimised further to virtualise sounds which are occluded. Even concepts such as these would be relatively straight-forward with ADX2!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction Occlusion refers to the obstruction of a path between two points. This phenomenon can be calculated in games by<\/p>\n","protected":false},"author":2,"featured_media":2494,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"colormag_page_container_layout":"default_layout","colormag_page_sidebar_layout":"default_layout","footnotes":""},"categories":[5,7],"tags":[],"class_list":["post-2485","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-adx","category-tutorials"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/blog.criware.com\/index.php\/wp-json\/wp\/v2\/posts\/2485","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.criware.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.criware.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.criware.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.criware.com\/index.php\/wp-json\/wp\/v2\/comments?post=2485"}],"version-history":[{"count":7,"href":"https:\/\/blog.criware.com\/index.php\/wp-json\/wp\/v2\/posts\/2485\/revisions"}],"predecessor-version":[{"id":2497,"href":"https:\/\/blog.criware.com\/index.php\/wp-json\/wp\/v2\/posts\/2485\/revisions\/2497"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.criware.com\/index.php\/wp-json\/wp\/v2\/media\/2494"}],"wp:attachment":[{"href":"https:\/\/blog.criware.com\/index.php\/wp-json\/wp\/v2\/media?parent=2485"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.criware.com\/index.php\/wp-json\/wp\/v2\/categories?post=2485"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.criware.com\/index.php\/wp-json\/wp\/v2\/tags?post=2485"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}