void DirectLightingIntegrator::take_single_bsdf_or_light_sample( SamplingContext& sampling_context, Spectrum& radiance, SpectrumStack& aovs) { radiance.set(0.0f); aovs.set(0.0f); sampling_context.split_in_place(1, 1); if (sampling_context.next_double2() < 0.5) { sampling_context.split_in_place(3, m_light_sample_count); take_single_light_sample( sampling_context, DirectLightingIntegrator::mis_balance, radiance, aovs); } else { take_single_bsdf_sample( sampling_context, DirectLightingIntegrator::mis_balance, radiance, aovs); } }
void Camera::initialize_ray( SamplingContext& sampling_context, ShadingRay& ray) const { ray.m_tmin = 0.0; ray.m_tmax = numeric_limits<double>::max(); ray.m_flags = VisibilityFlags::CameraRay; ray.m_depth = 0; if (m_shutter_open_time == m_shutter_close_time) { ray.m_time = ShadingRay::Time::create_with_normalized_time( 0.0, m_shutter_open_time, m_shutter_close_time); } else { sampling_context.split_in_place(1, 1); ray.m_time = ShadingRay::Time::create_with_normalized_time( sampling_context.next_double2(), m_shutter_open_time, m_shutter_close_time); } }
void DirectLightingIntegrator::compute_outgoing_radiance_single_sample( SamplingContext& sampling_context, const Dual3d& outgoing, Spectrum& radiance, SpectrumStack& aovs) const { radiance.set(0.0f); aovs.set(0.0f); if (m_light_sampler.get_emitting_triangle_count() > 0) { sampling_context.split_in_place(1, 1); if (sampling_context.next_double2() < 0.5) { sampling_context.split_in_place(3, 1); take_single_light_sample( sampling_context, MISBalance, outgoing, radiance, aovs); } else { take_single_bsdf_sample( sampling_context, MISBalance, outgoing, radiance, aovs); } radiance *= 2.0f; aovs *= 2.0f; } else { take_single_light_sample( sampling_context, MISNone, outgoing, radiance, aovs); } }
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( m_shading_context, input_evaluator, sampling_context.next_vector2<2>(), 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.next_vector2<2>()); // Compute the origin of the light ray. const Basis3d basis(-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 /= static_cast<float>(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.next_double2(), m_shutter_open_time, m_shutter_close_time); const ShadingRay light_ray( ray_origin, -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_non_physical_light_sample( SamplingContext& sampling_context, const LightSample& light_sample, SampleVector& samples) { // Sample the light. InputEvaluator input_evaluator(m_texture_cache); sampling_context.split_in_place(2, 1); Vector3d emission_position, emission_direction; Spectrum light_value; double light_prob; light_sample.m_light->sample( input_evaluator, light_sample.m_light_transform, sampling_context.next_vector2<2>(), emission_position, emission_direction, light_value, light_prob); // Compute the initial particle weight. Spectrum initial_flux = light_value; initial_flux /= static_cast<float>(light_sample.m_probability * light_prob); // Build the light ray. sampling_context.split_in_place(1, 1); const ShadingRay::Time time = ShadingRay::Time::create_with_normalized_time( sampling_context.next_double2(), m_shutter_open_time, m_shutter_close_time); const ShadingRay light_ray( emission_position, emission_direction, 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); // Handle the light vertex separately. Spectrum light_particle_flux = light_value; light_particle_flux /= static_cast<float>(light_sample.m_probability); path_visitor.visit_non_physical_light_vertex( emission_position, light_particle_flux, light_ray.m_time); // 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_emitting_triangle_sample( SamplingContext& sampling_context, LightSample& light_sample, SampleVector& samples) { // Make sure the geometric normal of the light sample is in the same hemisphere as the shading normal. light_sample.m_geometric_normal = flip_to_same_hemisphere( light_sample.m_geometric_normal, light_sample.m_shading_normal); const Material* material = light_sample.m_triangle->m_material; const Material::RenderData& material_data = material->get_render_data(); // Build a shading point on the light source. ShadingPoint light_shading_point; light_sample.make_shading_point( light_shading_point, light_sample.m_shading_normal, m_shading_context.get_intersector()); #ifdef APPLESEED_WITH_OSL if (material_data.m_shader_group) { m_shading_context.execute_osl_emission( *material_data.m_shader_group, light_shading_point); } #endif // Evaluate the EDF inputs. InputEvaluator input_evaluator(m_texture_cache); material_data.m_edf->evaluate_inputs(input_evaluator, light_shading_point); // Sample the EDF. sampling_context.split_in_place(2, 1); Vector3d emission_direction; Spectrum edf_value; double edf_prob; material_data.m_edf->sample( sampling_context, input_evaluator.data(), light_sample.m_geometric_normal, Basis3d(light_sample.m_shading_normal), sampling_context.next_vector2<2>(), emission_direction, edf_value, edf_prob); // Compute the initial particle weight. Spectrum initial_flux = edf_value; initial_flux *= static_cast<float>( dot(emission_direction, light_sample.m_shading_normal) / (light_sample.m_probability * edf_prob)); // Make a shading point that will be used to avoid self-intersections with the light sample. ShadingPoint parent_shading_point; light_sample.make_shading_point( parent_shading_point, emission_direction, m_intersector); // Build the light ray. sampling_context.split_in_place(1, 1); const ShadingRay::Time time = ShadingRay::Time::create_with_normalized_time( sampling_context.next_double2(), m_shutter_open_time, m_shutter_close_time); const ShadingRay light_ray( light_sample.m_point, emission_direction, 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, material_data.m_edf->get_light_near_start()); // don't illuminate points closer than the light near start value // Handle the light vertex separately. Spectrum light_particle_flux = edf_value; // todo: only works for diffuse EDF? What we need is the light exitance light_particle_flux /= static_cast<float>(light_sample.m_probability); path_visitor.visit_area_light_vertex( light_sample, light_particle_flux, light_ray.m_time); // Trace the light path. const size_t path_length = path_tracer.trace( sampling_context, m_shading_context, light_ray, &parent_shading_point); // 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 SubsurfaceSampler::sample( SamplingContext& sampling_context, const ShadingPoint& outgoing_point, const BSSRDF& bssrdf, const void* bssrdf_data, SubsurfaceSample samples[], const size_t max_sample_count) { assert(max_sample_count > 0); // Sample the diffusion profile. BSSRDFSample bssrdf_sample(sampling_context); if (!bssrdf.sample(bssrdf_data, bssrdf_sample)) return 0; // Reject points too far away. // This introduces negligible bias in comparison to the other approximations. const Vector2d& point(bssrdf_sample.get_point()); const double radius2 = square_norm(point); const double rmax2 = bssrdf_sample.get_rmax2(); if (radius2 > rmax2) return 0; // Evaluate the PDF of the diffusion profile. const double radius = sqrt(radius2); const double bssrdf_sample_pdf = bssrdf.evaluate_pdf(bssrdf_data, bssrdf_sample.get_channel(), radius); // Pick a sampling basis. sampling_context.split_in_place(1, 1); Axis sampling_axis; Basis3d sampling_basis; double sampling_basis_pdf; pick_sampling_basis( outgoing_point.get_shading_basis(), sampling_context.next_double2(), sampling_axis, sampling_basis, sampling_basis_pdf); // Compute height of sample point on (positive) hemisphere of radius Rmax. assert(rmax2 >= radius2); const double h = sqrt(rmax2 - radius2); // Compute sphere entry and exit points. Vector3d entry_point, exit_point; entry_point = exit_point = outgoing_point.get_point(); entry_point += sampling_basis.transform_to_parent(Vector3d(point[0], +h, point[1])); exit_point += sampling_basis.transform_to_parent(Vector3d(point[0], -h, point[1])); assert(feq(norm(exit_point - entry_point), 2.0 * h, 1.0e-9)); // Build a probe ray inscribed inside the sphere of radius Rmax. ShadingRay probe_ray( entry_point, -sampling_basis.get_normal(), 0.0, 2.0 * h, outgoing_point.get_time(), VisibilityFlags::ProbeRay, outgoing_point.get_ray().m_depth + 1); const Material* material = outgoing_point.get_material(); ShadingPoint shading_points[2]; size_t shading_point_index = 0; ShadingPoint* parent_shading_point = 0; size_t sample_count = 0; // Trace the ray and return all intersections (or up to max_sample_count of them) found inside the sphere. while (true) { // Continue tracing the ray. shading_points[shading_point_index].clear(); if (!m_shading_context.get_intersector().trace( probe_ray, shading_points[shading_point_index], parent_shading_point)) break; // Only consider points lying on surfaces with the same material as the outgoing point. if (shading_points[shading_point_index].get_material() == material) { // Execute the OSL shader if we have one. Needed for bump mapping. #ifdef APPLESEED_WITH_OSL if (material->has_osl_surface()) { sampling_context.split_in_place(1, 1); m_shading_context.execute_osl_bump( *material->get_osl_surface(), shading_points[shading_point_index], sampling_context.next_double2()); } #endif SubsurfaceSample& sample = samples[sample_count++]; sample.m_point = shading_points[shading_point_index]; // Compute sample probability. sample.m_probability = bssrdf_sample_pdf * sampling_basis_pdf * abs(dot(sampling_basis.get_normal(), sample.m_point.get_geometric_normal())); // todo: or shading normal? // Weight sample probability with multiple importance sampling. sample.m_probability /= compute_mis_weight( bssrdf, bssrdf_data, bssrdf_sample.get_channel(), sampling_basis, sampling_axis, sample.m_probability, outgoing_point.get_point(), sample.m_point.get_point(), sample.m_point.get_geometric_normal()); // todo: or shading normal? // Return the relative index of refraction. sample.m_eta = bssrdf_sample.get_eta(); if (sample_count == max_sample_count) break; } // Move the ray's origin past the hit surface. probe_ray.m_org = shading_points[shading_point_index].get_point(); probe_ray.m_tmax = norm(exit_point - probe_ray.m_org); // Swap the current and parent shading points. parent_shading_point = &shading_points[shading_point_index]; shading_point_index = 1 - shading_point_index; } return sample_count; }