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
        Vector3d sample_lens(SamplingContext& sampling_context) const
        {
            if (m_diaphragm_map_bound)
            {
                sampling_context.split_in_place(2, 1);
                const Vector2d s = sampling_context.next_vector2<2>();

                Vector2d v;
                size_t y;
                double prob_xy;
                m_importance_sampler->sample(s, v, y, prob_xy);

                const Vector2d lens_point = m_lens_radius * v;
                return Vector3d(lens_point.x, lens_point.y, 0.0);
            }
            else if (m_diaphragm_blade_count == 0)
            {
                sampling_context.split_in_place(2, 1);
                const Vector2d s = sampling_context.next_vector2<2>();
                const Vector2d lens_point = m_lens_radius * sample_disk_uniform(s);
                return Vector3d(lens_point.x, lens_point.y, 0.0);
            }
            else
            {
                sampling_context.split_in_place(3, 1);
                const Vector3d s = sampling_context.next_vector2<3>();
                const Vector2d lens_point =
                    m_lens_radius *
                    sample_regular_polygon_uniform(
                        s,
                        m_diaphragm_vertices.size(),
                        &m_diaphragm_vertices.front());
                return Vector3d(lens_point.x, lens_point.y, 0.0);
            }
        }
Example #3
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);
    }
}
        Vector3d sample_lens(SamplingContext& sampling_context) const
        {
            if (m_diaphragm_map_bound)
            {
                sampling_context.split_in_place(2, 1);
                const Vector2f s = sampling_context.next2<Vector2f>();

                size_t x, y;
                Vector2d payload;
                float prob_xy;
                m_importance_sampler->sample(s, x, y, payload, prob_xy);

                const Vector2d lens_point = m_lens_radius * payload;
                return Vector3d(lens_point.x, lens_point.y, 0.0);
            }
            else if (m_diaphragm_blade_count == 0)
            {
                sampling_context.split_in_place(2, 1);
                const Vector2d s = sampling_context.next2<Vector2d>();
                const Vector2d lens_point = m_lens_radius * sample_disk_uniform(s);
                return Vector3d(lens_point.x, lens_point.y, 0.0);
            }
            else
            {
                sampling_context.split_in_place(3, 1);
                const Vector3d s = sampling_context.next2<Vector3d>();
                const Vector2d lens_point =
                    m_lens_radius *
                    sample_regular_polygon_uniform(
                        s,
                        m_diaphragm_vertices.size(),
                        &m_diaphragm_vertices.front());
                return Vector3d(lens_point.x, lens_point.y, 0.0);
            }
        }
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;
    }
}
Example #6
0
bool DipoleBSSRDF::sample(
    SamplingContext&    sampling_context,
    const void*         data,
    BSSRDFSample&       sample) const
{
    const DipoleBSSRDFInputValues* values =
        reinterpret_cast<const DipoleBSSRDFInputValues*>(data);

    if (values->m_weight == 0.0)
        return false;

    sampling_context.split_in_place(3, 1);
    const Vector3d s = sampling_context.next_vector2<3>();

    // Sample a channel.
    const size_t channel =
        sample_cdf(
            &values->m_channel_cdf[0],
            &values->m_channel_cdf[0] + values->m_channel_cdf.size(),
            s[0]);

    // Sample a radius.
    const double sigma_tr = values->m_sigma_tr[channel];
    const double radius = sample_exponential_distribution(s[1], sigma_tr);

    // Sample an angle.
    const double phi = TwoPi * s[2];

    sample.m_eta = values->m_eta;
    sample.m_channel = channel;
    sample.m_point = Vector2d(radius * cos(phi), radius * sin(phi));
    sample.m_rmax2 = values->m_rmax2;

    return true;
}
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);
    }
}
Example #8
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);
    }
        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,
                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 light ray.
            sampling_context.split_in_place(1, 1);
            const ShadingRay light_ray(
                emission_position,
                emission_direction,
                sampling_context.next_double2(),
                m_ray_dtime,
                ShadingRay::LightRay);

            // Build the path tracer.
            PathVisitor path_visitor(
                m_params,
                m_scene,
                m_frame,
                m_shading_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 EDF* edf = material->get_edf();

            // Evaluate the EDF inputs.
            InputEvaluator input_evaluator(m_texture_cache);

            // TODO: refactor this code (est.).
            ShadingPoint shading_point;
            light_sample.make_shading_point(
                shading_point,
                light_sample.m_shading_normal,
                m_shading_context.get_intersector());
#ifdef WITH_OSL
            if (const ShaderGroup* sg = material->get_osl_surface())
            {
                // TODO: get object area somehow.
                const float surface_area = 0.0f;
                m_shading_context.execute_osl_emission(*sg, shading_point, surface_area);
            }
#endif
            edf->evaluate_inputs(input_evaluator, shading_point);

            // Sample the EDF.
            sampling_context.split_in_place(2, 1);
            Vector3d emission_direction;
            Spectrum edf_value;
            double edf_prob;
            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 light_ray(
                light_sample.m_point,
                emission_direction,
                sampling_context.next_double2(),
                m_ray_dtime,
                ShadingRay::LightRay);

            // Build the path tracer.
            PathVisitor path_visitor(
                m_params,
                m_scene,
                m_frame,
                m_shading_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,
                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();
        }
Example #12
0
void compute_ibl_environment_sampling(
    SamplingContext&        sampling_context,
    const ShadingContext&   shading_context,
    const EnvironmentEDF&   environment_edf,
    const ShadingPoint&     shading_point,
    const Vector3d&         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));

    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(
            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,
                ShadingRay::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,
                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);
}
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;
}
void DiagnosticSurfaceShader::evaluate(
    SamplingContext&        sampling_context,
    const PixelContext&     pixel_context,
    const ShadingContext&   shading_context,
    const ShadingPoint&     shading_point,
    ShadingResult&          shading_result) const
{
    switch (m_shading_mode)
    {
      case Color:
        {
            shading_result.set_main_to_opaque_pink_linear_rgba();

            const Material* material = shading_point.get_material();
            if (material)
            {
                const Material::RenderData& material_data = material->get_render_data();

#ifdef APPLESEED_WITH_OSL
                // Execute the OSL shader if there is one.
                if (material_data.m_shader_group)
                {
                    shading_context.execute_osl_shading(
                        *material_data.m_shader_group,
                        shading_point);
                }
#endif

                if (material_data.m_bsdf)
                {
                    InputEvaluator input_evaluator(shading_context.get_texture_cache());
                    material_data.m_bsdf->evaluate_inputs(
                        shading_context,
                        input_evaluator,
                        shading_point);

                    const Vector3d direction = -normalize(shading_point.get_ray().m_dir);
                    material_data.m_bsdf->evaluate(
                        input_evaluator.data(),
                        false,
                        false,
                        shading_point.get_geometric_normal(),
                        shading_point.get_shading_basis(),
                        direction,
                        direction,
                        ScatteringMode::All,
                        shading_result.m_main.m_color);

                    shading_result.m_color_space = ColorSpaceSpectral;
                }
            }
        }
        break;

      case Coverage:
        shading_result.set_main_to_linear_rgb(Color3f(1.0f));
        break;

      case Barycentric:
        shading_result.set_main_to_linear_rgb(
            vector2_to_color(shading_point.get_bary()));
        break;

      case UV:
        shading_result.set_main_to_linear_rgb(
            uvs_to_color(shading_point.get_uv(0)));
        break;

      case Tangent:
      case Bitangent:
      case ShadingNormal:
        {
#ifdef APPLESEED_WITH_OSL
            const Material* material = shading_point.get_material();
            if (material)
            {
                const Material::RenderData& material_data = material->get_render_data();

                // Execute the OSL shader if there is one.
                if (material_data.m_shader_group)
                {
                    sampling_context.split_in_place(2, 1);
                    shading_context.execute_osl_bump(
                        *material_data.m_shader_group,
                        shading_point,
                        sampling_context.next_vector2<2>());
                }
            }
#endif

            const Vector3d v =
                m_shading_mode == ShadingNormal ? shading_point.get_shading_basis().get_normal() :
                m_shading_mode == Tangent ? shading_point.get_shading_basis().get_tangent_u() :
                shading_point.get_shading_basis().get_tangent_v();

            shading_result.set_main_to_linear_rgb(vector3_to_color(v));
        }
        break;

      case GeometricNormal:
        shading_result.set_main_to_linear_rgb(
            vector3_to_color(shading_point.get_geometric_normal()));
        break;

      case OriginalShadingNormal:
        shading_result.set_main_to_linear_rgb(
            vector3_to_color(shading_point.get_original_shading_normal()));
        break;

      case WorldSpacePosition:
        {
            const Vector3d& p = shading_point.get_point();
            shading_result.set_main_to_linear_rgb(
                Color3f(Color3d(p.x, p.y, p.z)));
        }
        break;

      case Sides:
        shading_result.set_main_to_linear_rgb(
            shading_point.get_side() == ObjectInstance::FrontSide
                ? Color3f(0.0f, 0.0f, 1.0f)
                : Color3f(1.0f, 0.0f, 0.0f));
        break;

      case Depth:
        shading_result.set_main_to_linear_rgb(
            Color3f(static_cast<float>(shading_point.get_distance())));
        break;

      case ScreenSpaceWireframe:
        {
            // Initialize the shading result to the background color.
            shading_result.set_main_to_linear_rgba(Color4f(0.0f, 0.0f, 0.8f, 0.5f));

            if (shading_point.is_triangle_primitive())
            {
                // Film space thickness of the wires.
                const double SquareWireThickness = square(0.00025);

                // Retrieve the time, the scene and the camera.
                const double time = shading_point.get_time().m_absolute;
                const Scene& scene = shading_point.get_scene();
                const Camera& camera = *scene.get_camera();

                // Compute the film space coordinates of the intersection point.
                Vector2d point_ndc;
                camera.project_point(time, shading_point.get_point(), point_ndc);

                // Loop over the triangle edges.
                for (size_t i = 0; i < 3; ++i)
                {
                    // Retrieve the end points of this edge.
                    const size_t j = (i + 1) % 3;
                    const Vector3d vi = shading_point.get_vertex(i);
                    const Vector3d vj = shading_point.get_vertex(j);

                    // Compute the film space coordinates of the edge's end points.
                    Vector2d vi_ndc, vj_ndc;
                    if (!camera.project_segment(time, vi, vj, vi_ndc, vj_ndc))
                        continue;

                    // Compute the film space distance from the intersection point to the edge.
                    const double d = square_distance_point_segment(point_ndc, vi_ndc, vj_ndc);

                    // Shade with the wire's color if the hit point is close enough to the edge.
                    if (d < SquareWireThickness)
                    {
                        shading_result.set_main_to_linear_rgba(Color4f(1.0f));
                        break;
                    }
                }
            }
            else
            {
                assert(shading_point.is_curve_primitive());

                // todo: implement.
            }
        }
        break;

      case WorldSpaceWireframe:
        {
            // Initialize the shading result to the background color.
            shading_result.set_main_to_linear_rgba(Color4f(0.0f, 0.0f, 0.8f, 0.5f));

            if (shading_point.is_triangle_primitive())
            {
                // World space thickness of the wires.
                const double SquareWireThickness = square(0.0015);

                // Retrieve the world space intersection point.
                const Vector3d& point = shading_point.get_point();

                // Loop over the triangle edges.
                for (size_t i = 0; i < 3; ++i)
                {
                    // Retrieve the end points of this edge.
                    const size_t j = (i + 1) % 3;
                    const Vector3d& vi = shading_point.get_vertex(i);
                    const Vector3d& vj = shading_point.get_vertex(j);

                    // Compute the world space distance from the intersection point to the edge.
                    const double d = square_distance_point_segment(point, vi, vj);

                    // Shade with the wire's color if the hit point is close enough to the edge.
                    if (d < SquareWireThickness)
                    {
                        shading_result.set_main_to_linear_rgba(Color4f(1.0f));
                        break;
                    }
                }
            }
            else
            {
                assert(shading_point.is_curve_primitive());

                // todo: implement.
            }
        }
        break;

      case AmbientOcclusion:
        {
            // Compute the occlusion.
            const double occlusion =
                compute_ambient_occlusion(
                    sampling_context,
                    sample_hemisphere_uniform<double>,
                    shading_context.get_intersector(),
                    shading_point,
                    m_ao_max_distance,
                    m_ao_samples);

            // Return a gray scale value proportional to the accessibility.
            const float accessibility = static_cast<float>(1.0 - occlusion);
            shading_result.set_main_to_linear_rgb(Color3f(accessibility));
        }
        break;

      case AssemblyInstances:
        shading_result.set_main_to_linear_rgb(
            integer_to_color(shading_point.get_assembly_instance().get_uid()));
        break;

      case ObjectInstances:
        shading_result.set_main_to_linear_rgb(
            integer_to_color(shading_point.get_object_instance().get_uid()));
        break;

      case Regions:
        {
            const uint32 h =
                mix_uint32(
                    static_cast<uint32>(shading_point.get_object_instance().get_uid()),
                    static_cast<uint32>(shading_point.get_region_index()));
            shading_result.set_main_to_linear_rgb(integer_to_color(h));
        }
        break;

      case Primitives:
        {
            const uint32 h =
                mix_uint32(
                    static_cast<uint32>(shading_point.get_object_instance().get_uid()),
                    static_cast<uint32>(shading_point.get_region_index()),
                    static_cast<uint32>(shading_point.get_primitive_index()));
            shading_result.set_main_to_linear_rgb(integer_to_color(h));
        }
        break;

      case Materials:
        {
            const Material* material = shading_point.get_material();
            if (material)
                shading_result.set_main_to_linear_rgb(integer_to_color(material->get_uid()));
            else shading_result.set_main_to_opaque_pink_linear_rgba();
        }
        break;

      case RaySpread:
        {
            const ShadingRay& ray = shading_point.get_ray();
            if (!ray.m_has_differentials)
                break;

            const Material* material = shading_point.get_material();
            if (material)
            {
                const Material::RenderData& material_data = material->get_render_data();

#ifdef APPLESEED_WITH_OSL
                // Execute the OSL shader if there is one.
                if (material_data.m_shader_group)
                {
                    shading_context.execute_osl_shading(
                        *material_data.m_shader_group,
                        shading_point);
                }
#endif

                if (material_data.m_bsdf)
                {
                    const Dual3d outgoing(
                        -ray.m_dir,
                        ray.m_dir - ray.m_rx.m_dir,
                        ray.m_dir - ray.m_ry.m_dir);

                    InputEvaluator input_evaluator(shading_context.get_texture_cache());
                    material_data.m_bsdf->evaluate_inputs(
                        shading_context,
                        input_evaluator,
                        shading_point);
                    const void* bsdf_data = input_evaluator.data();

                    BSDFSample sample(shading_point, outgoing);
                    material_data.m_bsdf->sample(
                        sampling_context,
                        bsdf_data,
                        false,
                        false,
                        sample);

                    if (!sample.m_incoming.has_derivatives())
                        break;

                    // The 3.0 factor is chosen so that ray spread from Lambertian BRDFs is approximately 1.
                    const double spread =
                        max(
                            norm(sample.m_incoming.get_dx()),
                            norm(sample.m_incoming.get_dy())) * 3.0;

                    shading_result.set_main_to_linear_rgb(
                        Color3f(static_cast<float>(spread)));
                }
            }
        }
        break;

      case FacingRatio:
        {
            const Vector3d& normal = shading_point.get_shading_normal();
            const Vector3d& view   = shading_point.get_ray().m_dir;

            const double facing = abs(dot(normal, view));

            shading_result.set_main_to_linear_rgb(
                Color3f(static_cast<float>(facing)));
        }
        break;

      default:
        assert(false);
        shading_result.set_main_to_transparent_black_linear_rgba();
        break;
    }
}
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);
    }
}
Example #17
0
        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;
            float light_prob;
            light_sample.m_light->sample(
                input_evaluator,
                light_sample.m_light_transform,
                sampling_context.next2<Vector2d>(),
                emission_position,
                emission_direction,
                light_value,
                light_prob);

            // Compute the initial particle weight.
            Spectrum initial_flux = light_value;
            initial_flux /= 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.next2<float>(),
                    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 /= 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();
        }
Example #18
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 #19
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);
}
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;
}
        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(
                input_evaluator,
                sampling_context.next_vector2<2>(),
                outgoing,               // points toward the environment
                env_edf_value,
                env_edf_prob);

            // Compute the center of the tangent disk.
            const Vector3d disk_center = m_safe_scene_radius * outgoing;

            // Uniformly sample the tangent disk.
            sampling_context.split_in_place(2, 1);
            const Vector2d disk_point =
                m_safe_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 =
                disk_center +
                disk_point[0] * basis.get_tangent_u() +
                disk_point[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 light_ray(
                ray_origin,
                -outgoing,
                sampling_context.next_double2(),
                m_ray_dtime,
                ShadingRay::LightRay);

            // Build the path tracer.
            PathVisitor path_visitor(
                m_params,
                m_scene,
                m_frame,
                m_shading_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();
        }
Example #22
0
        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::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);
        }
    }
}
Example #24
0
        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());

            if (material_data.m_shader_group)
            {
                m_shading_context.execute_osl_emission(
                    *material_data.m_shader_group,
                    light_shading_point);
            }

            // 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);
            Vector3f emission_direction;
            Spectrum edf_value;
            float edf_prob;
            material_data.m_edf->sample(
                sampling_context,
                input_evaluator.data(),
                Vector3f(light_sample.m_geometric_normal),
                Basis3f(Vector3f(light_sample.m_shading_normal)),
                sampling_context.next2<Vector2f>(),
                emission_direction,
                edf_value,
                edf_prob);

            // Compute the initial particle weight.
            Spectrum initial_flux = edf_value;
            initial_flux *=
                dot(emission_direction, Vector3f(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,
                Vector3d(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.next2<float>(),
                    m_shutter_open_time,
                    m_shutter_close_time);
            const ShadingRay light_ray(
                light_sample.m_point,
                Vector3d(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 /= 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();
        }
Example #25
0
        FORCE_INLINE virtual Mode sample(
            SamplingContext&    sampling_context,
            const void*         data,
            const bool          adjoint,
            const bool          cosine_mult,
            const Vector3d&     geometric_normal,
            const Basis3d&      shading_basis,
            const Vector3d&     outgoing,
            Vector3d&           incoming,
            Spectrum&           value,
            double&             probability) const
        {
            const InputValues* values = static_cast<const InputValues*>(data);

            // Compute the incoming direction by sampling the MDF.
            sampling_context.split_in_place(2, 1);
            const Vector2d s = sampling_context.next_vector2<2>();
            const Vector3d m = m_mdf->sample(s, values->m_ax, values->m_ay);
            const Vector3d ht = shading_basis.transform_to_parent(m);

            if (!refract(
                    outgoing,
                    ht,
                    values->m_from_ior / values->m_to_ior,
                    incoming))
            {
                // Ignore TIR.
                return Absorption;
            }

            // If incoming and outgoing are on the same hemisphere
            // this is not a refraction.
            const Vector3d& n = shading_basis.get_normal();
            if (dot(incoming, n) * dot(outgoing, n) >= 0.0)
                return Absorption;

            const double G =
                m_mdf->G(
                    shading_basis.transform_to_local(incoming),
                    shading_basis.transform_to_local(outgoing),
                    m,
                    values->m_ax,
                    values->m_ay);

            if (G == 0.0)
                return Absorption;

            const double D = m_mdf->D(m, values->m_ax, values->m_ay);

            const double cos_oh = dot(outgoing, ht);
            const double cos_ih = dot(incoming, ht);
            const double cos_in = dot(incoming, n);
            const double cos_on = dot(outgoing, n);
 
            // [1] equation 21.
            double v = abs((cos_ih * cos_oh) / (cos_in * cos_on));
            v *= square(values->m_to_ior) * D * G;
            const double denom = values->m_to_ior * cos_ih + values->m_from_ior * cos_oh;
            v /= square(denom);
            value.set(static_cast<float>(v));

            const double ht_norm = norm(values->m_from_ior * outgoing + values->m_to_ior * incoming);
            const double dwh_dwo = refraction_jacobian(
                incoming,
                values->m_to_ior,
                ht,
                ht_norm);

            probability = m_mdf->pdf(m, values->m_ax, values->m_ay) * dwh_dwo;
            return Glossy;
        }
Example #26
0
        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);
            Vector3f outgoing;
            Spectrum env_edf_value;
            float env_edf_prob;
            env_edf->sample(
                m_shading_context,
                input_evaluator,
                sampling_context.next2<Vector2f>(),
                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.next2<Vector2d>());

            // Compute the origin of the light ray.
            const Basis3d basis(-Vector3d(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 /= 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.next2<float>(),
                    m_shutter_open_time,
                    m_shutter_close_time);
            const ShadingRay light_ray(
                ray_origin,
                -Vector3d(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();
        }
void DirectLightingIntegrator::compute_outgoing_radiance_light_sampling_low_variance(
    SamplingContext&                sampling_context,
    const MISHeuristic              mis_heuristic,
    const Dual3d&                   outgoing,
    DirectShadingComponents&        radiance,
    LightPathStream*                light_path_stream) const
{
    radiance.set(0.0f);

    // No light source in the scene.
    if (!m_light_sampler.has_lights())
        return;

    // Check if PDF of the sampler is Dirac delta and therefore cannot contribute to the light sampling.
    if (!m_material_sampler.contributes_to_light_sampling())
        return;

    if (m_light_sample_count > 0)
    {
        // Add contributions from all non-physical light sources that aren't part of the lightset.
        for (size_t i = 0, e = m_light_sampler.get_non_physical_light_count(); i < e; ++i)
        {
            // Sample the light.
            LightSample sample;
            m_light_sampler.sample_non_physical_light(m_time, i, sample);

            // Add the contribution of the chosen light.
            add_non_physical_light_sample_contribution(
                sampling_context,
                sample,
                outgoing,
                radiance,
                light_path_stream);
        }
    }

    // Add contributions from the light set.
    if (m_light_sampler.has_lightset())
    {
        DirectShadingComponents lightset_radiance;

        sampling_context.split_in_place(3, m_light_sample_count);

        for (size_t i = 0, e = m_light_sample_count; i < e; ++i)
        {
            // Sample the light set.
            LightSample sample;
            m_light_sampler.sample_lightset(
                m_time,
                sampling_context.next2<Vector3f>(),
                m_material_sampler.get_shading_point(),
                sample);

            // Add the contribution of the chosen light.
            if (sample.m_shape)
            {
                add_emitting_shape_sample_contribution(
                    sampling_context,
                    sample,
                    mis_heuristic,
                    outgoing,
                    lightset_radiance,
                    light_path_stream);
            }
            else
            {
                add_non_physical_light_sample_contribution(
                    sampling_context,
                    sample,
                    outgoing,
                    lightset_radiance,
                    light_path_stream);
            }
        }

        if (m_light_sample_count > 1)
            lightset_radiance /= static_cast<float>(m_light_sample_count);

        radiance += lightset_radiance;
    }
}