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);
    }
}
Example #5
0
        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);
}