void OSLShaderGroupExec::choose_bsdf_closure_shading_basis(
    const ShadingPoint&             shading_point,
    const Vector2f&                 s) const
{
    CompositeSurfaceClosure c(
        Basis3f(shading_point.get_shading_basis()),
        shading_point.get_osl_shader_globals().Ci,
        m_arena);

    float pdfs[CompositeSurfaceClosure::MaxClosureEntries];
    const size_t num_closures = c.compute_pdfs(ScatteringMode::All, pdfs);
    if (num_closures == 0)
        return;

    const size_t index = c.choose_closure(s[1], num_closures, pdfs);
    shading_point.set_shading_basis(
        Basis3d(c.get_closure_shading_basis(index)));
}
void OSLShaderGroupExec::execute_bump(
    const ShaderGroup&              shader_group,
    const ShadingPoint&             shading_point,
    const Vector2f&                 s) const
{
    // Choose between BSSRDF and BSDF.
    if (shader_group.has_subsurface() && s[0] < 0.5f)
    {
        do_execute(
            shader_group,
            shading_point,
            VisibilityFlags::SubsurfaceRay);

        CompositeSubsurfaceClosure c(
            Basis3f(shading_point.get_shading_basis()),
            shading_point.get_osl_shader_globals().Ci,
            m_arena);

        // Pick a shading basis from one of the BSSRDF closures.
        if (c.get_closure_count() > 0)
        {
            const size_t index = c.choose_closure(s[1]);
            shading_point.set_shading_basis(
                Basis3d(c.get_closure_shading_basis(index)));
        }
    }
    else
    {
        do_execute(
            shader_group,
            shading_point,
            VisibilityFlags::CameraRay);

        choose_bsdf_closure_shading_basis(shading_point, s);
    }
}
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;
}