Example #1
0
double compute_fast_ambient_occlusion(
    const SamplingContext&          sampling_context,
    const AOVoxelTreeIntersector&   intersector,
    const Vector3d&                 point,
    const Vector3d&                 geometric_normal,
    const Basis3d&                  shading_basis,
    const double                    max_distance,
    const size_t                    sample_count,
    double&                         min_distance)
{
    // Create a sampling context.
    SamplingContext child_sampling_context = sampling_context.split(2, sample_count);

    // Construct the ambient occlusion ray.
    ShadingRay::RayType ray;
    ray.m_org = point;
    ray.m_tmin = 0.0;
    ray.m_tmax = max_distance;

    size_t computed_samples = 0;
    size_t occluded_samples = 0;

    min_distance = max_distance;

    for (size_t i = 0; i < sample_count; ++i)
    {
        // Generate a cosine-weighted direction over the unit hemisphere.
        ray.m_dir = sample_hemisphere_cosine(child_sampling_context.next_vector2<2>());

        // Transform the direction to world space.
        ray.m_dir = shading_basis.transform_to_parent(ray.m_dir);

        // Don't cast rays on or below the geometric surface.
        if (dot(ray.m_dir, geometric_normal) <= 0.0)
            continue;

        // Count the number of computed samples.
        ++computed_samples;

        // Trace the ambient occlusion ray and count the number of occluded samples.
        double distance;
        if (intersector.trace(ray, true, distance))
        {
            ++occluded_samples;
            min_distance = min(min_distance, distance);
        }
    }

    // Compute occlusion as a scalar between 0.0 and 1.0.
    double occlusion = static_cast<double>(occluded_samples);
    if (computed_samples > 1)
        occlusion /= computed_samples;
    assert(occlusion >= 0.0);
    assert(occlusion <= 1.0);

    return occlusion;
}
    void trace_non_physical_light_photon(
        SamplingContext&        sampling_context,
        const LightSample&      light_sample)
    {
        // Sample the light.
        InputEvaluator input_evaluator(m_texture_cache);
        SamplingContext child_sampling_context = sampling_context.split(2, 1);
        Vector3d emission_position, emission_direction;
        Spectrum light_value;
        double light_prob;
        light_sample.m_light->sample(
            input_evaluator,
            child_sampling_context.next_vector2<2>(),
            emission_position,
            emission_direction,
            light_value,
            light_prob);

        // Transform the emission position and direction from assembly space to world space.
        emission_position = light_sample.m_light_transform.point_to_parent(emission_position);
        emission_direction = normalize(light_sample.m_light_transform.vector_to_parent(emission_direction));

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

        // Build the photon ray.
        child_sampling_context.split_in_place(1, 1);
        const ShadingRay ray(
            emission_position,
            emission_direction,
            child_sampling_context.next_double2(),
            ~0);

        // Build the path tracer.
        const bool cast_indirect_light = (light_sample.m_light->get_flags() & EDF::CastIndirectLight) != 0;
        PathVisitor path_visitor(
            initial_flux,
            m_params.m_dl_mode == SPPMParameters::SPPM, // store direct lighting photons?
            cast_indirect_light,
            m_params.m_enable_caustics,
            m_local_photons);
        PathTracer<PathVisitor, true> path_tracer(      // true = adjoint
            path_visitor,
            m_params.m_photon_tracing_rr_min_path_length,
            m_params.m_photon_tracing_max_path_length,
            m_params.m_max_iterations);

        // Trace the photon path.
        path_tracer.trace(
            child_sampling_context,
            m_intersector,
            m_texture_cache,
            ray);
    }
        void trace_emitting_triangle_photon(
            SamplingContext&        sampling_context,
            LightSample&            light_sample)
        {
            // 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 EDF* edf = light_sample.m_triangle->m_edf;

            // Evaluate the EDF inputs.
            InputEvaluator input_evaluator(m_texture_cache);
            edf->evaluate_inputs(input_evaluator, light_sample.m_bary);

            // Sample the EDF.
            SamplingContext child_sampling_context = sampling_context.split(2, 1);
            Vector3d emission_direction;
            Spectrum edf_value;
            double edf_prob;
            edf->sample(
                input_evaluator.data(),
                light_sample.m_geometric_normal,
                Basis3d(light_sample.m_shading_normal),
                child_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 * m_params.m_light_photon_count));

            // 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 photon ray.
            child_sampling_context.split_in_place(1, 1);
            const ShadingRay ray(
                light_sample.m_point,
                emission_direction,
                child_sampling_context.next_double2(),
                ShadingRay::LightRay);

            // Build the path tracer.
            const bool cast_indirect_light = (edf->get_flags() & EDF::CastIndirectLight) != 0;
            PathVisitor path_visitor(
                initial_flux,
                m_params.m_dl_mode == SPPMParameters::SPPM, // store direct lighting photons?
                cast_indirect_light,
                m_params.m_enable_caustics,
                m_local_photons);
            PathTracer<PathVisitor, true> path_tracer(      // true = adjoint
                path_visitor,
                m_params.m_photon_tracing_rr_min_path_length,
                m_params.m_photon_tracing_max_path_length,
                m_params.m_max_iterations,
                edf->get_light_near_start());               // don't illuminate points closer than the light near start value

            // Trace the photon path.
            path_tracer.trace(
                child_sampling_context,
                m_shading_context,
                ray,
                &parent_shading_point);
        }
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);
    }
}