/*
C++ implementation of TVCJ 2020 / CGI 2020 paper "Constant-Time Energy-Normalization for the Phong
Specular BRDFs" by Ian Mallett and Cem Yuksel, which currently can be found here:
https://graphics.geometrian.com/research/normalize_phong.html
This implementation is explicitly released to the public domain.
Warning: this code was derived from tested, correct code, but it has not, itself, been directly
tested, at least completely. Let us know if you discover issues!
*/
#include
#include
#define TWO_PI 6.2831853f
#define SQRT_PI 1.77245385f
//Incomplete Beta function.
// This is just one possible implementation from Boost. You can use whatever.
inline static float ibeta( float x, float a,float b ) {
return boost::math::beta( a,b, x );
}
//Computes Γ(a) / Γ(b).
inline static float gamma_quot( float a, float b ) {
return std::exp( std::lgamma(a) - std::lgamma(b) );
}
//Calculate normalization terms for the Phong and Modified Phong BRDFs, respectively.
// All you have to do is divide the BRDF by its term (see also Eqn.s 2 and 3 in the paper).
inline static float calc_I_P( float NdotV, float n ) {
float const& costerm = NdotV;
float sinterm_sq = 1.0f - costerm*costerm;
float halfn = 0.5f * n;
return (
TWO_PI -
SQRT_PI * gamma_quot( halfn+1.0f, halfn+0.5f ) * ibeta( sinterm_sq, halfn+0.5f,0.5f )
) / (n+1.0f);
}
inline static float calc_I_M( float NdotV, float n ) {
float const& costerm = NdotV;
float sinterm_sq = 1.0f - costerm*costerm;
float halfn = 0.5f * n;
float negterm = costerm;
//The epsilon chosen here doesn't matter a lot. We found we could make it around 10⁻³⁷, but
// that's probably not great to rely upon, and 10⁻¹⁸ is plenty tiny.
if (n>=1e-18f) {
negterm *= halfn * ibeta( sinterm_sq, halfn,0.5f );
}
return (
TWO_PI * costerm +
SQRT_PI * gamma_quot( halfn+0.5f, halfn+1.0f ) * ( std::pow(sinterm_sq,halfn) - negterm )
) / (n+2.0f);
}