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; }
void trace_non_physical_light_photon( SamplingContext& sampling_context, const LightSample& light_sample) { // Sample the light. InputEvaluator input_evaluator(m_texture_cache); SamplingContext child_sampling_context = sampling_context.split(2, 1); Vector3d emission_position, emission_direction; Spectrum light_value; double light_prob; light_sample.m_light->sample( input_evaluator, child_sampling_context.next_vector2<2>(), emission_position, emission_direction, light_value, light_prob); // Transform the emission position and direction from assembly space to world space. emission_position = light_sample.m_light_transform.point_to_parent(emission_position); emission_direction = normalize(light_sample.m_light_transform.vector_to_parent(emission_direction)); // Compute the initial particle weight. Spectrum initial_flux = light_value; initial_flux /= static_cast<float>(light_sample.m_probability * light_prob); // Build the photon ray. child_sampling_context.split_in_place(1, 1); const ShadingRay ray( emission_position, emission_direction, child_sampling_context.next_double2(), ~0); // Build the path tracer. const bool cast_indirect_light = (light_sample.m_light->get_flags() & EDF::CastIndirectLight) != 0; PathVisitor path_visitor( initial_flux, m_params.m_dl_mode == SPPMParameters::SPPM, // store direct lighting photons? cast_indirect_light, m_params.m_enable_caustics, m_local_photons); PathTracer<PathVisitor, true> path_tracer( // true = adjoint path_visitor, m_params.m_photon_tracing_rr_min_path_length, m_params.m_photon_tracing_max_path_length, m_params.m_max_iterations); // Trace the photon path. path_tracer.trace( child_sampling_context, m_intersector, m_texture_cache, ray); }
void trace_emitting_triangle_photon( SamplingContext& sampling_context, LightSample& light_sample) { // 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 EDF* edf = light_sample.m_triangle->m_edf; // Evaluate the EDF inputs. InputEvaluator input_evaluator(m_texture_cache); edf->evaluate_inputs(input_evaluator, light_sample.m_bary); // Sample the EDF. SamplingContext child_sampling_context = sampling_context.split(2, 1); Vector3d emission_direction; Spectrum edf_value; double edf_prob; edf->sample( input_evaluator.data(), light_sample.m_geometric_normal, Basis3d(light_sample.m_shading_normal), child_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 * m_params.m_light_photon_count)); // 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 photon ray. child_sampling_context.split_in_place(1, 1); const ShadingRay ray( light_sample.m_point, emission_direction, child_sampling_context.next_double2(), ShadingRay::LightRay); // Build the path tracer. const bool cast_indirect_light = (edf->get_flags() & EDF::CastIndirectLight) != 0; PathVisitor path_visitor( initial_flux, m_params.m_dl_mode == SPPMParameters::SPPM, // store direct lighting photons? cast_indirect_light, m_params.m_enable_caustics, m_local_photons); PathTracer<PathVisitor, true> path_tracer( // true = adjoint path_visitor, m_params.m_photon_tracing_rr_min_path_length, m_params.m_photon_tracing_max_path_length, m_params.m_max_iterations, edf->get_light_near_start()); // don't illuminate points closer than the light near start value // Trace the photon path. path_tracer.trace( child_sampling_context, m_shading_context, ray, &parent_shading_point); }
void DirectLightingIntegrator::add_non_physical_light_sample_contribution( SamplingContext& sampling_context, const LightSample& sample, const Dual3d& outgoing, DirectShadingComponents& radiance, LightPathStream* light_path_stream) const { const Light* light = sample.m_light; // No contribution if we are computing indirect lighting but this light does not cast indirect light. if (m_indirect && !(light->get_flags() & Light::CastIndirectLight)) return; // Generate a uniform sample in [0,1)^2. SamplingContext child_sampling_context = sampling_context.split(2, 1); const Vector2d s = child_sampling_context.next2<Vector2d>(); // Evaluate the light. Vector3d emission_position, emission_direction; Spectrum light_value(Spectrum::Illuminance); float probability; light->sample( m_shading_context, sample.m_light_transform, m_material_sampler.get_point(), s, emission_position, emission_direction, light_value, probability); // Compute the incoming direction in world space. const Vector3d incoming = -emission_direction; // Compute the transmission factor between the light sample and the shading point. Spectrum transmission; m_material_sampler.trace_between( m_shading_context, emission_position, transmission); // Discard occluded samples. if (is_zero(transmission)) return; // Evaluate the BSDF (or volume). DirectShadingComponents material_value; const float material_probability = m_material_sampler.evaluate( Vector3f(outgoing.get_value()), Vector3f(incoming), m_light_sampling_modes, material_value); assert(material_probability >= 0.0f); if (material_probability == 0.0f) return; // Add the contribution of this sample to the illumination. const float attenuation = light->compute_distance_attenuation( m_material_sampler.get_point(), emission_position); light_value *= transmission; light_value *= attenuation / (sample.m_probability * probability); madd(radiance, material_value, light_value); // Record light path event. if (light_path_stream) { light_path_stream->sampled_non_physical_light( *light, emission_position, material_value.m_beauty, light_value); } }
void DirectLightingIntegrator::add_emitting_shape_sample_contribution( SamplingContext& sampling_context, const LightSample& sample, const MISHeuristic mis_heuristic, const Dual3d& outgoing, DirectShadingComponents& radiance, LightPathStream* light_path_stream) const { const Material* material = sample.m_shape->get_material(); const Material::RenderData& material_data = material->get_render_data(); const EDF* edf = material_data.m_edf; // No contribution if we are computing indirect lighting but this light does not cast indirect light. if (m_indirect && !(edf->get_flags() & EDF::CastIndirectLight)) return; // Compute the incoming direction in world space. Vector3d incoming = sample.m_point - m_material_sampler.get_point(); // No contribution if the shading point is behind the light. double cos_on = dot(-incoming, sample.m_shading_normal); if (cos_on <= 0.0) return; // Compute the square distance between the light sample and the shading point. const double square_distance = square_norm(incoming); // Don't use this sample if we're closer than the light near start value. if (square_distance < square(edf->get_light_near_start())) return; const double rcp_sample_square_distance = 1.0 / square_distance; const double rcp_sample_distance = sqrt(rcp_sample_square_distance); // Normalize the incoming direction. cos_on *= rcp_sample_distance; incoming *= rcp_sample_distance; // Probabilistically skip light samples with low maximum contribution. float contribution_prob = 1.0f; if (m_low_light_threshold > 0.0f) { // Compute the approximate maximum contribution of this light sample. const float max_contribution = static_cast<float>( cos_on * rcp_sample_square_distance * sample.m_shape->get_max_flux()); // Use Russian Roulette to skip this sample if its maximum contribution is low. if (max_contribution < m_low_light_threshold) { // Generate a uniform sample in [0,1). SamplingContext child_sampling_context = sampling_context.split(1, 1); const float s = child_sampling_context.next2<float>(); // Compute the probability of taking the sample's contribution into account. contribution_prob = max_contribution / m_low_light_threshold; // Russian Roulette. if (!pass_rr(contribution_prob, s)) return; } } // Compute the transmission factor between the light sample and the shading point. Spectrum transmission; m_material_sampler.trace_between( m_shading_context, sample.m_point, transmission); // Discard occluded samples. if (is_zero(transmission)) return; // Evaluate the BSDF (or volume). DirectShadingComponents material_value; const float material_probability = m_material_sampler.evaluate( Vector3f(outgoing.get_value()), Vector3f(incoming), m_light_sampling_modes, material_value); assert(material_probability >= 0.0f); if (material_probability == 0.0f) return; // Build a shading point on the light source. ShadingPoint light_shading_point; sample.make_shading_point( light_shading_point, sample.m_shading_normal, m_shading_context.get_intersector()); if (material_data.m_shader_group) { m_shading_context.execute_osl_emission( *material_data.m_shader_group, light_shading_point); } // Evaluate the EDF. Spectrum edf_value(Spectrum::Illuminance); edf->evaluate( edf->evaluate_inputs(m_shading_context, light_shading_point), Vector3f(sample.m_geometric_normal), Basis3f(Vector3f(sample.m_shading_normal)), -Vector3f(incoming), edf_value); const float g = static_cast<float>(cos_on * rcp_sample_square_distance); // Apply MIS weighting. const float mis_weight = mis( mis_heuristic, m_light_sample_count * sample.m_probability, m_material_sample_count * material_probability * g); // Add the contribution of this sample to the illumination. edf_value *= transmission; edf_value *= (mis_weight * g) / (sample.m_probability * contribution_prob); madd(radiance, material_value, edf_value); // Record light path event. if (light_path_stream) { light_path_stream->sampled_emitting_shape( *sample.m_shape, sample.m_point, material_value.m_beauty, edf_value); } }