420 lines
8.3 KiB
C
420 lines
8.3 KiB
C
|
// This code is in the public domain -- castanyo@yahoo.es
|
||
|
|
||
|
#ifndef NV_MATH_SPHERICALHARMONIC_H
|
||
|
#define NV_MATH_SPHERICALHARMONIC_H
|
||
|
|
||
|
#include <nvmath/Vector.h>
|
||
|
|
||
|
namespace nv
|
||
|
{
|
||
|
|
||
|
NVMATH_API float legendrePolynomial( int l, int m, float x ) NV_CONST;
|
||
|
NVMATH_API float y( int l, int m, float theta, float phi ) NV_CONST;
|
||
|
NVMATH_API float y( int l, int m, Vector3::Arg v ) NV_CONST;
|
||
|
NVMATH_API float hy( int l, int m, float theta, float phi ) NV_CONST;
|
||
|
NVMATH_API float hy( int l, int m, Vector3::Arg v ) NV_CONST;
|
||
|
|
||
|
class Sh;
|
||
|
float dot(const Sh & a, const Sh & b) NV_CONST;
|
||
|
|
||
|
|
||
|
/// Spherical harmonic class.
|
||
|
class Sh
|
||
|
{
|
||
|
friend class Sh2;
|
||
|
friend class ShMatrix;
|
||
|
public:
|
||
|
|
||
|
/// Construct a spherical harmonic of the given order.
|
||
|
Sh(int o) : m_order(o)
|
||
|
{
|
||
|
m_elemArray = new float[basisNum()];
|
||
|
}
|
||
|
|
||
|
/// Copy constructor.
|
||
|
Sh(const Sh & sh) : m_order(sh.order())
|
||
|
{
|
||
|
m_elemArray = new float[basisNum()];
|
||
|
memcpy(m_elemArray, sh.m_elemArray, sizeof(float) * basisNum());
|
||
|
}
|
||
|
|
||
|
/// Destructor.
|
||
|
~Sh()
|
||
|
{
|
||
|
delete [] m_elemArray;
|
||
|
m_elemArray = NULL;
|
||
|
}
|
||
|
|
||
|
/// Get number of bands.
|
||
|
static int bandNum(int order) {
|
||
|
return order + 1;
|
||
|
}
|
||
|
|
||
|
/// Get number of sh basis.
|
||
|
static int basisNum(int order) {
|
||
|
return (order + 1) * (order + 1);
|
||
|
}
|
||
|
|
||
|
/// Get the index for the given coefficients.
|
||
|
static int index( int l, int m ) {
|
||
|
return l * l + l + m;
|
||
|
}
|
||
|
|
||
|
/// Get sh order.
|
||
|
int order() const
|
||
|
{
|
||
|
return m_order;
|
||
|
}
|
||
|
|
||
|
/// Get sh order.
|
||
|
int bandNum() const
|
||
|
{
|
||
|
return bandNum(m_order);
|
||
|
}
|
||
|
|
||
|
/// Get sh order.
|
||
|
int basisNum() const
|
||
|
{
|
||
|
return basisNum(m_order);
|
||
|
}
|
||
|
|
||
|
/// Get sh coefficient indexed by l,m.
|
||
|
float elem( int l, int m ) const
|
||
|
{
|
||
|
return m_elemArray[index(l, m)];
|
||
|
}
|
||
|
|
||
|
/// Get sh coefficient indexed by l,m.
|
||
|
float & elem( int l, int m )
|
||
|
{
|
||
|
return m_elemArray[index(l, m)];
|
||
|
}
|
||
|
|
||
|
|
||
|
/// Get sh coefficient indexed by i.
|
||
|
float elemAt( int i ) const {
|
||
|
return m_elemArray[i];
|
||
|
}
|
||
|
|
||
|
/// Get sh coefficient indexed by i.
|
||
|
float & elemAt( int i )
|
||
|
{
|
||
|
return m_elemArray[i];
|
||
|
}
|
||
|
|
||
|
|
||
|
/// Reset the sh coefficients.
|
||
|
void reset()
|
||
|
{
|
||
|
for( int i = 0; i < basisNum(); i++ ) {
|
||
|
m_elemArray[i] = 0.0f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Copy spherical harmonic.
|
||
|
void operator= ( const Sh & sh )
|
||
|
{
|
||
|
nvDebugCheck(order() <= sh.order());
|
||
|
|
||
|
for(int i = 0; i < basisNum(); i++) {
|
||
|
m_elemArray[i] = sh.m_elemArray[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Add spherical harmonics.
|
||
|
void operator+= ( const Sh & sh )
|
||
|
{
|
||
|
nvDebugCheck(order() == sh.order());
|
||
|
|
||
|
for(int i = 0; i < basisNum(); i++) {
|
||
|
m_elemArray[i] += sh.m_elemArray[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Substract spherical harmonics.
|
||
|
void operator-= ( const Sh & sh )
|
||
|
{
|
||
|
nvDebugCheck(order() == sh.order());
|
||
|
|
||
|
for(int i = 0; i < basisNum(); i++) {
|
||
|
m_elemArray[i] -= sh.m_elemArray[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Not exactly convolution, nor product.
|
||
|
void operator*= ( const Sh & sh )
|
||
|
{
|
||
|
nvDebugCheck(order() == sh.order());
|
||
|
|
||
|
for(int i = 0; i < basisNum(); i++) {
|
||
|
m_elemArray[i] *= sh.m_elemArray[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Scale spherical harmonics.
|
||
|
void operator*= ( float f )
|
||
|
{
|
||
|
for(int i = 0; i < basisNum(); i++) {
|
||
|
m_elemArray[i] *= f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Add scaled spherical harmonics.
|
||
|
void addScaled( const Sh & sh, float f )
|
||
|
{
|
||
|
nvDebugCheck(order() == sh.order());
|
||
|
|
||
|
for(int i = 0; i < basisNum(); i++) {
|
||
|
m_elemArray[i] += sh.m_elemArray[i] * f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*/// Add a weighted sample to the sh coefficients.
|
||
|
void AddSample( const Vec3 & dir, const Color3f & color, float w=1.0f ) {
|
||
|
for(int l = 0; l <= order; l++) {
|
||
|
for(int m = -l; m <= l; m++) {
|
||
|
Color3f & elem = GetElem(l, m);
|
||
|
elem.Mad( elem, color, w * y(l, m, dir) );
|
||
|
}
|
||
|
}
|
||
|
}*/
|
||
|
|
||
|
/// Evaluate
|
||
|
void eval(Vector3::Arg dir)
|
||
|
{
|
||
|
for(int l = 0; l <= m_order; l++) {
|
||
|
for(int m = -l; m <= l; m++) {
|
||
|
elem(l, m) = y(l, m, dir);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/// Evaluate the spherical harmonic function.
|
||
|
float sample(Vector3::Arg dir) const
|
||
|
{
|
||
|
Sh sh(order());
|
||
|
sh.eval(dir);
|
||
|
|
||
|
return dot(sh, *this);
|
||
|
}
|
||
|
|
||
|
|
||
|
protected:
|
||
|
|
||
|
const int m_order;
|
||
|
float * m_elemArray;
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
/// Compute dot product of the spherical harmonics.
|
||
|
inline float dot(const Sh & a, const Sh & b)
|
||
|
{
|
||
|
nvDebugCheck(a.order() == b.order());
|
||
|
|
||
|
float sum = 0;
|
||
|
for( int i = 0; i < Sh::basisNum(a.order()); i++ ) {
|
||
|
sum += a.elemAt(i) * b.elemAt(i);
|
||
|
}
|
||
|
|
||
|
return sum;
|
||
|
}
|
||
|
|
||
|
|
||
|
/// Second order spherical harmonic.
|
||
|
class Sh2 : public Sh
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
/// Constructor.
|
||
|
Sh2() : Sh(2) {}
|
||
|
|
||
|
/// Copy constructor.
|
||
|
Sh2(const Sh2 & sh) : Sh(sh) {}
|
||
|
|
||
|
/// Spherical harmonic resulting from projecting the clamped cosine transfer function to the SH basis.
|
||
|
void cosineTransfer()
|
||
|
{
|
||
|
const float c1 = 0.282095f; // K(0, 0)
|
||
|
const float c2 = 0.488603f; // K(1, 0)
|
||
|
const float c3 = 1.092548f; // sqrt(15.0f / PI) / 2.0f = K(2, -2)
|
||
|
const float c4 = 0.315392f; // sqrt(5.0f / PI) / 4.0f) = K(2, 0)
|
||
|
const float c5 = 0.546274f; // sqrt(15.0f / PI) / 4.0f) = K(2, 2)
|
||
|
|
||
|
const float normalization = PI * 16.0f / 17.0f;
|
||
|
|
||
|
const float const1 = c1 * normalization * 1.0f;
|
||
|
const float const2 = c2 * normalization * (2.0f / 3.0f);
|
||
|
const float const3 = c3 * normalization * (1.0f / 4.0f);
|
||
|
const float const4 = c4 * normalization * (1.0f / 4.0f);
|
||
|
const float const5 = c5 * normalization * (1.0f / 4.0f);
|
||
|
|
||
|
m_elemArray[0] = const1;
|
||
|
|
||
|
m_elemArray[1] = -const2;
|
||
|
m_elemArray[2] = const2;
|
||
|
m_elemArray[3] = -const2;
|
||
|
|
||
|
m_elemArray[4] = const3;
|
||
|
m_elemArray[5] = -const3;
|
||
|
m_elemArray[6] = const4;
|
||
|
m_elemArray[7] = -const3;
|
||
|
m_elemArray[8] = const5;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
#if 0
|
||
|
|
||
|
/// Spherical harmonic matrix.
|
||
|
class ShMatrix
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
/// Create an identity matrix of the given order.
|
||
|
ShMatrix(int o = 2) : order(o), identity(true)
|
||
|
{
|
||
|
nvCheck(order > 0);
|
||
|
e = new float[Size()];
|
||
|
band = new float *[GetBandNum()];
|
||
|
setupBands();
|
||
|
}
|
||
|
|
||
|
/// Destroy and free matrix elements.
|
||
|
~ShMatrix()
|
||
|
{
|
||
|
delete e;
|
||
|
delete band;
|
||
|
}
|
||
|
|
||
|
/// Set identity matrix.
|
||
|
void setIdentity()
|
||
|
{
|
||
|
identity = true;
|
||
|
}
|
||
|
|
||
|
/// Return true if this is an identity matrix, false in other case.
|
||
|
bool isIdentity() const {
|
||
|
return identity;
|
||
|
}
|
||
|
|
||
|
/// Get number of bands of this matrix.
|
||
|
int bandNum() const
|
||
|
{
|
||
|
return order+1;
|
||
|
}
|
||
|
|
||
|
/// Get total number of elements in the matrix.
|
||
|
int size() const
|
||
|
{
|
||
|
int size = 0;
|
||
|
for( int i = 0; i < bandNum(); i++ ) {
|
||
|
size += SQ(i * 2 + 1);
|
||
|
}
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
/// Get element at the given raw index.
|
||
|
float elem(const int idx) const
|
||
|
{
|
||
|
return e[idx];
|
||
|
}
|
||
|
|
||
|
/// Get element at the given with the given indices.
|
||
|
float & elem( const int b, const int x, const int y )
|
||
|
{
|
||
|
nvDebugCheck(b >= 0);
|
||
|
nvDebugCheck(b < bandNum());
|
||
|
return band[b][(b + y) * (b * 2 + 1) + (b + x)];
|
||
|
}
|
||
|
|
||
|
/// Get element at the given with the given indices.
|
||
|
float elem( const int b, const int x, const int y ) const
|
||
|
{
|
||
|
nvDebugCheck(b >= 0);
|
||
|
nvDebugCheck(b < bandNum());
|
||
|
return band[b][(b + y) * (b * 2 + 1) + (b + x)];
|
||
|
}
|
||
|
|
||
|
/** Copy matrix. */
|
||
|
void Copy( const ShMatrix & m )
|
||
|
{
|
||
|
nvDebugCheck(order == m.order);
|
||
|
memcpy(e, m.e, Size() * sizeof(float));
|
||
|
}
|
||
|
|
||
|
/** Rotate the given coefficients. */
|
||
|
void transform( const Sh & restrict source, Sh * restrict dest ) const {
|
||
|
piCheck( &source != dest ); // Make sure there's no aliasing.
|
||
|
piCheck( dest->order <= order );
|
||
|
piCheck( order <= source.order );
|
||
|
|
||
|
if( identity ) {
|
||
|
*dest = source;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Loop through each band.
|
||
|
for( int l = 0; l <= dest->order; l++ ) {
|
||
|
|
||
|
for( int mo = -l; mo <= l; mo++ ) {
|
||
|
|
||
|
Color3f rgb = Color3f::Black;
|
||
|
|
||
|
for( int mi = -l; mi <= l; mi++ ) {
|
||
|
rgb.Mad( rgb, source.elem(l, mi), elem(l, mo, mi) );
|
||
|
}
|
||
|
|
||
|
dest->elem(l, mo) = rgb;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
MATHLIB_API void multiply( const ShMatrix &A, const ShMatrix &B );
|
||
|
MATHLIB_API void rotation( const Matrix & m );
|
||
|
MATHLIB_API void rotation( int axis, float angles );
|
||
|
MATHLIB_API void print();
|
||
|
|
||
|
|
||
|
private:
|
||
|
|
||
|
// @@ These could be static indices precomputed only once.
|
||
|
/// Setup the band pointers.
|
||
|
void setupBands()
|
||
|
{
|
||
|
int size = 0;
|
||
|
for( int i = 0; i < bandNum(); i++ ) {
|
||
|
band[i] = &e[size];
|
||
|
size += SQ(i * 2 + 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
private:
|
||
|
|
||
|
// Matrix order.
|
||
|
const int m_order;
|
||
|
|
||
|
// Identity flag for quick transform.
|
||
|
bool m_identity;
|
||
|
|
||
|
// Array of elements.
|
||
|
float * m_e;
|
||
|
|
||
|
// Band pointers.
|
||
|
float ** m_band;
|
||
|
|
||
|
};
|
||
|
|
||
|
#endif // 0
|
||
|
|
||
|
|
||
|
|
||
|
} // nv namespace
|
||
|
|
||
|
#endif // NV_MATH_SPHERICALHARMONIC_H
|