August 9th, 2008, 04:59 PM

3D sound, calculating pan
Anyone out there good with 3D math? Unfortunately, I am definitely not.
I need a formula to calculate pan, a value between 1 and 1.
1 is full sound on left speaker
0 is equal sound on both speakers
1 is full sound on right speaker
Given values are:
The source's 3D position
The listener's 3D position
The "lookat" direction (a normal vector)
The "up" direction (also normal)
Any help with this would be greatly appreciated. I know a number of computer languages, so it doesn't matter what language you write in, I am mainly just looking for the math.
August 22nd, 2008, 03:02 AM

I'm not sure that there are any free algorithms for placement of sounds in a 3D space for stereo, it'd be really great to know if you've found any, but there are the usual panning laws for placing sound in stereo. Just google for those.
August 22nd, 2008, 03:04 AM

Oh in which case just project your sound in 3D space to the horizontal plane of the camera and use those panning laws.
August 31st, 2008, 11:59 AM

Solution
For anyone who is interested, I found a couple of friendly mathematicians who helped me come up with the following formula (written in Java):
Code:
side = listenerUpVector.cross( listenerLookAtVector );
side.normalize();
float x = sourcePositionVector.dot( sourcePositionVector.subtract( listenerPositionVector ), side );
float z = sourcePositionVector.dot( sourcePositionVector.subtract( listenerPositionVector ), listenerLookAtVector );
float angle = (float) Math.atan2( x, z );
pan = (float)  Math.sin( angle );
The above code uses the following Vector3D class:
Code:
public class Vector3D
{
/**
* The vector's X coordinate.
*/
public float x;
/**
* The vector's Y coordinate.
*/
public float y;
/**
* The vector's Z coordinate.
*/
public float z;
/**
* Constructor: Places the vector at the origin.
*/
public Vector3D()
{
x = 0.0f;
y = 0.0f;
z = 0.0f;
}
/**
* Constructor: Places the vector at the specified 3D coordinates.
* @param nx X coordinate for the new vector.
* @param ny Y coordinate for the new vector.
* @param nz Z coordinate for the new vector.
*/
public Vector3D( float nx, float ny, float nz )
{
x = nx;
y = ny;
z = nz;
}
/**
* Returns a new instance containing the same information as this one.
* @return A new Vector3D.
*/
@Override
public Vector3D clone()
{
return new Vector3D( x, y, z );
}
/**
* Returns a vector containing the crossproduct: A cross B.
* @param A First vector in the cross product.
* @param B Second vector in the cross product.
* @return A new Vector3D.
*/
public Vector3D cross( Vector3D A, Vector3D B )
{
return new Vector3D(
A.y * B.z  B.y * A.z,
A.z * B.x  B.z * A.x,
A.x * B.y  B.x * A.y );
}
/**
* Returns a vector containing the crossproduct: (this) cross B.
* @param B Second vector in the cross product.
* @return A new Vector3D.
*/
public Vector3D cross( Vector3D B )
{
return new Vector3D(
y * B.z  B.y * z,
z * B.x  B.z * x,
x * B.y  B.x * y );
}
/**
* Returns the dotproduct result of: A dot B.
* @param A First vector in the dot product.
* @param B Second vector in the dot product.
* @return Dot product.
*/
public float dot( Vector3D A, Vector3D B )
{
return( (A.x * B.x) + (A.y * B.y) + (A.z * B.z) );
}
/**
* Returns the dotproduct result of: (this) dot B.
* @param B Second vector in the dot product.
* @return Dot product.
*/
public float dot( Vector3D B )
{
return( (x * B.x) + (y * B.y) + (z * B.z) );
}
/**
* Returns the vector represented by: A + B.
* @param A First vector.
* @param B Vector to add to A.
* @return A new Vector3D.
*/
public Vector3D add( Vector3D A, Vector3D B )
{
return new Vector3D( A.x + B.x, A.y + B.y, A.z + B.z );
}
/**
* Returns the vector represented by: (this) + B.
* @param B Vector to add to this one.
* @return A new Vector3D.
*/
public Vector3D add( Vector3D B )
{
return new Vector3D( x + B.x, y + B.y, z + B.z );
}
/**
* Returns the vector represented by: A  B.
* @param A First vector.
* @param B Vector to subtract from A.
* @return A new Vector3D.
*/
public Vector3D subtract( Vector3D A, Vector3D B )
{
return new Vector3D( A.x  B.x, A.y  B.y, A.z  B.z );
}
/**
* Returns the vector represented by: (this)  B.
* @param B Vector to subtract from this one.
* @return A new Vector3D.
*/
public Vector3D subtract( Vector3D B )
{
return new Vector3D( x  B.x, y  B.y, z  B.z );
}
/**
* Changes the length of this vector to 1.0.
*/
public void normalize()
{
double t = Math.sqrt( x*x + y*y + z*z );
x = (float) (x / t);
y = (float) (y / t);
z = (float) (z / t);
}
}