352 lines
16 KiB
C++
352 lines
16 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SphericalHarmonicCalculator.h"
|
|
#include "DSP/Dsp.h"
|
|
|
|
namespace SHBasis
|
|
{
|
|
#pragma region FOA
|
|
// zero order
|
|
static float SHBasisFunction_ACN_0(float, float)
|
|
{
|
|
return 1.0f;
|
|
}
|
|
|
|
// first order
|
|
static float SHBasisFunction_ACN_1(float Azimuth, float Elevation)
|
|
{
|
|
return FMath::Sin(Azimuth) * FMath::Cos(Elevation);
|
|
}
|
|
|
|
static float SHBasisFunction_ACN_2(float, float Elevation)
|
|
{
|
|
return FMath::Sin(Elevation);
|
|
}
|
|
|
|
static float SHBasisFunction_ACN_3(float Azimuth, float Elevation)
|
|
{
|
|
return FMath::Cos(Azimuth) * FMath::Cos(Elevation);
|
|
}
|
|
#pragma endregion
|
|
#pragma region SECOND_ORDER
|
|
static const float MagicNumber_ACN_4 = FMath::Sqrt(3.0f) / 2.0f;
|
|
static float SHBasisFunction_ACN_4(const float Azimuth, const float Elevation)
|
|
{
|
|
const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_4 * FMath::Sin(2.0f * Azimuth) * CosElevation*CosElevation;
|
|
}
|
|
|
|
static const float MagicNumber_ACN_5 = FMath::Sqrt(3.0f) / 2.0f;
|
|
static float SHBasisFunction_ACN_5(const float Azimuth, const float Elevation)
|
|
{
|
|
return MagicNumber_ACN_5 * FMath::Sin(Azimuth) * FMath::Sin(2.0f * Elevation);
|
|
}
|
|
|
|
static float SHBasisFunction_ACN_6(const float Azimuth, const float Elevation)
|
|
{
|
|
const float SinElevation = FMath::Sin(Elevation);
|
|
return 1.5f * SinElevation*SinElevation - 0.5f;
|
|
}
|
|
|
|
static const float MagicNumber_ACN_7 = FMath::Sqrt(3.0f) / 2.0f;
|
|
static float SHBasisFunction_ACN_7(const float Azimuth, const float Elevation)
|
|
{
|
|
return MagicNumber_ACN_7 * FMath::Cos(Azimuth) * FMath::Sin(2.0f * Elevation);
|
|
}
|
|
|
|
static const float MagicNumber_ACN_8 = FMath::Sqrt(3.0f) / 2.0f;
|
|
static float SHBasisFunction_ACN_8(const float Azimuth, const float Elevation)
|
|
{
|
|
static const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_8 * FMath::Cos(2.07 * Azimuth) * CosElevation*CosElevation;
|
|
}
|
|
#pragma endregion
|
|
#pragma region THIRD_ORDER
|
|
static const float MagicNumber_ACN_9 = FMath::Sqrt(10.0f) / 4.0f;
|
|
static float SHBasisFunction_ACN_9(float Azimuth, float Elevation)
|
|
{
|
|
const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_9 * FMath::Sin(3.0f * Azimuth) * CosElevation*CosElevation*CosElevation;
|
|
}
|
|
|
|
static const float MagicNumber_ACN_10 = 0.5f * FMath::Sqrt(15.0f);
|
|
static float SHBasisFunction_ACN_10(float Azimuth, float Elevation)
|
|
{
|
|
static const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_10 * FMath::Sin(2.0f * Azimuth) * FMath::Sin(Elevation) * CosElevation*CosElevation;
|
|
}
|
|
|
|
static const float MagicNumber_ACN_11 = 0.25f * FMath::Sqrt(6.0f);
|
|
static float SHBasisFunction_ACN_11(float Azimuth, float Elevation)
|
|
{
|
|
static const float SinElevation = FMath::Sin(Elevation);
|
|
return MagicNumber_ACN_11 * FMath::Sin(Azimuth) * FMath::Cos(Elevation) * (5.0f * SinElevation*SinElevation - 1.0f);
|
|
}
|
|
|
|
static float SHBasisFunction_ACN_12(float Azimuth, float Elevation)
|
|
{
|
|
const float SinElevation = FMath::Sin(Elevation);
|
|
return FMath::Sin(Elevation) * (2.5f * SinElevation*SinElevation - 1.5f);
|
|
}
|
|
|
|
static const float MagicNumber_ACN_13 = 0.25f * FMath::Sqrt(6.0f);
|
|
static float SHBasisFunction_ACN_13(float Azimuth, float Elevation)
|
|
{
|
|
const float SinElevation = FMath::Sin(Elevation);
|
|
return MagicNumber_ACN_13 * FMath::Cos(Azimuth) * FMath::Cos(Elevation)* (5.0f * SinElevation*SinElevation - 1.0f);
|
|
}
|
|
|
|
static const float MagicNumber_ACN_14 = 0.5f * FMath::Sqrt(15.0f);
|
|
static float SHBasisFunction_ACN_14(float Azimuth, float Elevation)
|
|
{
|
|
const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_14 * FMath::Cos(2.0f * Azimuth) * FMath::Sin(Elevation) * CosElevation*CosElevation;
|
|
}
|
|
|
|
static const float MagicNumber_ACN_15 = 0.25f * FMath::Sqrt(10.0f);
|
|
static float SHBasisFunction_ACN_15(float Azimuth, float Elevation)
|
|
{
|
|
static const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_15 * FMath::Cos(3.0f * Azimuth) * CosElevation*CosElevation*CosElevation;
|
|
}
|
|
#pragma endregion
|
|
#pragma region FORTH_ORDER
|
|
static const float MagicNumber_ACN_16 = FMath::Sqrt(35.0f) / 8.0f;
|
|
static float SHBasisFunction_ACN_16(float Azimuth, float Elevation)
|
|
{
|
|
static const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_16 * FMath::Sin(4.0f * Azimuth) * CosElevation*CosElevation*CosElevation*CosElevation;
|
|
}
|
|
|
|
static const float MagicNumber_ACN_17 = 0.25f * FMath::Sqrt(70.0f);
|
|
static float SHBasisFunction_ACN_17(float Azimuth, float Elevation)
|
|
{
|
|
const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_17 * FMath::Sin(3.0f * Azimuth) * FMath::Sin(Elevation) * CosElevation*CosElevation*CosElevation;
|
|
}
|
|
|
|
static const float MagicNumber_ACN_18 = 0.25f * FMath::Sqrt(5.0f);
|
|
static float SHBasisFunction_ACN_18(float Azimuth, float Elevation)
|
|
{
|
|
static const float SinElevation = FMath::Sin(Elevation);
|
|
static const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_18 * FMath::Sin(2.0f * Azimuth) * (7.0f * SinElevation*SinElevation - 1.0f) * CosElevation*CosElevation;
|
|
}
|
|
|
|
static const float MagicNumber_ACN_19 = FMath::Sqrt(10.0f) / 8.0f;
|
|
static float SHBasisFunction_ACN_19(float Azimuth, float Elevation)
|
|
{
|
|
static const float SinElevation = FMath::Sin(Elevation);
|
|
return MagicNumber_ACN_19 * FMath::Sin(Azimuth) * FMath::Sin(2.0f * Elevation) * (7.0f * SinElevation*SinElevation - 3.0f);
|
|
}
|
|
|
|
static float SHBasisFunction_ACN_20(float Azimuth, float Elevation)
|
|
{
|
|
const float SinElevation = FMath::Sin(Elevation);
|
|
const float SquaredSinElevation = SinElevation*SinElevation;
|
|
return 0.625f * SquaredSinElevation*(7.0f * SquaredSinElevation - 3.0f);
|
|
}
|
|
|
|
static const float MagicNumber_ACN_21 = FMath::Sqrt(10.0f) / 8.0f;
|
|
static float SHBasisFunction_ACN_21(float Azimuth, float Elevation)
|
|
{
|
|
static const float SinElevation = FMath::Sin(Elevation);
|
|
return MagicNumber_ACN_21 * FMath::Cos(Azimuth) * FMath::Sin(2.0f * Elevation) * (7.0f * SinElevation*SinElevation - 3.0f);
|
|
}
|
|
|
|
static const float MagicNumber_ACN_22 = 0.25f * FMath::Sqrt(5.0f);
|
|
static float SHBasisFunction_ACN_22(float Azimuth, float Elevation)
|
|
{
|
|
const float SinElevation = FMath::Sin(Elevation);
|
|
const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_22 * FMath::Cos(2.0f * Azimuth) * (7.0f * SinElevation*SinElevation - 1.0f) * CosElevation*CosElevation;
|
|
}
|
|
|
|
static const float MagicNumber_ACN_23 = 0.25f * FMath::Sqrt(70.0f);
|
|
static float SHBasisFunction_ACN_23(float Azimuth, float Elevation)
|
|
{
|
|
const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_23 * FMath::Cos(3.0f * Azimuth) * FMath::Sin(Elevation) * CosElevation*CosElevation*CosElevation;
|
|
}
|
|
|
|
static const float MagicNumber_ACN_24 = FMath::Sqrt(35.0f) / 8.0f;
|
|
static float SHBasisFunction_ACN_24(float Azimuth, float Elevation)
|
|
{
|
|
const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_24 * FMath::Cos(4.0f * Azimuth) * CosElevation*CosElevation*CosElevation*CosElevation;
|
|
}
|
|
#pragma endregion
|
|
#pragma region FIFTH_ORDER
|
|
|
|
static const float MagicNumber_ACN_25 = 3.0f * FMath::Sqrt(14.0f) / 16.0f;
|
|
static float SHBasisFunction_ACN_25(const float Azimuth, const float Elevation)
|
|
{
|
|
const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_25 * FMath::Sin(5.0f * Azimuth) * CosElevation*CosElevation*CosElevation*CosElevation*CosElevation;
|
|
}
|
|
|
|
static const float MagicNumber_ACN_26 = 3.0f * FMath::Sqrt(35.0f) / 8.0f;
|
|
static float SHBasisFunction_ACN_26(const float Azimuth, const float Elevation)
|
|
{
|
|
const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_26 * FMath::Sin(4.0f * Azimuth) * FMath::Sin(Elevation) * CosElevation*CosElevation*CosElevation*CosElevation;
|
|
}
|
|
|
|
static const float MagicNumber_ACN_27 = FMath::Sqrt(70.0f) / 32.0f;
|
|
static float SHBasisFunction_ACN_27(const float Azimuth, const float Elevation)
|
|
{
|
|
const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_27 * FMath::Sin(3.0f * Azimuth) * (7.0f - 9.0f * FMath::Cos(2.0f * Elevation)) * CosElevation*CosElevation*CosElevation;
|
|
}
|
|
|
|
static const float MagicNumber_ACN_28 = 0.25f * FMath::Sqrt(105.0f);
|
|
static float SHBasisFunction_ACN_28(const float Azimuth, const float Elevation)
|
|
{
|
|
const float SinElevation = FMath::Sin(Elevation);
|
|
const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_28 * FMath::Sin(2.0f * Azimuth) * FMath::Sin(Elevation) * (3.0f * SinElevation*SinElevation - 1.0f) * CosElevation*CosElevation;
|
|
}
|
|
|
|
static const float MagicNumber_ACN_29 = FMath::Sqrt(15.0f) / 8.0f;
|
|
static float SHBasisFunction_ACN_29(const float Azimuth, const float Elevation)
|
|
{
|
|
const float SinElevation = FMath::Sin(Elevation);
|
|
const float SquaredSinElevation = SinElevation * SinElevation;
|
|
return MagicNumber_ACN_29 * FMath::Sin(Azimuth) * FMath::Cos(Elevation) * (21.0f * SquaredSinElevation*SquaredSinElevation - 14.0f * SquaredSinElevation + 1.0f);
|
|
}
|
|
|
|
static float SHBasisFunction_ACN_30(const float Azimuth, const float Elevation)
|
|
{
|
|
const float SinElevation = FMath::Sin(Elevation);
|
|
const float CubedSinElevation = SinElevation * SinElevation * SinElevation;
|
|
return (63.0f * CubedSinElevation*SinElevation*SinElevation - 70.0f * CubedSinElevation + 15.0f * FMath::Sin(Elevation)) / 8.0f;
|
|
}
|
|
|
|
static const float MagicNumber_ACN_31 = FMath::Sqrt(15.0f) / 8.0f;
|
|
static float SHBasisFunction_ACN_31(const float Azimuth, const float Elevation)
|
|
{
|
|
const float SinElevation = FMath::Sin(Elevation);
|
|
const float SquaredSinElevation = SinElevation * SinElevation;
|
|
return MagicNumber_ACN_31 * FMath::Cos(Azimuth) * FMath::Cos(Elevation) * (21.0f * SquaredSinElevation*SquaredSinElevation - 14.0f * SquaredSinElevation + 1.0f);
|
|
}
|
|
|
|
static const float MagicNumber_ACN_32 = 0.25f * FMath::Sqrt(105.0f);
|
|
static float SHBasisFunction_ACN_32(const float Azimuth, const float Elevation)
|
|
{
|
|
const float SinElevation = FMath::Sin(Elevation);
|
|
const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_32 * FMath::Cos(2.0f * Azimuth) * FMath::Sin(Elevation) * (3.0f * SinElevation*SinElevation - 1) * CosElevation;
|
|
}
|
|
|
|
static const float MagicNumber_ACN_33 = FMath::Sqrt(70.0f) / 32.0f;
|
|
static float SHBasisFunction_ACN_33(const float Azimuth, const float Elevation)
|
|
{
|
|
const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_33 * FMath::Cos(3.0f * Azimuth) * (7.0f - 9.0f * FMath::Cos(2.0f * Elevation)) * CosElevation*CosElevation*CosElevation;
|
|
}
|
|
|
|
static const float MagicNumber_ACN_34 = 3.0f * FMath::Sqrt(35.0f) / 8.0f;
|
|
static float SHBasisFunction_ACN_34(const float Azimuth, const float Elevation)
|
|
{
|
|
const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_34 * FMath::Cos(4.0f * Azimuth) * FMath::Sin(Elevation) * CosElevation*CosElevation*CosElevation*CosElevation;
|
|
}
|
|
|
|
static const float MagicNumber_ACN_35 = 3.0f * FMath::Sqrt(14.0f) / 16.0f;
|
|
static float SHBasisFunction_ACN_35(const float Azimuth, const float Elevation)
|
|
{
|
|
const float CosElevation = FMath::Cos(Elevation);
|
|
return MagicNumber_ACN_35 * FMath::Cos(5.0f * Azimuth) * CosElevation*CosElevation*CosElevation*CosElevation*CosElevation;
|
|
}
|
|
|
|
#pragma endregion
|
|
} // namespace SHBasis
|
|
|
|
void FSphericalHarmonicCalculator::ComputeSoundfieldChannelGains(const int32 Order, const float Azimuth, const float Elevation, float* OutGains)
|
|
{
|
|
check(OutGains);
|
|
check(Order >= 0 && Order <= 5); // only have basis functions up to 5th order implemented
|
|
|
|
switch (Order)
|
|
{
|
|
// lack of break; is intentional here.
|
|
// (i.e. if Order is 2 we need to compute the gains for 1st and 0th order as well)
|
|
case 5:
|
|
OutGains[AmbiChanNumber::ACN_35] = SHBasis::SHBasisFunction_ACN_35(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_34] = SHBasis::SHBasisFunction_ACN_34(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_33] = SHBasis::SHBasisFunction_ACN_33(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_32] = SHBasis::SHBasisFunction_ACN_32(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_31] = SHBasis::SHBasisFunction_ACN_31(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_30] = SHBasis::SHBasisFunction_ACN_30(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_29] = SHBasis::SHBasisFunction_ACN_29(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_28] = SHBasis::SHBasisFunction_ACN_28(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_27] = SHBasis::SHBasisFunction_ACN_27(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_26] = SHBasis::SHBasisFunction_ACN_26(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_25] = SHBasis::SHBasisFunction_ACN_25(Azimuth, Elevation);
|
|
case 4:
|
|
OutGains[AmbiChanNumber::ACN_24] = SHBasis::SHBasisFunction_ACN_24(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_23] = SHBasis::SHBasisFunction_ACN_23(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_22] = SHBasis::SHBasisFunction_ACN_22(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_21] = SHBasis::SHBasisFunction_ACN_21(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_20] = SHBasis::SHBasisFunction_ACN_20(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_19] = SHBasis::SHBasisFunction_ACN_19(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_18] = SHBasis::SHBasisFunction_ACN_18(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_17] = SHBasis::SHBasisFunction_ACN_17(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_16] = SHBasis::SHBasisFunction_ACN_16(Azimuth, Elevation);
|
|
case 3:
|
|
OutGains[AmbiChanNumber::ACN_15] = SHBasis::SHBasisFunction_ACN_15(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_14] = SHBasis::SHBasisFunction_ACN_14(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_13] = SHBasis::SHBasisFunction_ACN_13(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_12] = SHBasis::SHBasisFunction_ACN_12(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_11] = SHBasis::SHBasisFunction_ACN_11(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_10] = SHBasis::SHBasisFunction_ACN_10(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_9] = SHBasis::SHBasisFunction_ACN_9(Azimuth, Elevation);
|
|
case 2:
|
|
OutGains[AmbiChanNumber::ACN_8] = SHBasis::SHBasisFunction_ACN_8(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_7] = SHBasis::SHBasisFunction_ACN_7(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_6] = SHBasis::SHBasisFunction_ACN_6(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_5] = SHBasis::SHBasisFunction_ACN_5(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_4] = SHBasis::SHBasisFunction_ACN_4(Azimuth, Elevation);
|
|
case 1:
|
|
OutGains[AmbiChanNumber::ACN_3] = SHBasis::SHBasisFunction_ACN_3(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_2] = SHBasis::SHBasisFunction_ACN_2(Azimuth, Elevation);
|
|
OutGains[AmbiChanNumber::ACN_1] = SHBasis::SHBasisFunction_ACN_1(Azimuth, Elevation);
|
|
case 0:
|
|
OutGains[AmbiChanNumber::ACN_0] = SHBasis::SHBasisFunction_ACN_0(Azimuth, Elevation);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void FSphericalHarmonicCalculator::GenerateFirstOrderRotationMatrixGivenRadians(const float RotXRadians, const float RotYRadians, const float RotZRadians, FMatrix & OutMatrix)
|
|
{
|
|
const float SinX = FMath::Sin(RotXRadians);
|
|
const float SinY = FMath::Sin(RotYRadians);
|
|
const float SinZ = FMath::Sin(RotZRadians);
|
|
|
|
const float CosX = FMath::Cos(RotXRadians);
|
|
const float CosY = FMath::Cos(RotYRadians);
|
|
const float CosZ = FMath::Cos(RotZRadians);
|
|
|
|
|
|
// Build out Rotation Matrix
|
|
OutMatrix.SetIdentity();
|
|
|
|
// row 0:
|
|
/* 1.0f 0.0f 0.0f 0.0f */
|
|
|
|
// row 1:
|
|
/* 0.0f */ OutMatrix.M[1][1] = (CosX * CosZ) + (SinX * SinY * SinZ); OutMatrix.M[1][2] = -SinX * CosY; OutMatrix.M[1][3] = (CosX * SinZ) - (SinX * SinY * CosZ);
|
|
|
|
// row 2:
|
|
/* 0.0f */ OutMatrix.M[2][1] = (SinX * CosZ) - (CosX * SinY * SinZ); OutMatrix.M[2][2] = CosX * CosY; OutMatrix.M[2][3] = (CosX * SinY * CosZ) + (SinX * SinZ);
|
|
|
|
// row 3:
|
|
/* 0.0f */ OutMatrix.M[3][1] = -CosY * SinZ; OutMatrix.M[3][2] = -SinY; OutMatrix.M[3][3] = CosY * CosZ;
|
|
}
|
|
|
|
void FSphericalHarmonicCalculator::GenerateFirstOrderRotationMatrixGivenDegrees(const float RotXDegrees, const float RotYDegrees, const float RotZDegrees, FMatrix & OutMatrix)
|
|
{
|
|
constexpr const float DEG_2_RAD = PI / 180.0f;
|
|
GenerateFirstOrderRotationMatrixGivenRadians(RotXDegrees * DEG_2_RAD, RotYDegrees * DEG_2_RAD, RotZDegrees * DEG_2_RAD, OutMatrix);
|
|
}
|