static real ChiSquare(creal x, cint df) { real y; if( df <= 0 ) return -999; if( x <= 0 ) return 0; if( x > 1000*df ) return 1; if( df > 1000 ) { if( x < 2 ) return 0; y = 2./(9*df); y = (powx(x/df, 1/3.) - (1 - y))/sqrtx(y); if( y > 5 ) return 1; if( y < -18.8055 ) return 0; return Normal(y); } y = .5*x; if( df & 1 ) { creal sqrty = sqrtx(y); real h = Erf(sqrty); count i; if( df == 1 ) return h; y = sqrty*expx(-y)/.8862269254527579825931; for( i = 3; i < df; i += 2 ) { h -= y; y *= x/i; } y = h - y; } else { real term = expx(-y), sum = term; count i; for( i = 1; i < df/2; ++i ) sum += term *= y/i; y = 1 - sum; } return Max(0., y); }
inline hvl_float CalcB1pB2(const HVL_Context *ctx, const hvl_float &a3, const hvl_float &sqrtz) { const hvl_float b1 = ctx->HVL_ONE / (Exp(a3) - ctx->HVL_ONE); const hvl_float b2 = ctx->HVL_HALF * (ctx->HVL_ONE + Erf(sqrtz)); return b1 + b2; }
inline double HVL_da3_internal(const HVL_Context *ctx, const hvl_float &a0, const hvl_float &a3, const hvl_float &gauss_base, const hvl_float &b1pb2, const hvl_float &reca3, const hvl_float &sqrtz) { if (a3 == ctx->HVL_ZERO) return ToDouble(-ctx->HVL_HALF * a0 * Erf(sqrtz) * gauss_base); const hvl_float a3e = Exp(a3); const hvl_float nmr = gauss_base * a0 * (-reca3 + (a3e / (Pow(a3e - ctx->HVL_ONE, 2) * b1pb2))); return ToDouble(reca3 * nmr / b1pb2); }
// Microfacet Utility Functions static void BeckmannSample11(Float cosThetaI, Float U1, Float U2, Float *slope_x, Float *slope_y) { /* Special case (normal incidence) */ if (cosThetaI > .9999) { Float r = std::sqrt(-std::log(1.0f - U1)); Float sinPhi = std::sin(2 * Pi * U2); Float cosPhi = std::cos(2 * Pi * U2); *slope_x = r * cosPhi; *slope_y = r * sinPhi; } /* The original inversion routine from the paper contained discontinuities, which causes issues for QMC integration and techniques like Kelemen-style MLT. The following code performs a numerical inversion with better behavior */ Float sinThetaI = std::sqrt(std::max((Float)0, (Float)1 - cosThetaI * cosThetaI)); Float tanThetaI = sinThetaI / cosThetaI; Float cotThetaI = 1 / tanThetaI; /* Search interval -- everything is parameterized in the Erf() domain */ Float a = -1, c = Erf(cotThetaI); Float sample_x = std::max(U1, (Float)1e-6f); /* Start with a good initial guess */ // Float b = (1-sample_x) * a + sample_x * c; /* We can do better (inverse of an approximation computed in * Mathematica) */ Float thetaI = std::acos(cosThetaI); Float fit = 1 + thetaI * (-0.876f + thetaI * (0.4265f - 0.0594f * thetaI)); Float b = c - (1 + c) * std::pow(1 - sample_x, fit); /* Normalization factor for the CDF */ static const Float SQRT_PI_INV = 1.f / std::sqrt(Pi); Float normalization = 1 / (1 + c + SQRT_PI_INV * tanThetaI * std::exp(-cotThetaI * cotThetaI)); int it = 0; while (++it < 10) { /* Bisection criterion -- the oddly-looking Boolean expression are intentional to check for NaNs at little additional cost */ if (!(b >= a && b <= c)) b = 0.5f * (a + c); /* Evaluate the CDF and its derivative (i.e. the density function) */ Float invErf = ErfInv(b); Float value = normalization * (1 + b + SQRT_PI_INV * tanThetaI * std::exp(-invErf * invErf)) - sample_x; Float derivative = normalization * (1 - invErf * tanThetaI); if (std::abs(value) < 1e-5f) break; /* Update bisection intervals */ if (value > 0) c = b; else a = b; b -= value / derivative; } /* Now convert back into a slope value */ *slope_x = ErfInv(b); /* Simulate Y component */ *slope_y = ErfInv(2.0f * std::max(U2, (Float)1e-6f) - 1.0f); Assert(!std::isinf(*slope_x)); Assert(!std::isnan(*slope_x)); Assert(!std::isinf(*slope_y)); Assert(!std::isnan(*slope_y)); }
static inline real Normal(creal x) { return .5*Erf(x/1.414213562373095048801689) + .5; }