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 #2
0
void Camera::initialize_ray(
    SamplingContext&    sampling_context,
    ShadingRay&         ray) const
{
    ray.m_tmin = 0.0;
    ray.m_tmax = numeric_limits<double>::max();
    ray.m_flags = VisibilityFlags::CameraRay;
    ray.m_depth = 0;

    if (m_shutter_open_time == m_shutter_close_time)
    {
        ray.m_time =
            ShadingRay::Time::create_with_normalized_time(
                0.0,
                m_shutter_open_time,
                m_shutter_close_time);
    }
    else
    {
        sampling_context.split_in_place(1, 1);
        ray.m_time =
            ShadingRay::Time::create_with_normalized_time(
                sampling_context.next_double2(),
                m_shutter_open_time,
                m_shutter_close_time);
    }
}
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);
    }
}
    size_t generate_environment_sample(
        SamplingContext&            sampling_context,
        const EnvironmentEDF*       env_edf,
        SampleVector&               samples)
    {
        // Sample the environment.
        sampling_context.split_in_place(2, 1);
        InputEvaluator input_evaluator(m_texture_cache);
        Vector3d outgoing;
        Spectrum env_edf_value;
        double env_edf_prob;
        env_edf->sample(
            m_shading_context,
            input_evaluator,
            sampling_context.next_vector2<2>(),
            outgoing,               // points toward the environment
            env_edf_value,
            env_edf_prob);

        // Uniformly sample the tangent disk.
        sampling_context.split_in_place(2, 1);
        const Vector2d p =
            m_scene_radius
            * sample_disk_uniform(sampling_context.next_vector2<2>());

        // Compute the origin of the light ray.
        const Basis3d basis(-outgoing);
        const Vector3d ray_origin =
            m_scene_center
            - m_safe_scene_diameter * basis.get_normal()    // a safe radius would have been sufficient
            + p[0] * basis.get_tangent_u() +
            + p[1] * basis.get_tangent_v();

        // Compute the initial particle weight.
        Spectrum initial_flux = env_edf_value;
        initial_flux /= static_cast<float>(m_disk_point_prob * env_edf_prob);

        // Build the light ray.
        sampling_context.split_in_place(1, 1);
        const ShadingRay::Time time =
            ShadingRay::Time::create_with_normalized_time(
                sampling_context.next_double2(),
                m_shutter_open_time,
                m_shutter_close_time);
        const ShadingRay light_ray(
            ray_origin,
            -outgoing,
            time,
            VisibilityFlags::LightRay,
            0);

        // Build the path tracer.
        PathVisitor path_visitor(
            m_params,
            m_scene,
            m_frame,
            m_shading_context,
            sampling_context,
            samples,
            initial_flux);
        PathTracerType path_tracer(
            path_visitor,
            m_params.m_rr_min_path_length,
            m_params.m_max_path_length,
            m_params.m_max_iterations);

        // Trace the light path.
        const size_t path_length =
            path_tracer.trace(
                sampling_context,
                m_shading_context,
                light_ray);

        // Update path statistics.
        ++m_path_count;
        m_path_length.insert(path_length);

        // Return the number of samples generated when tracing this light path.
        return path_visitor.get_sample_count();
    }
    size_t generate_non_physical_light_sample(
        SamplingContext&            sampling_context,
        const LightSample&          light_sample,
        SampleVector&               samples)
    {
        // Sample the light.
        InputEvaluator input_evaluator(m_texture_cache);
        sampling_context.split_in_place(2, 1);
        Vector3d emission_position, emission_direction;
        Spectrum light_value;
        double light_prob;
        light_sample.m_light->sample(
            input_evaluator,
            light_sample.m_light_transform,
            sampling_context.next_vector2<2>(),
            emission_position,
            emission_direction,
            light_value,
            light_prob);

        // Compute the initial particle weight.
        Spectrum initial_flux = light_value;
        initial_flux /= static_cast<float>(light_sample.m_probability * light_prob);

        // Build the light ray.
        sampling_context.split_in_place(1, 1);
        const ShadingRay::Time time =
            ShadingRay::Time::create_with_normalized_time(
                sampling_context.next_double2(),
                m_shutter_open_time,
                m_shutter_close_time);
        const ShadingRay light_ray(
            emission_position,
            emission_direction,
            time,
            VisibilityFlags::LightRay,
            0);

        // Build the path tracer.
        PathVisitor path_visitor(
            m_params,
            m_scene,
            m_frame,
            m_shading_context,
            sampling_context,
            samples,
            initial_flux);
        PathTracerType path_tracer(
            path_visitor,
            m_params.m_rr_min_path_length,
            m_params.m_max_path_length,
            m_params.m_max_iterations);

        // Handle the light vertex separately.
        Spectrum light_particle_flux = light_value;
        light_particle_flux /= static_cast<float>(light_sample.m_probability);
        path_visitor.visit_non_physical_light_vertex(
            emission_position,
            light_particle_flux,
            light_ray.m_time);

        // Trace the light path.
        const size_t path_length =
            path_tracer.trace(
                sampling_context,
                m_shading_context,
                light_ray);

        // Update path statistics.
        ++m_path_count;
        m_path_length.insert(path_length);

        // Return the number of samples generated when tracing this light path.
        return path_visitor.get_sample_count();
    }
    size_t generate_emitting_triangle_sample(
        SamplingContext&            sampling_context,
        LightSample&                light_sample,
        SampleVector&               samples)
    {
        // Make sure the geometric normal of the light sample is in the same hemisphere as the shading normal.
        light_sample.m_geometric_normal =
            flip_to_same_hemisphere(
                light_sample.m_geometric_normal,
                light_sample.m_shading_normal);

        const Material* material = light_sample.m_triangle->m_material;
        const Material::RenderData& material_data = material->get_render_data();

        // Build a shading point on the light source.
        ShadingPoint light_shading_point;
        light_sample.make_shading_point(
            light_shading_point,
            light_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 input_evaluator(m_texture_cache);
        material_data.m_edf->evaluate_inputs(input_evaluator, light_shading_point);

        // Sample the EDF.
        sampling_context.split_in_place(2, 1);
        Vector3d emission_direction;
        Spectrum edf_value;
        double edf_prob;
        material_data.m_edf->sample(
            sampling_context,
            input_evaluator.data(),
            light_sample.m_geometric_normal,
            Basis3d(light_sample.m_shading_normal),
            sampling_context.next_vector2<2>(),
            emission_direction,
            edf_value,
            edf_prob);

        // Compute the initial particle weight.
        Spectrum initial_flux = edf_value;
        initial_flux *=
            static_cast<float>(
                dot(emission_direction, light_sample.m_shading_normal)
                / (light_sample.m_probability * edf_prob));

        // Make a shading point that will be used to avoid self-intersections with the light sample.
        ShadingPoint parent_shading_point;
        light_sample.make_shading_point(
            parent_shading_point,
            emission_direction,
            m_intersector);

        // Build the light ray.
        sampling_context.split_in_place(1, 1);
        const ShadingRay::Time time =
            ShadingRay::Time::create_with_normalized_time(
                sampling_context.next_double2(),
                m_shutter_open_time,
                m_shutter_close_time);
        const ShadingRay light_ray(
            light_sample.m_point,
            emission_direction,
            time,
            VisibilityFlags::LightRay,
            0);

        // Build the path tracer.
        PathVisitor path_visitor(
            m_params,
            m_scene,
            m_frame,
            m_shading_context,
            sampling_context,
            samples,
            initial_flux);
        PathTracerType path_tracer(
            path_visitor,
            m_params.m_rr_min_path_length,
            m_params.m_max_path_length,
            m_params.m_max_iterations,
            material_data.m_edf->get_light_near_start());   // don't illuminate points closer than the light near start value

        // Handle the light vertex separately.
        Spectrum light_particle_flux = edf_value;       // todo: only works for diffuse EDF? What we need is the light exitance
        light_particle_flux /= static_cast<float>(light_sample.m_probability);
        path_visitor.visit_area_light_vertex(
            light_sample,
            light_particle_flux,
            light_ray.m_time);

        // Trace the light path.
        const size_t path_length =
            path_tracer.trace(
                sampling_context,
                m_shading_context,
                light_ray,
                &parent_shading_point);

        // Update path statistics.
        ++m_path_count;
        m_path_length.insert(path_length);

        // Return the number of samples generated when tracing this light path.
        return path_visitor.get_sample_count();
    }
size_t SubsurfaceSampler::sample(
    SamplingContext&        sampling_context,
    const ShadingPoint&     outgoing_point,
    const BSSRDF&           bssrdf,
    const void*             bssrdf_data,
    SubsurfaceSample        samples[],
    const size_t            max_sample_count)
{
    assert(max_sample_count > 0);

    // Sample the diffusion profile.
    BSSRDFSample bssrdf_sample(sampling_context);
    if (!bssrdf.sample(bssrdf_data, bssrdf_sample))
        return 0;

    // Reject points too far away.
    // This introduces negligible bias in comparison to the other approximations.
    const Vector2d& point(bssrdf_sample.get_point());
    const double radius2 = square_norm(point);
    const double rmax2 = bssrdf_sample.get_rmax2();
    if (radius2 > rmax2)
        return 0;

    // Evaluate the PDF of the diffusion profile.
    const double radius = sqrt(radius2);
    const double bssrdf_sample_pdf =
        bssrdf.evaluate_pdf(bssrdf_data, bssrdf_sample.get_channel(), radius);

    // Pick a sampling basis.
    sampling_context.split_in_place(1, 1);
    Axis sampling_axis;
    Basis3d sampling_basis;
    double sampling_basis_pdf;
    pick_sampling_basis(
        outgoing_point.get_shading_basis(),
        sampling_context.next_double2(),
        sampling_axis,
        sampling_basis,
        sampling_basis_pdf);

    // Compute height of sample point on (positive) hemisphere of radius Rmax.
    assert(rmax2 >= radius2);
    const double h = sqrt(rmax2 - radius2);

    // Compute sphere entry and exit points.
    Vector3d entry_point, exit_point;
    entry_point = exit_point = outgoing_point.get_point();
    entry_point += sampling_basis.transform_to_parent(Vector3d(point[0], +h, point[1]));
    exit_point += sampling_basis.transform_to_parent(Vector3d(point[0], -h, point[1]));
    assert(feq(norm(exit_point - entry_point), 2.0 * h, 1.0e-9));

    // Build a probe ray inscribed inside the sphere of radius Rmax.
    ShadingRay probe_ray(
        entry_point,
        -sampling_basis.get_normal(),
        0.0,
        2.0 * h,
        outgoing_point.get_time(),
        VisibilityFlags::ProbeRay,
        outgoing_point.get_ray().m_depth + 1);

    const Material* material = outgoing_point.get_material();
    ShadingPoint shading_points[2];
    size_t shading_point_index = 0;
    ShadingPoint* parent_shading_point = 0;

    size_t sample_count = 0;

    // Trace the ray and return all intersections (or up to max_sample_count of them) found inside the sphere.
    while (true)
    {
        // Continue tracing the ray.
        shading_points[shading_point_index].clear();
        if (!m_shading_context.get_intersector().trace(
                probe_ray,
                shading_points[shading_point_index],
                parent_shading_point))
            break;

        // Only consider points lying on surfaces with the same material as the outgoing point.
        if (shading_points[shading_point_index].get_material() == material)
        {
            // Execute the OSL shader if we have one. Needed for bump mapping.
#ifdef APPLESEED_WITH_OSL
            if (material->has_osl_surface())
            {
                sampling_context.split_in_place(1, 1);
                m_shading_context.execute_osl_bump(
                    *material->get_osl_surface(),
                    shading_points[shading_point_index],
                    sampling_context.next_double2());
            }
#endif
            SubsurfaceSample& sample = samples[sample_count++];
            sample.m_point = shading_points[shading_point_index];

            // Compute sample probability.
            sample.m_probability =
                  bssrdf_sample_pdf
                * sampling_basis_pdf
                * abs(dot(sampling_basis.get_normal(), sample.m_point.get_geometric_normal()));     // todo: or shading normal?

            // Weight sample probability with multiple importance sampling.
            sample.m_probability /=
                compute_mis_weight(
                    bssrdf,
                    bssrdf_data,
                    bssrdf_sample.get_channel(),
                    sampling_basis,
                    sampling_axis,
                    sample.m_probability,
                    outgoing_point.get_point(),
                    sample.m_point.get_point(),
                    sample.m_point.get_geometric_normal());     // todo: or shading normal?

            // Return the relative index of refraction.
            sample.m_eta = bssrdf_sample.get_eta();

            if (sample_count == max_sample_count)
                break;
        }

        // Move the ray's origin past the hit surface.
        probe_ray.m_org = shading_points[shading_point_index].get_point();
        probe_ray.m_tmax = norm(exit_point - probe_ray.m_org);

        // Swap the current and parent shading points.
        parent_shading_point = &shading_points[shading_point_index];
        shading_point_index = 1 - shading_point_index;
    }

    return sample_count;
}