void DirectLightingIntegrator::compute_outgoing_radiance_combined_sampling_low_variance( SamplingContext& sampling_context, const Dual3d& outgoing, Spectrum& radiance, SpectrumStack& aovs) const { compute_outgoing_radiance_bsdf_sampling( sampling_context, MISPower2, outgoing, radiance, aovs); Spectrum radiance_light_sampling; SpectrumStack aovs_light_sampling(aovs.size()); compute_outgoing_radiance_light_sampling_low_variance( sampling_context, MISPower2, outgoing, radiance_light_sampling, aovs_light_sampling); radiance += radiance_light_sampling; aovs += aovs_light_sampling; }
void DirectLightingIntegrator::compute_outgoing_radiance_light_sampling( SamplingContext& sampling_context, const MISHeuristic mis_heuristic, const Dual3d& outgoing, Spectrum& radiance, SpectrumStack& aovs) const { radiance.set(0.0f); aovs.set(0.0f); sampling_context.split_in_place(3, m_light_sample_count); for (size_t i = 0; i < m_light_sample_count; ++i) { take_single_light_sample( sampling_context, mis_heuristic, outgoing, radiance, aovs); } if (m_light_sample_count > 1) { const float rcp_light_sample_count = 1.0f / m_light_sample_count; radiance *= rcp_light_sample_count; aovs *= rcp_light_sample_count; } }
void DirectLightingIntegrator::compute_outgoing_radiance_bsdf_sampling( SamplingContext& sampling_context, const MISHeuristic mis_heuristic, 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) return; for (size_t i = 0; i < m_bsdf_sample_count; ++i) { take_single_bsdf_sample( sampling_context, mis_heuristic, outgoing, radiance, aovs); } if (m_bsdf_sample_count > 1) { const float rcp_bsdf_sample_count = 1.0f / m_bsdf_sample_count; radiance *= rcp_bsdf_sample_count; aovs *= rcp_bsdf_sample_count; } }
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 add_back_lighting( const InputValues& values, SamplingContext& sampling_context, const PixelContext& pixel_context, const ShadingContext& shading_context, const ShadingPoint& shading_point, Spectrum& radiance, SpectrumStack& aovs) const { const Vector3d& p = shading_point.get_point(); const Vector3d& n = shading_point.get_original_shading_normal(); const Vector3d& d = shading_point.get_ray().m_dir; // Construct a ray perpendicular to the other side of the surface. ShadingRay back_ray(shading_point.get_ray()); back_ray.m_tmax *= norm(d); back_ray.m_dir = dot(d, n) > 0.0 ? -n : n; back_ray.m_org = p - back_ray.m_tmax * back_ray.m_dir; ShadingPoint back_shading_point(shading_point); back_shading_point.set_ray(back_ray); Spectrum back_radiance(0.0f); SpectrumStack back_aovs(aovs.size(), 0.0f); // Compute back lighting. for (size_t i = 0; i < m_back_lighting_samples; ++i) { shading_context.get_lighting_engine()->compute_lighting( sampling_context, pixel_context, shading_context, back_shading_point, back_radiance, back_aovs); } // Apply translucency factor. back_radiance *= values.m_translucency; back_aovs *= values.m_translucency; // Divide by the number of samples. const float rcp_sample_count = 1.0f / static_cast<float>(m_back_lighting_samples); back_radiance *= rcp_sample_count; back_aovs *= rcp_sample_count; // Add back lighting contribution. radiance += back_radiance; aovs += back_aovs; }
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); } }
void DirectLightingIntegrator::sample_bsdf_and_lights_low_variance( SamplingContext& sampling_context, Spectrum& radiance, SpectrumStack& aovs) { sample_bsdf( sampling_context, DirectLightingIntegrator::mis_power2, radiance, aovs); Spectrum radiance_light_sampling; SpectrumStack aovs_light_sampling(aovs.size()); sample_lights_low_variance( sampling_context, DirectLightingIntegrator::mis_power2, radiance_light_sampling, aovs_light_sampling); radiance += radiance_light_sampling; aovs += aovs_light_sampling; }
void DirectLightingIntegrator::add_non_physical_light_sample_contribution( const LightSample& sample, Spectrum& radiance, SpectrumStack& aovs) { 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; // Evaluate the light. InputEvaluator input_evaluator(m_shading_context.get_texture_cache()); Vector3d emission_position, emission_direction; Spectrum light_value; light->evaluate( input_evaluator, sample.m_light_transform.point_to_local(m_point), emission_position, emission_direction, light_value); // Transform the emission position and direction from assembly space to world space. emission_position = sample.m_light_transform.point_to_parent(emission_position); emission_direction = normalize(sample.m_light_transform.vector_to_parent(emission_direction)); // Compute the incoming direction in world space. const Vector3d incoming = -emission_direction; // Cull light samples behind the shading surface. 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; // 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, ShadingRay::ShadowRay); // Discard occluded samples. if (transmission == 0.0) return; // 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, m_outgoing, incoming, m_light_sampling_modes, bsdf_value); if (bsdf_prob == 0.0) return; // Add the contribution of this sample to the illumination. const double attenuation = light->compute_distance_attenuation(m_point, emission_position); const double weight = (transmission * attenuation) / sample.m_probability; light_value *= static_cast<float>(weight); light_value *= bsdf_value; radiance += light_value; aovs.add(light->get_render_layer_index(), light_value); }
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); }
void DirectLightingIntegrator::take_single_bsdf_sample( SamplingContext& sampling_context, const MISHeuristic mis_heuristic, const Dual3d& outgoing, Spectrum& radiance, SpectrumStack& aovs) const { assert(m_light_sampler.get_emitting_triangle_count() > 0); // Sample the BSDF. BSDFSample sample(m_shading_point, outgoing); m_bsdf.sample( sampling_context, m_bsdf_data, false, // not adjoint true, // multiply by |cos(incoming, normal)| sample); // Filter scattering modes. if (!(m_bsdf_sampling_modes & sample.m_mode)) return; // Trace a ray in the direction of the reflection. double weight; const ShadingPoint& light_shading_point = m_shading_context.get_tracer().trace( m_shading_point, sample.m_incoming.get_value(), VisibilityFlags::ShadowRay, weight); // todo: wouldn't it be more efficient to look the environment up at this point? if (!light_shading_point.hit()) return; // Retrieve the material at the intersection point. const Material* material = light_shading_point.get_material(); if (material == 0) return; const Material::RenderData& material_data = material->get_render_data(); // Retrieve the EDF at the intersection point. const EDF* edf = material_data.m_edf; if (edf == 0) return; // 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; // Cull the samples on the back side of the lights' shading surface. const double cos_on = dot(-sample.m_incoming.get_value(), light_shading_point.get_shading_normal()); if (cos_on <= 0.0) return; #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 emitted radiance. Spectrum edf_value; double edf_prob; edf->evaluate( edf_input_evaluator.data(), light_shading_point.get_geometric_normal(), light_shading_point.get_shading_basis(), -sample.m_incoming.get_value(), edf_value, edf_prob); if (edf_prob == 0.0) return; // Compute the square distance between the light sample and the shading point. const double square_distance = square(light_shading_point.get_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; if (mis_heuristic != MISNone && square_distance > 0.0) { // Transform bsdf_prob to surface area measure (Veach: 8.2.2.2 eq. 8.10). const double bsdf_prob_area = sample.m_probability * cos_on / square_distance; // Compute the probability density wrt. surface area mesure of the light sample. const double light_prob_area = m_light_sampler.evaluate_pdf(light_shading_point); // Apply the weighting function. weight *= mis( mis_heuristic, m_bsdf_sample_count * bsdf_prob_area, m_light_sample_count * light_prob_area); } // Add the contribution of this sample to the illumination. edf_value *= static_cast<float>(weight / sample.m_probability); edf_value *= sample.m_value; radiance += edf_value; aovs.add(edf->get_render_layer_index(), edf_value); }
void DirectLightingIntegrator::compute_outgoing_radiance_light_sampling_low_variance( SamplingContext& sampling_context, const MISHeuristic mis_heuristic, const Dual3d& outgoing, Spectrum& radiance, SpectrumStack& aovs) const { radiance.set(0.0f); aovs.set(0.0f); // todo: if we had a way to know that a BSDF is purely specular, we could // immediately return black here since there will be no contribution from // such a BSDF. // Sample emitting triangles. if (m_light_sampler.get_emitting_triangle_count() > 0) { sampling_context.split_in_place(3, m_light_sample_count); for (size_t i = 0; i < m_light_sample_count; ++i) { const Vector3d s = sampling_context.next_vector2<3>(); LightSample sample; m_light_sampler.sample_emitting_triangles(m_time, s, sample); add_emitting_triangle_sample_contribution( sample, mis_heuristic, outgoing, radiance, aovs); } if (m_light_sample_count > 1) { const float rcp_light_sample_count = 1.0f / m_light_sample_count; radiance *= rcp_light_sample_count; aovs *= rcp_light_sample_count; } } // Sample non-physical light sources. const size_t light_count = m_light_sampler.get_non_physical_light_count(); if (light_count > 0) { sampling_context.split_in_place(2, light_count); for (size_t i = 0; i < light_count; ++i) { const Vector2d s = sampling_context.next_vector2<2>(); LightSample sample; m_light_sampler.sample_non_physical_light(m_time, s, i, sample); add_non_physical_light_sample_contribution( sample, outgoing, radiance, aovs); } } }
void DirectLightingIntegrator::add_light_sample_contribution( const LightSample& sample, Spectrum& radiance, SpectrumStack& aovs) { // Compute the incoming direction in world space. Vector3d incoming = sample.m_point - m_point; // Cull light samples behind the shading surface. 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; // Compute the transmission factor between the light sample and the shading point. const double transmission = m_shading_context.get_tracer().trace_between( m_point, sample.m_point, m_time, m_parent_shading_point); // Discard occluded samples. if (transmission == 0.0) return; // Compute the square distance between the light sample and the shading point. const double rcp_sample_square_distance = 1.0 / square_norm(incoming); const double rcp_sample_distance = sqrt(rcp_sample_square_distance); // Normalize the incoming direction. incoming *= rcp_sample_distance; cos_in *= 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, m_outgoing, incoming, m_bsdf_modes, bsdf_value); if (bsdf_prob == 0.0) return; const Light* light = sample.m_light; // Evaluate the input values of the light. InputEvaluator light_input_evaluator(m_shading_context.get_texture_cache()); light->evaluate_inputs(light_input_evaluator, -incoming); // Evaluate the light. Spectrum light_value; light->evaluate( light_input_evaluator.data(), -incoming, light_value); // Add the contribution of this sample to the illumination. const double weight = transmission * rcp_sample_square_distance / sample.m_probability; light_value *= static_cast<float>(weight); light_value *= bsdf_value; radiance += light_value; aovs.add(light->get_render_layer_index(), radiance); }