TJHeuvel
TJHeuvel

Reputation: 12618

Web Audio Spatialization based on view direction

I'm trying to modulate the volume of some sound based on the view direction between the camera and the sound. So if you are fully looking at the sound source the volume is 100%, if you turn away it is turned down.

Setting the built-in directionalCone, which links to the Panner Audio API is not what i want. This defines if audio is enabled while the player is positioned inside the cone, i'd like to to work based on the view direction.

I have something working in Aframe, by doing a dot between the camera view direction and direction between the player and audio clip. However this (for some reason) is quite expensive, i'm wondering if there is some built in functionality that i am overlooking.

tick: function() {
        if(!this.sound.isPlaying) return; //todo: this is true even outside the spatial distance!

        var camFwd = this.camFwd;
        this.camera.object3D.getWorldPosition(camFwd);

        var dir = this.dir;     
        this.el.object3D.getWorldPosition(dir);

        dir.subVectors(
            camFwd, //camera pos
            dir     //element pos
            ).normalize();

        this.camera.object3D.getWorldDirection(camFwd);

        var dot = THREE.Math.clamp(camFwd.dot(dir), 0, 1);

//float dot = Mathf.dot(transform.forward, (camTrans.position-transform.position).normalized);

        this.setVolume(THREE.Math.lerp(
            this.data.minVolume, 
            this.data.maxVolume, 
            dot));
    }, 

This gives the intended effect, but it shows up in the performance profiler as quite expensive. Especialy the getWorldDirection for some reason is costly, eventhough the hierarchy itself is simple.

Upvotes: 0

Views: 115

Answers (1)

Mugen87
Mugen87

Reputation: 31076

Especialy the getWorldDirection for some reason is costly

Object3D.getWorldPosition() and Object3D.getWorldDirection() always force a recomputation of the object's world matrix. Depending on the time when tick is executed, it is sufficient to do this:

camFwd.setFromMatrixPosition( this.camera.object3D.matrixWorld );
dir.setFromMatrixPosition( this.el.object3D.matrixWorld );

The code just extracts the position from the world matrix without updating it. You can use a similar approach for the direction vector although the code is a bit more complex:

var e = this.camera.object3D.matrixWorld.elements;
camFwd.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize();

Upvotes: 1

Related Questions