FORCE_INLINE virtual double evaluate( const void* data, const bool adjoint, const bool cosine_mult, const Vector3d& geometric_normal, const Basis3d& shading_basis, const Vector3d& outgoing, const Vector3d& incoming, const int modes, Spectrum& value) const { if (!(modes & BSDFSample::Diffuse)) return 0.0; const Vector3d& n = shading_basis.get_normal(); const double cos_in = abs(dot(incoming, n)); // Compute the BRDF value. const InputValues* values = static_cast<const InputValues*>(data); value = values->m_transmittance; value *= static_cast<float>(values->m_transmittance_multiplier * RcpPi); // Return the probability density of the sampled direction. return cos_in * RcpPi; }
double compute_fast_ambient_occlusion( const SamplingContext& sampling_context, const AOVoxelTreeIntersector& intersector, const Vector3d& point, const Vector3d& geometric_normal, const Basis3d& shading_basis, const double max_distance, const size_t sample_count, double& min_distance) { // Create a sampling context. SamplingContext child_sampling_context = sampling_context.split(2, sample_count); // Construct the ambient occlusion ray. ShadingRay::RayType ray; ray.m_org = point; ray.m_tmin = 0.0; ray.m_tmax = max_distance; size_t computed_samples = 0; size_t occluded_samples = 0; min_distance = max_distance; for (size_t i = 0; i < sample_count; ++i) { // Generate a cosine-weighted direction over the unit hemisphere. ray.m_dir = sample_hemisphere_cosine(child_sampling_context.next_vector2<2>()); // Transform the direction to world space. ray.m_dir = shading_basis.transform_to_parent(ray.m_dir); // Don't cast rays on or below the geometric surface. if (dot(ray.m_dir, geometric_normal) <= 0.0) continue; // Count the number of computed samples. ++computed_samples; // Trace the ambient occlusion ray and count the number of occluded samples. double distance; if (intersector.trace(ray, true, distance)) { ++occluded_samples; min_distance = min(min_distance, distance); } } // Compute occlusion as a scalar between 0.0 and 1.0. double occlusion = static_cast<double>(occluded_samples); if (computed_samples > 1) occlusion /= computed_samples; assert(occlusion >= 0.0); assert(occlusion <= 1.0); return occlusion; }
FORCE_INLINE virtual double evaluate_pdf( const void* data, const Vector3d& geometric_normal, const Basis3d& shading_basis, const Vector3d& outgoing, const Vector3d& incoming, const int modes) const { if (!(modes & Glossy)) return 0.0; // If incoming and outgoing are on the same hemisphere // this is not a refraction. const Vector3d& n = shading_basis.get_normal(); if (dot(incoming, n) * dot(outgoing, n) >= 0.0) return Absorption; const InputValues* values = static_cast<const InputValues*>(data); double ht_norm; const Vector3d ht = half_refraction_vector( outgoing, incoming, n, values->m_from_ior, values->m_to_ior, ht_norm); const Vector3d m = shading_basis.transform_to_local(ht); const double dwh_dwo = refraction_jacobian( incoming, values->m_to_ior, ht, ht_norm); return m_mdf->pdf(m, values->m_ax, values->m_ay) * dwh_dwo; }
FORCE_INLINE virtual double evaluate_pdf( const void* data, const Vector3d& geometric_normal, const Basis3d& shading_basis, const Vector3d& outgoing, const Vector3d& incoming, const int modes) const { if (!(modes & BSDFSample::Diffuse)) return 0.0; const Vector3d& n = shading_basis.get_normal(); const double cos_in = abs(dot(incoming, n)); return cos_in * RcpPi; }
size_t generate_environment_sample( SamplingContext& sampling_context, const EnvironmentEDF* env_edf, SampleVector& samples) { // Sample the environment. sampling_context.split_in_place(2, 1); InputEvaluator input_evaluator(m_texture_cache); Vector3f outgoing; Spectrum env_edf_value; float env_edf_prob; env_edf->sample( m_shading_context, input_evaluator, sampling_context.next2<Vector2f>(), outgoing, // points toward the environment env_edf_value, env_edf_prob); // Uniformly sample the tangent disk. sampling_context.split_in_place(2, 1); const Vector2d p = m_scene_radius * sample_disk_uniform(sampling_context.next2<Vector2d>()); // Compute the origin of the light ray. const Basis3d basis(-Vector3d(outgoing)); const Vector3d ray_origin = m_scene_center - m_safe_scene_diameter * basis.get_normal() // a safe radius would have been sufficient + p[0] * basis.get_tangent_u() + + p[1] * basis.get_tangent_v(); // Compute the initial particle weight. Spectrum initial_flux = env_edf_value; initial_flux /= m_disk_point_prob * env_edf_prob; // Build the light ray. sampling_context.split_in_place(1, 1); const ShadingRay::Time time = ShadingRay::Time::create_with_normalized_time( sampling_context.next2<float>(), m_shutter_open_time, m_shutter_close_time); const ShadingRay light_ray( ray_origin, -Vector3d(outgoing), time, VisibilityFlags::LightRay, 0); // Build the path tracer. PathVisitor path_visitor( m_params, m_scene, m_frame, m_shading_context, sampling_context, samples, initial_flux); PathTracerType path_tracer( path_visitor, m_params.m_rr_min_path_length, m_params.m_max_path_length, m_params.m_max_iterations); // Trace the light path. const size_t path_length = path_tracer.trace( sampling_context, m_shading_context, light_ray); // Update path statistics. ++m_path_count; m_path_length.insert(path_length); // Return the number of samples generated when tracing this light path. return path_visitor.get_sample_count(); }
size_t generate_environment_sample( SamplingContext& sampling_context, const EnvironmentEDF* env_edf, SampleVector& samples) { // Sample the environment. sampling_context.split_in_place(2, 1); InputEvaluator input_evaluator(m_texture_cache); Vector3d outgoing; Spectrum env_edf_value; double env_edf_prob; env_edf->sample( input_evaluator, sampling_context.next_vector2<2>(), outgoing, // points toward the environment env_edf_value, env_edf_prob); // Compute the center of the tangent disk. const Vector3d disk_center = m_safe_scene_radius * outgoing; // Uniformly sample the tangent disk. sampling_context.split_in_place(2, 1); const Vector2d disk_point = m_safe_scene_radius * sample_disk_uniform(sampling_context.next_vector2<2>()); // Compute the origin of the light ray. const Basis3d basis(-outgoing); const Vector3d ray_origin = disk_center + disk_point[0] * basis.get_tangent_u() + disk_point[1] * basis.get_tangent_v(); // Compute the initial particle weight. Spectrum initial_flux = env_edf_value; initial_flux /= static_cast<float>(m_disk_point_prob * env_edf_prob); // Build the light ray. sampling_context.split_in_place(1, 1); const ShadingRay light_ray( ray_origin, -outgoing, sampling_context.next_double2(), m_ray_dtime, ShadingRay::LightRay); // Build the path tracer. PathVisitor path_visitor( m_params, m_scene, m_frame, m_shading_context, samples, initial_flux); PathTracerType path_tracer( path_visitor, m_params.m_rr_min_path_length, m_params.m_max_path_length, m_params.m_max_iterations); // Trace the light path. const size_t path_length = path_tracer.trace( sampling_context, m_shading_context, light_ray); // Update path statistics. ++m_path_count; m_path_length.insert(path_length); // Return the number of samples generated when tracing this light path. return path_visitor.get_sample_count(); }
FORCE_INLINE virtual double evaluate( const void* data, const bool adjoint, const bool cosine_mult, const Vector3d& geometric_normal, const Basis3d& shading_basis, const Vector3d& outgoing, const Vector3d& incoming, const int modes, Spectrum& value) const { if (!(modes & Glossy)) return 0.0; // If incoming and outgoing are on the same hemisphere // this is not a refraction. const Vector3d& n = shading_basis.get_normal(); if (dot(incoming, n) * dot(outgoing, n) >= 0.0) return Absorption; const InputValues* values = static_cast<const InputValues*>(data); double ht_norm; const Vector3d ht = half_refraction_vector( outgoing, incoming, n, values->m_from_ior, values->m_to_ior, ht_norm); const Vector3d m = shading_basis.transform_to_local(ht); const double G = m_mdf->G( shading_basis.transform_to_local(incoming), shading_basis.transform_to_local(outgoing), m, values->m_ax, values->m_ay); if (G == 0.0) return 0.0; const double D = m_mdf->D(m, values->m_ax, values->m_ay); const double cos_oh = dot(outgoing, ht); const double cos_ih = dot(incoming, ht); const double cos_in = dot(incoming, n); const double cos_on = dot(outgoing, n); // [1] equation 21. double v = abs((cos_ih * cos_oh) / (cos_in * cos_on)); v *= square(values->m_to_ior) * D * G; const double denom = values->m_to_ior * cos_ih + values->m_from_ior * cos_oh; v /= square(denom); value.set(static_cast<float>(v)); const double dwh_dwo = refraction_jacobian( incoming, values->m_to_ior, ht, ht_norm); return m_mdf->pdf(m, values->m_ax, values->m_ay) * dwh_dwo; }
FORCE_INLINE virtual Mode sample( SamplingContext& sampling_context, const void* data, const bool adjoint, const bool cosine_mult, const Vector3d& geometric_normal, const Basis3d& shading_basis, const Vector3d& outgoing, Vector3d& incoming, Spectrum& value, double& probability) const { const InputValues* values = static_cast<const InputValues*>(data); // Compute the incoming direction by sampling the MDF. sampling_context.split_in_place(2, 1); const Vector2d s = sampling_context.next_vector2<2>(); const Vector3d m = m_mdf->sample(s, values->m_ax, values->m_ay); const Vector3d ht = shading_basis.transform_to_parent(m); if (!refract( outgoing, ht, values->m_from_ior / values->m_to_ior, incoming)) { // Ignore TIR. return Absorption; } // If incoming and outgoing are on the same hemisphere // this is not a refraction. const Vector3d& n = shading_basis.get_normal(); if (dot(incoming, n) * dot(outgoing, n) >= 0.0) return Absorption; const double G = m_mdf->G( shading_basis.transform_to_local(incoming), shading_basis.transform_to_local(outgoing), m, values->m_ax, values->m_ay); if (G == 0.0) return Absorption; const double D = m_mdf->D(m, values->m_ax, values->m_ay); const double cos_oh = dot(outgoing, ht); const double cos_ih = dot(incoming, ht); const double cos_in = dot(incoming, n); const double cos_on = dot(outgoing, n); // [1] equation 21. double v = abs((cos_ih * cos_oh) / (cos_in * cos_on)); v *= square(values->m_to_ior) * D * G; const double denom = values->m_to_ior * cos_ih + values->m_from_ior * cos_oh; v /= square(denom); value.set(static_cast<float>(v)); const double ht_norm = norm(values->m_from_ior * outgoing + values->m_to_ior * incoming); const double dwh_dwo = refraction_jacobian( incoming, values->m_to_ior, ht, ht_norm); probability = m_mdf->pdf(m, values->m_ax, values->m_ay) * dwh_dwo; return Glossy; }