void weak_white_furnace_test( size_t num_runs, const float alpha_x, const float alpha_y, const float gamma, const float angle_step, WeakWhiteFurnaceTestResult& result) { result.m_min_G1 = numeric_limits<float>::max(); result.m_max_G1 = -numeric_limits<float>::max(); result.m_min_result = numeric_limits<float>::max(); result.m_max_result = -numeric_limits<float>::max(); MDF mdf; for (size_t i = 0; i < num_runs; ++i) { static const size_t Bases[] = { 2 }; const Vector2f s = hammersley_sequence<float, 2>(Bases, num_runs, i); const Vector3f v = sample_hemisphere_uniform(s); const float G1 = mdf.G1(v, Vector3f(0.0f, 1.0, 0.0f), alpha_x, alpha_y, gamma); result.m_min_G1 = std::min(result.m_min_G1, G1); result.m_max_G1 = std::max(result.m_max_G1, G1); const float cos_thetha_o_4 = std::abs(4.0f * v.y); float integral = 0.0f; for (float theta = 0.0f; theta < Pi<float>(); theta += angle_step) { const float cos_theta = std::cos(theta); const float sin_theta = std::sin(theta); for (float phi = 0.0f; phi < TwoPi<float>(); phi += angle_step) { const float cos_phi = std::cos(phi); const float sin_phi = std::sin(phi); const Vector3f l = Vector3f::make_unit_vector( cos_theta, sin_theta, cos_phi, sin_phi); const Vector3f h = normalize(v + l); if (h.y > 0.0f) integral += sin_theta * mdf.D(h, alpha_x, alpha_y, gamma) * G1 / cos_thetha_o_4; } } // Result should be 1. integral *= square(angle_step); result.m_min_result = std::min(result.m_min_result, integral); result.m_max_result = std::max(result.m_max_result, integral); } }
void weak_white_furnace_test( size_t num_runs, const double alpha_x, const double alpha_y, const double angle_step, WeakWhiteFurnaceTestResult& result) { result.m_min_G1 = numeric_limits<double>::max(); result.m_max_G1 = -numeric_limits<double>::max(); result.m_min_result = numeric_limits<double>::max(); result.m_max_result = -numeric_limits<double>::max(); MDF mdf; for (size_t i = 0; i < num_runs; ++i) { static const size_t Bases[] = { 2 }; const Vector2d s = hammersley_sequence<double, 2>(Bases, num_runs, i); const Vector3d v = sample_hemisphere_uniform(s); const double G1 = mdf.G1(v, Vector3d(0.0, 1.0, 0.0), alpha_x, alpha_y); result.m_min_G1 = std::min(result.m_min_G1, G1); result.m_max_G1 = std::max(result.m_max_G1, G1); const double cos_thetha_o_4 = std::abs(4.0 * v.y); double integral = 0.0; for (double theta = 0.0; theta < Pi<double>(); theta += angle_step) { const double cos_theta = std::cos(theta); const double sin_theta = std::sin(theta); for (double phi = 0.0; phi < TwoPi<double>(); phi += angle_step) { const double cos_phi = std::cos(phi); const double sin_phi = std::sin(phi); const Vector3d l = Vector3d::make_unit_vector( cos_theta, sin_theta, cos_phi, sin_phi); const Vector3d h = normalize(v + l); if (h.y > 0.0) integral += sin_theta * mdf.D(h, alpha_x, alpha_y) * G1 / cos_thetha_o_4; } } // Result should be 1. integral *= square(angle_step); result.m_min_result = std::min(result.m_min_result, integral); result.m_max_result = std::max(result.m_max_result, 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); }
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 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; }
bool is_positive( const MDF& mdf, const typename MDF::ValueType alpha_x, const typename MDF::ValueType alpha_y, const size_t 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, sample_count, i); const Vector<typename MDF::ValueType, 3> h = sample_hemisphere_uniform(s); const double value = mdf.D(h, alpha_x, alpha_y); if (value < 0.0) return false; } return true; }
bool is_positive( const MDF& mdf, const float alpha_x, const float alpha_y, const size_t sample_count, const float gamma = 0.0f) { for (size_t i = 0; i < sample_count; ++i) { static const size_t Bases[] = { 2 }; const Vector2f s = hammersley_sequence<float, 2>(Bases, sample_count, i); const Vector3f h = sample_hemisphere_uniform(s); const float value = mdf.D(h, alpha_x, alpha_y, gamma); if (value < 0.0f) return false; } return true; }
float integrate( const MDF& mdf, const float alpha, const size_t sample_count, const float gamma = 0.0f) { float integral = 0.0f; for (size_t i = 0; i < sample_count; ++i) { const float theta = radical_inverse_base2<float>(i) * HalfPi<float>(); const Vector3f h(0.0f, cos(theta), 0.0f); const float value = mdf.D(h, alpha, alpha, gamma); integral += value * h.y * sin(theta); } integral *= HalfPi<float>() / sample_count; // integration over theta integral *= TwoPi<float>(); // integration over phi return integral; }
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; }
typename MDF::ValueType integrate( const MDF& mdf, const typename MDF::ValueType alpha, const size_t sample_count) { typedef typename MDF::ValueType ValueType; ValueType integral = ValueType(0.0); for (size_t i = 0; i < sample_count; ++i) { const ValueType theta = radical_inverse_base2<ValueType>(i) * HalfPi<ValueType>(); const Vector<ValueType, 3> h(ValueType(0.0), cos(theta), ValueType(0.0)); const ValueType value = mdf.D(h, alpha, alpha); integral += value * h.y * sin(theta); } integral *= HalfPi<ValueType>() / sample_count; // integration over theta integral *= TwoPi<ValueType>(); // integration over phi return integral; }