void plot( MapleFile& file, const string& name, const MDF& mdf, const size_t point_count, const size_t sample_count) { vector<double> angles(point_count); vector<double> densities(point_count); for (size_t i = 0; i < point_count; ++i) { const double angle = fit( static_cast<double>(i), 0.0, static_cast<double>(point_count - 1), -HalfPi, +HalfPi); const double cos_angle = cos(angle); angles[i] = rad_to_deg(angle); densities[i] = mdf.evaluate(cos_angle) * cos_angle; } vector<double> angle_samples(sample_count); vector<double> density_samples(sample_count); for (size_t i = 0; i < sample_count; ++i) { static const size_t Bases[] = { 2 }; const Vector2d s = hammersley_sequence<double, 2>(Bases, i, sample_count); const Vector3d w = mdf.sample(s); const double cos_angle = w.y; const double angle = acos(cos_angle) * (w.x < 0.0 ? -1.0 : 1.0); angle_samples[i] = rad_to_deg(angle); density_samples[i] = mdf.evaluate(cos_angle) * cos_angle; } file.define(name, angles, densities); file.define(name + "_samples", angle_samples, density_samples); file.plot( make_vector( MaplePlotDef(name) .set_legend("Microfacet Distribution Function (" + name + ")"), MaplePlotDef(name + "_samples") .set_legend("Integration Samples") .set_style("point") .set_color("red"))); }
double integrate_sampling( const MDF& mdf, const Sampler& sampler, const size_t sample_count) { double integral = 0.0; for (size_t i = 0; i < sample_count; ++i) { static const size_t Bases[] = { 2 }; const Vector2d s = hammersley_sequence<double, 2>(Bases, i, sample_count); const Vector3d w = sampler.sample(s); const double pdf = sampler.pdf(w); const double cos_theta = w.y; const double value = mdf.evaluate(cos_theta); const double sample = value / pdf; integral += sample * cos_theta; } integral /= static_cast<double>(sample_count); return integral; }
static void compute_specular_albedo( const MDF& mdf, const Spectrum& rs, const Vector3d& V, Spectrum& albedo) { // V must lie above or in the surface. assert(V.y >= 0.0); albedo.set(0.0f); for (size_t i = 0; i < AlbedoSampleCount; ++i) { // Generate a uniform sample in [0,1)^2. static const size_t Bases[] = { 2 }; const Vector2d s = hammersley_sequence<double, 2>(Bases, i, AlbedoSampleCount); // Sample the microfacet distribution to get an halfway vector H. const Vector3d H = mdf.sample(s); const double dot_HV = dot(H, V); if (dot_HV <= 0.0) continue; // L is the reflection of V around H. const Vector3d L = (dot_HV + dot_HV) * H - V; // Reject L if it lies in or below the surface. if (L.y <= 0.0) continue; // Evaluate the PDF of L. const double dot_HN = H.y; const double pdf_H = mdf.evaluate_pdf(dot_HN); const double pdf_L = pdf_H / (4.0 * dot_HV); assert(pdf_L >= 0.0); if (pdf_L == 0.0) continue; // Sanity checks. assert(is_normalized(V)); assert(is_normalized(H)); assert(is_normalized(L)); // Evaluate the specular component for this (L, V) pair. Spectrum fr_spec; fr_spec = fresnel_dielectric_schlick(rs, dot_HV); fr_spec *= static_cast<float>((L.y * mdf.evaluate(dot_HN)) / (4.0 * pdf_L * dot_HV * dot_HV)); albedo += fr_spec; } albedo /= static_cast<float>(AlbedoSampleCount); }
static void evaluate_fr_spec( const MDF& mdf, const Spectrum& rs, const double dot_HL, // cos_beta in the paper const double dot_HN, Spectrum& fr_spec) { assert(dot_HL >= 0.0); assert(dot_HN >= 0.0); fr_spec = fresnel_dielectric_schlick(rs, dot_HL); fr_spec *= static_cast<float>(mdf.evaluate(dot_HN) / (4.0 * dot_HL * dot_HL)); }
bool is_positive( const MDF& mdf, const size_t sample_count) { for (size_t i = 0; i < sample_count; ++i) { const double theta = radical_inverse_base2<double>(i) * HalfPi; const double cos_theta = cos(theta); const double value = mdf.evaluate(cos_theta); if (value < 0.0) return false; } return true; }
double integrate_quadrature( const MDF& mdf, const size_t sample_count) { double integral = 0.0; for (size_t i = 0; i < sample_count; ++i) { const double theta = radical_inverse_base2<double>(i) * HalfPi; const double cos_theta = cos(theta); const double sin_theta = sin(theta); const double value = mdf.evaluate(cos_theta); integral += value * cos_theta * sin_theta; } integral *= HalfPi / sample_count; // integration over theta integral *= TwoPi; // integration over phi return integral; }