seamless cubemap filtering.

This commit is contained in:
castano
2011-10-11 06:40:40 +00:00
parent 2ec37026be
commit d11d7a5f38
12 changed files with 981 additions and 385 deletions

View File

@ -2,13 +2,14 @@ PROJECT(nvmath)
SET(MATH_SRCS
nvmath.h
Vector.h
Matrix.h
Plane.h Plane.cpp
Box.h
Color.h
Box.h Box.inl
Color.h Color.inl
Fitting.h Fitting.cpp
Half.h Half.cpp
Fitting.h Fitting.cpp)
Matrix.h
Plane.h Plane.inl Plane.cpp
SphericalHarmonic.h SphericalHarmonic.cpp
Vector.h Vector.inl)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -11,13 +11,13 @@
namespace nv
{
/// Clamp color components.
// Clamp color components.
inline Vector3 colorClamp(Vector3::Arg c)
{
return Vector3(clamp(c.x, 0.0f, 1.0f), clamp(c.y, 0.0f, 1.0f), clamp(c.z, 0.0f, 1.0f));
}
/// Clamp without allowing the hue to change.
// Clamp without allowing the hue to change.
inline Vector3 colorNormalize(Vector3::Arg c)
{
float scale = 1.0f;
@ -27,15 +27,15 @@ namespace nv
return c / scale;
}
/// Convert Color32 to Color16.
// Convert Color32 to Color16.
inline Color16 toColor16(Color32 c)
{
Color16 color;
// rrrrrggggggbbbbb
// rrrrr000gggggg00bbbbb000
// color.u = (c.u >> 3) & 0x1F;
// color.u |= (c.u >> 5) & 0x7E0;
// color.u |= (c.u >> 8) & 0xF800;
// color.u = (c.u >> 3) & 0x1F;
// color.u |= (c.u >> 5) & 0x7E0;
// color.u |= (c.u >> 8) & 0xF800;
color.r = c.r >> 3;
color.g = c.g >> 2;
@ -44,13 +44,13 @@ namespace nv
}
/// Promote 16 bit color to 32 bit using regular bit expansion.
// Promote 16 bit color to 32 bit using regular bit expansion.
inline Color32 toColor32(Color16 c)
{
Color32 color;
// c.u = ((col0.u << 3) & 0xf8) | ((col0.u << 5) & 0xfc00) | ((col0.u << 8) & 0xf80000);
// c.u |= (c.u >> 5) & 0x070007;
// c.u |= (c.u >> 6) & 0x000300;
// c.u = ((col0.u << 3) & 0xf8) | ((col0.u << 5) & 0xfc00) | ((col0.u << 8) & 0xf80000);
// c.u |= (c.u >> 5) & 0x070007;
// c.u |= (c.u >> 6) & 0x000300;
color.b = (c.b << 3) | (c.b >> 2);
color.g = (c.g << 2) | (c.g >> 4);

View File

@ -0,0 +1,243 @@
// This code is in the public domain -- castanyo@yahoo.es
#include <nvmath/SphericalHarmonic.h>
using namespace nv;
namespace
{
// Basic integer factorial.
inline static int factorial( int v )
{
const static int fac_table[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800 };
if(v <= 11){
return fac_table[v];
}
int result = v;
while (--v > 0) {
result *= v;
}
return result;
}
// Double factorial.
// Defined as: n!! = n*(n - 2)*(n - 4)..., n!!(0,-1) = 1.
inline static int doubleFactorial( int x )
{
if (x == 0 || x == -1) {
return 1;
}
int result = x;
while ((x -= 2) > 0) {
result *= x;
}
return result;
}
/// Normalization constant for spherical harmonic.
/// @param l is the band.
/// @param m is the argument, in the range [0, m]
inline static float K( int l, int m )
{
nvDebugCheck( m >= 0 );
return sqrtf(((2 * l + 1) * factorial(l - m)) / (4 * PI * factorial(l + m)));
}
/// Normalization constant for hemispherical harmonic.
inline static float HK( int l, int m )
{
nvDebugCheck( m >= 0 );
return sqrtf(((2 * l + 1) * factorial(l - m)) / (2 * PI * factorial(l + m)));
}
/// Evaluate Legendre polynomial. */
static float legendre( int l, int m, float x )
{
// piDebugCheck( m >= 0 );
// piDebugCheck( m <= l );
// piDebugCheck( fabs(x) <= 1 );
// Rule 2 needs no previous results
if (l == m) {
return powf(-1.0f, float(m)) * doubleFactorial(2 * m - 1) * powf(1 - x*x, 0.5f * m);
}
// Rule 3 requires the result for the same argument of the previous band
if (l == m + 1) {
return x * (2 * m + 1) * legendrePolynomial(m, m, x);
}
// Main reccurence used by rule 1 that uses result of the same argument from
// the previous two bands
return (x * (2 * l - 1) * legendrePolynomial(l - 1, m, x) - (l + m - 1) * legendrePolynomial(l - 2, m, x)) / (l - m);
}
template <int l, int m> float legendre(float x);
template <> float legendre<0, 0>(float ) {
return 1;
}
template <> float legendre<1, 0>(float x) {
return x;
}
template <> float legendre<1, 1>(float x) {
return -sqrtf(1 - x * x);
}
template <> float legendre<2, 0>(float x) {
return -0.5f + (3 * x * x) / 2;
}
template <> float legendre<2, 1>(float x) {
return -3 * x * sqrtf(1 - x * x);
}
template <> float legendre<2, 2>(float x) {
return -3 * (-1 + x * x);
}
template <> float legendre<3, 0>(float x) {
return -(3 * x) / 2 + (5 * x * x * x) / 2;
}
template <> float legendre<3, 1>(float x) {
return -3 * sqrtf(1 - x * x) / 2 * (-1 + 5 * x * x);
}
template <> float legendre<3, 2>(float x) {
return -15 * (-x + x * x * x);
}
template <> float legendre<3, 3>(float x) {
return -15 * powf(1 - x * x, 1.5f);
}
template <> float legendre<4, 0>(float x) {
return 0.125f * (3.0f - 30.0f * x * x + 35.0f * x * x * x * x);
}
template <> float legendre<4, 1>(float x) {
return -2.5f * x * sqrtf(1.0f - x * x) * (7.0f * x * x - 3.0f);
}
template <> float legendre<4, 2>(float x) {
return -7.5f * (1.0f - 8.0f * x * x + 7.0f * x * x * x * x);
}
template <> float legendre<4, 3>(float x) {
return -105.0f * x * powf(1 - x * x, 1.5f);
}
template <> float legendre<4, 4>(float x) {
return 105.0f * (x * x - 1.0f) * (x * x - 1.0f);
}
} // namespace
float nv::legendrePolynomial(int l, int m, float x)
{
switch(l)
{
case 0:
return legendre<0, 0>(x);
case 1:
if(m == 0) return legendre<1, 0>(x);
return legendre<1, 1>(x);
case 2:
if(m == 0) return legendre<2, 0>(x);
else if(m == 1) return legendre<2, 1>(x);
return legendre<2, 2>(x);
case 3:
if(m == 0) return legendre<3, 0>(x);
else if(m == 1) return legendre<3, 1>(x);
else if(m == 2) return legendre<3, 2>(x);
return legendre<3, 3>(x);
case 4:
if(m == 0) return legendre<4, 0>(x);
else if(m == 1) return legendre<4, 1>(x);
else if(m == 2) return legendre<4, 2>(x);
else if(m == 3) return legendre<4, 3>(x);
else return legendre<4, 4>(x);
}
// Fallback to the expensive version.
return legendre(l, m, x);
}
/**
* Evaluate the spherical harmonic function for the given angles.
* @param l is the band.
* @param m is the argument, in the range [-l,l]
* @param theta is the altitude, in the range [0, PI]
* @param phi is the azimuth, in the range [0, 2*PI]
*/
float nv::shBasis( int l, int m, float theta, float phi )
{
if( m == 0 ) {
// K(l, 0) = sqrt((2*l+1)/(4*PI))
return sqrtf((2 * l + 1) / (4 * PI)) * legendrePolynomial(l, 0, cosf(theta));
}
else if( m > 0 ) {
return sqrtf(2.0f) * K(l, m) * cosf(m * phi) * legendrePolynomial(l, m, cosf(theta));
}
else {
return sqrtf(2.0f) * K(l, -m) * sinf(-m * phi) * legendrePolynomial(l, -m, cosf(theta));
}
}
/**
* Real spherical harmonic function of an unit vector. Uses the following
* equalities to call the angular function:
* x = sin(theta)*cos(phi)
* y = sin(theta)*sin(phi)
* z = cos(theta)
*/
float nv::shBasis( int l, int m, Vector3::Arg v )
{
float theta = acosf(v.z);
float phi = atan2f(v.y, v.x);
return shBasis( l, m, theta, phi );
}
/**
* Evaluate the hemispherical harmonic function for the given angles.
* @param l is the band.
* @param m is the argument, in the range [-l,l]
* @param theta is the altitude, in the range [0, PI/2]
* @param phi is the azimuth, in the range [0, 2*PI]
*/
float nv::hshBasis( int l, int m, float theta, float phi )
{
if( m == 0 ) {
// HK(l, 0) = sqrt((2*l+1)/(2*PI))
return sqrtf((2 * l + 1) / (2 * PI)) * legendrePolynomial(l, 0, 2*cosf(theta)-1);
}
else if( m > 0 ) {
return sqrtf(2.0f) * HK(l, m) * cosf(m * phi) * legendrePolynomial(l, m, 2*cosf(theta)-1);
}
else {
return sqrtf(2.0f) * HK(l, -m) * sinf(-m * phi) * legendrePolynomial(l, -m, 2*cosf(theta)-1);
}
}
/**
* Real hemispherical harmonic function of an unit vector. Uses the following
* equalities to call the angular function:
* x = sin(theta)*cos(phi)
* y = sin(theta)*sin(phi)
* z = cos(theta)
*/
float nv::hshBasis( int l, int m, Vector3::Arg v )
{
float theta = acosf(v.z);
float phi = atan2f(v.y, v.x);
return hshBasis( l, m, theta, phi );
}

View File

@ -0,0 +1,418 @@
// This code is in the public domain -- castanyo@yahoo.es
#ifndef NV_MATH_SPHERICALHARMONIC_H
#define NV_MATH_SPHERICALHARMONIC_H
#include "Vector.h"
#include <string.h> // memcpy
namespace nv
{
class Matrix;
NVMATH_API float legendrePolynomial( int l, int m, float x ) NV_CONST;
NVMATH_API float shBasis( int l, int m, float theta, float phi ) NV_CONST;
NVMATH_API float shBasis( int l, int m, Vector3::Arg v ) NV_CONST;
NVMATH_API float hshBasis( int l, int m, float theta, float phi ) NV_CONST;
NVMATH_API float hshBasis( 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 m_order) {
return m_order + 1;
}
/// Get number of sh basis.
static int basisNum(int m_order) {
return (m_order + 1) * (m_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 * shBasis(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) = shBasis(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;
}
};
/// Spherical harmonic matrix.
class ShMatrix
{
public:
/// Create an identity matrix of the given order.
ShMatrix(int o = 2) : m_order(o), m_identity(true)
{
nvCheck(m_order > 0);
m_e = new float[size()];
m_band = new float *[bandNum()];
setupBands();
}
/// Destroy and free matrix elements.
~ShMatrix()
{
delete m_e;
delete m_band;
}
/// Set identity matrix.
void setIdentity()
{
m_identity = true;
}
/// Return true if this is an identity matrix, false in other case.
bool isIdentity() const {
return m_identity;
}
/// Get number of bands of this matrix.
int bandNum() const
{
return m_order+1;
}
/// Get total number of elements in the matrix.
int size() const
{
int size = 0;
for (int i = 0; i < bandNum(); i++) {
size += square(i * 2 + 1);
}
return size;
}
/// Get element at the given raw index.
float element(int idx) const
{
return m_e[idx];
}
/// Get element at the given with the given indices.
float & element(int b, int x, int y)
{
nvDebugCheck(b >= 0);
nvDebugCheck(b < bandNum());
return m_band[b][(b + y) * (b * 2 + 1) + (b + x)];
}
/// Get element at the given with the given indices.
float element(int b, int x, int y) const
{
nvDebugCheck(b >= 0);
nvDebugCheck(b < bandNum());
return m_band[b][(b + y) * (b * 2 + 1) + (b + x)];
}
/// Copy matrix.
void copy(const ShMatrix & m)
{
nvDebugCheck(m_order == m.m_order);
memcpy(m_e, m.m_e, size() * sizeof(float));
}
/// Rotate the given coefficients.
/*void transform( const Sh & restrict source, Sh * restrict dest ) const {
nvCheck( &source != dest ); // Make sure there's no aliasing.
nvCheck( dest->m_order <= m_order );
nvCheck( m_order <= source.m_order );
if (m_identity) {
*dest = source;
return;
}
// Loop through each band.
for (int l = 0; l <= dest->m_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;
}
}
}*/
NVMATH_API void multiply( const ShMatrix &A, const ShMatrix &B );
NVMATH_API void rotation( const Matrix & m );
NVMATH_API void rotation( int axis, float angles );
NVMATH_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++ ) {
m_band[i] = &m_e[size];
size += square(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;
};
} // nv namespace
#endif // NV_MATH_SPHERICALHARMONIC_H

View File

@ -6,7 +6,7 @@
#include "nvcore/nvcore.h"
#include "nvcore/Debug.h" // nvDebugCheck
#include "nvcore/Utils.h" // clamp
#include "nvcore/Utils.h" // max, clamp
#include <math.h>
@ -109,7 +109,7 @@ namespace nv
inline bool equal(const float f0, const float f1, const float epsilon = NV_EPSILON)
{
//return fabs(f0-f1) <= epsilon;
return fabs(f0-f1) <= epsilon * max(1.0f, fabs(f0), fabs(f1));
return fabs(f0-f1) <= epsilon * max(1.0f, fabsf(f0), fabsf(f1));
}
inline bool isZero(const float f, const float epsilon = NV_EPSILON)