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); }
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()); if (material_data.m_shader_group) { m_shading_context.execute_osl_emission( *material_data.m_shader_group, light_shading_point); } // 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); Vector3f emission_direction; Spectrum edf_value; float edf_prob; material_data.m_edf->sample( sampling_context, input_evaluator.data(), Vector3f(light_sample.m_geometric_normal), Basis3f(Vector3f(light_sample.m_shading_normal)), sampling_context.next2<Vector2f>(), emission_direction, edf_value, edf_prob); // Compute the initial particle weight. Spectrum initial_flux = edf_value; initial_flux *= dot(emission_direction, Vector3f(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, Vector3d(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.next2<float>(), m_shutter_open_time, m_shutter_close_time); const ShadingRay light_ray( light_sample.m_point, Vector3d(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 /= 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(); }
spectrum PathTracerIntegrator::_trace_ray(const Scene& scene, const Ray& ray, Sampler& sampler, int depth) const { ++rays_traced; if (depth == 1) ++primary_rays_traced; auto isect_opt = scene.intersect(ray); const Vec3 ray_dir_origin = -ray.direction.normal(); if (!isect_opt.is()) return scene.environment_light_emission(-ray_dir_origin); auto isect = isect_opt.get(); spectrum total(0); total += isect.emission(); // direct lighting: randomly choose a light, and contribute // the light from that shape if appropriate scalar light_prob; const auto light = scene.sample_light(sampler.sample_1d(), light_prob); if (light_prob > 0) { LightSample ls = light->sample_emission(isect, sampler); if (ls && ls.p() > 0) { if (!ls.is_occluded(scene)) { scalar NL = max<scalar>(ls.direction().dot(isect.normal), 0.0); auto ca = isect.reflectance(ls.direction(), ray_dir_origin); auto light_contrib = ls.emission() * ca * spectrum{NL / (light_prob * ls.p())}; total += light_contrib; } } } // Decide whether or not to continue trace and, if so, what the multiplier // should be. bool continue_trace = true; scalar p_mult = 1.0; if (opt.max_depth > 0 && depth >= opt.max_depth) continue_trace = false; else if (opt.russian_roulette && depth >= opt.min_rr_depth) { if (sampler.sample_1d() < opt.rr_kill_prob) { continue_trace = false; } else { p_mult /= (1 - opt.rr_kill_prob); } } if (continue_trace) { auto mat_sample = isect.sample_bsdf(ray_dir_origin, sampler); scalar nl = fabs(mat_sample.direction.dot(isect.normal));// max<scalar>(brdf_dir.dot(isect.normal), 0); if (mat_sample.prob > 0 && nl > 0) { total += _trace_ray(scene, Ray{isect.position, mat_sample.direction}.nudge(), sampler, depth + 1) * mat_sample.reflectance * spectrum{p_mult / mat_sample.prob * nl}; } } return total; }
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 EDF* edf = material->get_edf(); // Evaluate the EDF inputs. InputEvaluator input_evaluator(m_texture_cache); // TODO: refactor this code (est.). ShadingPoint shading_point; light_sample.make_shading_point( shading_point, light_sample.m_shading_normal, m_shading_context.get_intersector()); #ifdef WITH_OSL if (const ShaderGroup* sg = material->get_osl_surface()) { // TODO: get object area somehow. const float surface_area = 0.0f; m_shading_context.execute_osl_emission(*sg, shading_point, surface_area); } #endif edf->evaluate_inputs(input_evaluator, shading_point); // Sample the EDF. sampling_context.split_in_place(2, 1); Vector3d emission_direction; Spectrum edf_value; double edf_prob; 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 light_ray( light_sample.m_point, emission_direction, 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, 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(); }
void DirectLightingIntegrator::add_emitting_triangle_sample_contribution( const LightSample& sample, const MISHeuristic mis_heuristic, const Dual3d& outgoing, Spectrum& radiance, SpectrumStack& aovs) const { const Material* material = sample.m_triangle->m_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_point; // Cull light samples behind the shading surface if the BSDF is either reflective or transmissive, // but not both. if (m_bsdf.get_type() != BSDF::AllBSDFTypes) { double cos_in = dot(incoming, m_shading_basis.get_normal()); if (m_bsdf.get_type() == BSDF::Transmissive) cos_in = -cos_in; if (cos_in <= 0.0) return; } // 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 transmission factor between the light sample and the shading point. const double transmission = m_shading_context.get_tracer().trace_between( m_shading_point, sample.m_point, VisibilityFlags::ShadowRay); // Discard occluded samples. if (transmission == 0.0) return; // Compute the square distance between the light sample and the shading point. const double square_distance = square_norm(incoming); const double rcp_sample_square_distance = 1.0 / square_distance; const double rcp_sample_distance = sqrt(rcp_sample_square_distance); // 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; // Normalize the incoming direction. incoming *= rcp_sample_distance; cos_on *= rcp_sample_distance; // Evaluate the BSDF. Spectrum bsdf_value; const double bsdf_prob = m_bsdf.evaluate( m_bsdf_data, false, // not adjoint true, // multiply by |cos(incoming, normal)| m_geometric_normal, m_shading_basis, outgoing.get_value(), incoming, m_light_sampling_modes, bsdf_value); if (bsdf_prob == 0.0) 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()); #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 edf_input_evaluator(m_shading_context.get_texture_cache()); edf->evaluate_inputs(edf_input_evaluator, light_shading_point); // Evaluate the EDF. Spectrum edf_value; edf->evaluate( edf_input_evaluator.data(), sample.m_geometric_normal, Basis3d(sample.m_shading_normal), -incoming, edf_value); const double g = cos_on * rcp_sample_square_distance; double weight = transmission * g / sample.m_probability; // Apply MIS weighting. weight *= mis( mis_heuristic, m_light_sample_count * sample.m_probability, m_bsdf_sample_count * bsdf_prob * g); // Add the contribution of this sample to the illumination. edf_value *= static_cast<float>(weight); edf_value *= bsdf_value; radiance += edf_value; aovs.add(edf->get_render_layer_index(), edf_value); }
bool DirectLightingIntegrator::compute_incoming_radiance( SamplingContext& sampling_context, Vector3d& incoming, double& incoming_prob, Spectrum& radiance) const { if (!m_light_sampler.has_lights_or_emitting_triangles()) return false; sampling_context.split_in_place(3, 1); const Vector3d s = sampling_context.next_vector2<3>(); LightSample sample; m_light_sampler.sample(m_time, s, sample); if (sample.m_triangle) { const Material* material = sample.m_triangle->m_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 false; // Compute the incoming direction in world space. incoming = sample.m_point - m_point; // No contribution if the shading point is behind the light. double cos_on_light = dot(-incoming, sample.m_shading_normal); if (cos_on_light <= 0.0) return false; // Compute the transmission factor between the light sample and the shading point. const double transmission = m_shading_context.get_tracer().trace_between( m_shading_point, sample.m_point, VisibilityFlags::ShadowRay); // Discard occluded samples. if (transmission == 0.0) return false; // Don't use this sample if we're closer than the light near start value. const double square_distance = square_norm(incoming); if (square_distance < square(edf->get_light_near_start())) return false; // Normalize the incoming direction. const double rcp_square_distance = 1.0 / square_distance; const double rcp_distance = sqrt(rcp_square_distance); incoming *= rcp_distance; cos_on_light *= rcp_distance; // 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()); #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 edf_input_evaluator(m_shading_context.get_texture_cache()); edf->evaluate_inputs(edf_input_evaluator, light_shading_point); // Evaluate the EDF. edf->evaluate( edf_input_evaluator.data(), sample.m_geometric_normal, Basis3d(sample.m_shading_normal), -incoming, radiance); // Compute probability with respect to solid angle of incoming direction. const double g = cos_on_light * rcp_square_distance; incoming_prob = sample.m_probability / g; // Compute and return the incoming radiance. radiance *= static_cast<float>(transmission * g / sample.m_probability); } else { 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 false; // Evaluate the light. InputEvaluator input_evaluator(m_shading_context.get_texture_cache()); Vector3d emission_position, emission_direction; light->evaluate( input_evaluator, sample.m_light_transform, m_point, emission_position, emission_direction, radiance); // Compute the transmission factor between the light sample and the shading point. const double transmission = m_shading_context.get_tracer().trace_between( m_shading_point, emission_position, VisibilityFlags::ShadowRay); // Discard occluded samples. if (transmission == 0.0) return false; // Compute the incoming direction in world space. incoming = -emission_direction; incoming_prob = BSDF::DiracDelta; // Compute and return the incoming radiance. const double attenuation = light->compute_distance_attenuation(m_point, emission_position); radiance *= static_cast<float>(transmission * attenuation / sample.m_probability); } return true; }
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); } }