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()));
}
Example #2
0
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);
}
Example #5
0
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);
}
Example #6
0
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);
}
Example #7
0
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);
}
Example #8
0
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);
    }
}