DirectLightingIntegrator::DirectLightingIntegrator( const ShadingContext& shading_context, const LightSampler& light_sampler, const ShadingPoint& shading_point, const Dual3d& outgoing, const BSDF& bsdf, const void* bsdf_data, const int bsdf_sampling_modes, const int light_sampling_modes, const size_t bsdf_sample_count, const size_t light_sample_count, const bool indirect) : m_shading_context(shading_context) , m_light_sampler(light_sampler) , m_shading_point(shading_point) , m_point(shading_point.get_point()) , m_geometric_normal(shading_point.get_geometric_normal()) , m_shading_basis(shading_point.get_shading_basis()) , m_time(shading_point.get_time()) , m_outgoing(outgoing) , m_bsdf(bsdf) , m_bsdf_data(bsdf_data) , m_bsdf_sampling_modes(bsdf_sampling_modes) , m_light_sampling_modes(light_sampling_modes) , m_bsdf_sample_count(bsdf_sample_count) , m_light_sample_count(light_sample_count) , m_indirect(indirect) { assert(is_normalized(outgoing.get_value())); }
void compute_ibl( SamplingContext& sampling_context, const ShadingContext& shading_context, const EnvironmentEDF& environment_edf, const ShadingPoint& shading_point, const Dual3d& outgoing, const BSDF& bsdf, const void* bsdf_data, const int bsdf_sampling_modes, const int env_sampling_modes, const size_t bsdf_sample_count, const size_t env_sample_count, Spectrum& radiance) { assert(is_normalized(outgoing.get_value())); // Compute IBL by sampling the BSDF. compute_ibl_bsdf_sampling( sampling_context, shading_context, environment_edf, shading_point, outgoing, bsdf, bsdf_data, bsdf_sampling_modes, bsdf_sample_count, env_sample_count, radiance); // Compute IBL by sampling the environment. Spectrum radiance_env_sampling; compute_ibl_environment_sampling( sampling_context, shading_context, environment_edf, shading_point, outgoing, bsdf, bsdf_data, env_sampling_modes, bsdf_sample_count, env_sample_count, radiance_env_sampling); radiance += radiance_env_sampling; }
void DirectLightingIntegrator::add_non_physical_light_sample_contribution( const LightSample& sample, const Dual3d& outgoing, Spectrum& radiance, SpectrumStack& aovs) 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; // 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, m_point, emission_position, emission_direction, light_value); // Compute the incoming direction in world space. const Vector3d incoming = -emission_direction; // 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; } // 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; // 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; // 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 compute_ibl_environment_sampling( SamplingContext& sampling_context, const ShadingContext& shading_context, const EnvironmentEDF& environment_edf, const BSSRDF& bssrdf, const void* bssrdf_data, const ShadingPoint& incoming_point, const ShadingPoint& outgoing_point, const Dual3d& outgoing, const size_t bssrdf_sample_count, const size_t env_sample_count, Spectrum& radiance) { assert(is_normalized(outgoing.get_value())); const Basis3d& shading_basis = incoming_point.get_shading_basis(); radiance.set(0.0f); sampling_context.split_in_place(2, env_sample_count); for (size_t i = 0; i < env_sample_count; ++i) { // Generate a uniform sample in [0,1)^2. const Vector2d s = sampling_context.next_vector2<2>(); // Sample the environment. InputEvaluator input_evaluator(shading_context.get_texture_cache()); Vector3d incoming; Spectrum env_value; double env_prob; environment_edf.sample( shading_context, input_evaluator, s, incoming, env_value, env_prob); // Cull samples behind the shading surface. assert(is_normalized(incoming)); const double cos_in = dot(incoming, shading_basis.get_normal()); if (cos_in <= 0.0) continue; // Discard occluded samples. const double transmission = shading_context.get_tracer().trace( incoming_point, incoming, VisibilityFlags::ShadowRay); if (transmission == 0.0) continue; // Evaluate the BSSRDF. Spectrum bssrdf_value; bssrdf.evaluate( bssrdf_data, outgoing_point, outgoing.get_value(), incoming_point, incoming, bssrdf_value); // Compute MIS weight. const double bssrdf_prob = cos_in * RcpPi; const double mis_weight = mis_power2( env_sample_count * env_prob, bssrdf_sample_count * bssrdf_prob); // Add the contribution of this sample to the illumination. env_value *= static_cast<float>(transmission * cos_in / env_prob * mis_weight); env_value *= bssrdf_value; radiance += env_value; } if (env_sample_count > 1) radiance /= static_cast<float>(env_sample_count); }
void compute_ibl_environment_sampling( SamplingContext& sampling_context, const ShadingContext& shading_context, const EnvironmentEDF& environment_edf, const ShadingPoint& shading_point, const Dual3d& outgoing, const BSDF& bsdf, const void* bsdf_data, const int env_sampling_modes, const size_t bsdf_sample_count, const size_t env_sample_count, Spectrum& radiance) { assert(is_normalized(outgoing.get_value())); const Vector3d& geometric_normal = shading_point.get_geometric_normal(); const Basis3d& shading_basis = shading_point.get_shading_basis(); radiance.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. sampling_context.split_in_place(2, env_sample_count); for (size_t i = 0; i < env_sample_count; ++i) { // Generate a uniform sample in [0,1)^2. const Vector2d s = sampling_context.next_vector2<2>(); // Sample the environment. InputEvaluator input_evaluator(shading_context.get_texture_cache()); Vector3d incoming; Spectrum env_value; double env_prob; environment_edf.sample( shading_context, input_evaluator, s, incoming, env_value, env_prob); // Cull samples behind the shading surface. assert(is_normalized(incoming)); const double cos_in = dot(incoming, shading_basis.get_normal()); if (cos_in < 0.0) continue; // Discard occluded samples. const double transmission = shading_context.get_tracer().trace( shading_point, incoming, VisibilityFlags::ShadowRay); if (transmission == 0.0) continue; // Evaluate the BSDF. Spectrum bsdf_value; const double bsdf_prob = bsdf.evaluate( bsdf_data, false, // not adjoint true, // multiply by |cos(incoming, normal)| geometric_normal, shading_basis, outgoing.get_value(), incoming, env_sampling_modes, bsdf_value); if (bsdf_prob == 0.0) continue; // Compute MIS weight. const double mis_weight = mis_power2( env_sample_count * env_prob, bsdf_sample_count * bsdf_prob); // Add the contribution of this sample to the illumination. env_value *= static_cast<float>(transmission / env_prob * mis_weight); env_value *= bsdf_value; radiance += env_value; } if (env_sample_count > 1) radiance /= static_cast<float>(env_sample_count); }
void compute_ibl_bssrdf_sampling( SamplingContext& sampling_context, const ShadingContext& shading_context, const EnvironmentEDF& environment_edf, const BSSRDF& bssrdf, const void* bssrdf_data, const ShadingPoint& incoming_point, const ShadingPoint& outgoing_point, const Dual3d& outgoing, const size_t bssrdf_sample_count, const size_t env_sample_count, Spectrum& radiance) { assert(is_normalized(outgoing.get_value())); radiance.set(0.0f); sampling_context.split_in_place(2, bssrdf_sample_count); for (size_t i = 0; i < bssrdf_sample_count; ++i) { // Generate a uniform sample in [0,1)^2. const Vector2d s = sampling_context.next_vector2<2>(); // Sample the BSSRDF (hemisphere cosine). Vector3d incoming = sample_hemisphere_cosine(s); const double cos_in = incoming.y; const double bssrdf_prob = cos_in * RcpPi; incoming = incoming_point.get_shading_basis().transform_to_parent(incoming); if (incoming_point.get_side() == ObjectInstance::BackSide) incoming = -incoming; assert(is_normalized(incoming)); // Discard occluded samples. const double transmission = shading_context.get_tracer().trace( incoming_point, incoming, VisibilityFlags::ShadowRay); if (transmission == 0.0) continue; // Evaluate the BSSRDF. Spectrum bssrdf_value; bssrdf.evaluate( bssrdf_data, outgoing_point, outgoing.get_value(), incoming_point, incoming, bssrdf_value); // Evaluate the environment's EDF. InputEvaluator input_evaluator(shading_context.get_texture_cache()); Spectrum env_value; double env_prob; environment_edf.evaluate( shading_context, input_evaluator, incoming, env_value, env_prob); // Compute MIS weight. const double mis_weight = mis_power2( bssrdf_sample_count * bssrdf_prob, env_sample_count * env_prob); // Add the contribution of this sample to the illumination. env_value *= static_cast<float>(transmission * cos_in / bssrdf_prob * mis_weight); env_value *= bssrdf_value; radiance += env_value; } if (bssrdf_sample_count > 1) radiance /= static_cast<float>(bssrdf_sample_count); }
void compute_ibl_bsdf_sampling( SamplingContext& sampling_context, const ShadingContext& shading_context, const EnvironmentEDF& environment_edf, const ShadingPoint& shading_point, const Dual3d& outgoing, const BSDF& bsdf, const void* bsdf_data, const int bsdf_sampling_modes, const size_t bsdf_sample_count, const size_t env_sample_count, Spectrum& radiance) { assert(is_normalized(outgoing.get_value())); radiance.set(0.0f); for (size_t i = 0; i < bsdf_sample_count; ++i) { // Sample the BSDF. // todo: rendering will be incorrect if the BSDF value returned by the sample() method // includes the contribution of a specular component since these are explicitly rejected // afterward. We need a mechanism to indicate that we want the contribution of some of // the components only. BSDFSample sample( shading_point, outgoing); bsdf.sample( sampling_context, bsdf_data, false, // not adjoint true, // multiply by |cos(incoming, normal)| sample); // Filter scattering modes. if (!(bsdf_sampling_modes & sample.m_mode)) continue; // Discard occluded samples. const double transmission = shading_context.get_tracer().trace( shading_point, sample.m_incoming.get_value(), VisibilityFlags::ShadowRay); if (transmission == 0.0) continue; // Evaluate the environment's EDF. InputEvaluator input_evaluator(shading_context.get_texture_cache()); Spectrum env_value; double env_prob; environment_edf.evaluate( shading_context, input_evaluator, sample.m_incoming.get_value(), env_value, env_prob); // Apply all weights, including MIS weight. if (sample.m_mode == ScatteringMode::Specular) env_value *= static_cast<float>(transmission); else { const double mis_weight = mis_power2( bsdf_sample_count * sample.m_probability, env_sample_count * env_prob); env_value *= static_cast<float>(transmission / sample.m_probability * mis_weight); } // Add the contribution of this sample to the illumination. env_value *= sample.m_value; radiance += env_value; } if (bsdf_sample_count > 1) radiance /= static_cast<float>(bsdf_sample_count); }
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); } }