def test03_point_sample_direction_vec(variant_packet_spectral, spectrum_key):
    from mitsuba.render import SurfaceInteraction3f

    emitter_pos = [10, -1, 2]
    emitter, spectrum = create_emitter_and_spectrum(emitter_pos, spectrum_key)

    # Direction sampling
    it = SurfaceInteraction3f.zero(3)
    it.p = [[0.0, 0.0, 0.0], [-2.0, 0.0, -2.0], [4.5, 4.5,
                                                 0.0]]  # Some positions
    it.time = [0.3, 0.3, 0.3]

    # Direction from the position to the point emitter
    d = -it.p + emitter_pos
    dist = ek.norm(d)
    d /= dist

    # Sample direction on the emitter
    sample = [0.1, 0.5]
    ds, res = emitter.sample_direction(it, sample)

    assert ek.all(ds.time == it.time)
    assert ek.all(ds.pdf == 1.0)
    assert ek.all(ds.delta)
    assert ek.allclose(ds.d, d / ek.norm(d))

    # Evalutate the spectrum
    spec = spectrum.eval(it) / (dist**2)
    assert ek.allclose(res, spec)
Example #2
0
def test_sample_ray(variant_scalar_rgb, direction):
    import enoki as ek
    from mitsuba.core import Vector2f, Vector3f

    emitter = make_emitter(direction=direction)
    direction = Vector3f(direction)

    time = 1.0
    wavelength_sample = 0.3
    directional_sample = [0.3, 0.2]

    for spatial_sample in [
        [0.85, 0.13],
        [0.16, 0.50],
        [0.00, 1.00],
        [0.32, 0.87],
        [0.16, 0.44],
        [0.17, 0.44],
        [0.22, 0.81],
        [0.12, 0.82],
        [0.99, 0.42],
        [0.72, 0.40],
        [0.01, 0.61],
    ]:
        ray, _ = emitter.sample_ray(
            time, wavelength_sample, spatial_sample, directional_sample)

        # Check that ray direction is what is expected
        assert ek.allclose(ray.d, direction / ek.norm(direction))

        # Check that ray origin is outside of bounding sphere
        # Bounding sphere is centered at world origin and has radius 1 without scene
        assert ek.norm(ray.o) >= 1.
Example #3
0
def test02_ray_intersect_transform(variant_scalar_rgb, shape):
    from mitsuba.core import Ray3f, ScalarVector3f
    from mitsuba.render import HitComputeFlags

    trans = ScalarVector3f([0, 1, 0])
    angle = 15

    for scale in [0.5, 2.7]:
        s, s_inst = example_scene(shape, scale, trans, angle)

        # grid size
        n = 21
        inv_n = 1.0 / n

        for x in range(n):
            for y in range(n):
                x_coord = scale * (2 * (x * inv_n) - 1)
                y_coord = scale * (2 * (y * inv_n) - 1)

                ray = Ray3f(o=ScalarVector3f([x_coord, y_coord, -12]) + trans,
                            d=[0.0, 0.0, 1.0],
                            time=0.0,
                            wavelengths=[])

                si_found = s.ray_test(ray)
                si_found_inst = s_inst.ray_test(ray)

                assert si_found == si_found_inst

                for dn_flags in [
                        HitComputeFlags.dNGdUV, HitComputeFlags.dNSdUV
                ]:
                    if si_found:
                        si = s.ray_intersect(ray,
                                             HitComputeFlags.All | dn_flags)
                        si_inst = s_inst.ray_intersect(
                            ray, HitComputeFlags.All | dn_flags)

                        assert si.prim_index == si_inst.prim_index
                        assert si.instance is None
                        assert si_inst.instance is not None
                        assert ek.allclose(si.t, si_inst.t, atol=2e-2)
                        assert ek.allclose(si.time, si_inst.time, atol=2e-2)
                        assert ek.allclose(si.p, si_inst.p, atol=2e-2)
                        assert ek.allclose(si.dp_du, si_inst.dp_du, atol=2e-2)
                        assert ek.allclose(si.dp_dv, si_inst.dp_dv, atol=2e-2)
                        assert ek.allclose(si.uv, si_inst.uv, atol=2e-2)
                        assert ek.allclose(si.wi, si_inst.wi, atol=2e-2)

                        if ek.norm(si.dn_du) > 0.0 and ek.norm(si.dn_dv) > 0.0:
                            assert ek.allclose(si.dn_du,
                                               si_inst.dn_du,
                                               atol=2e-2)
                            assert ek.allclose(si.dn_dv,
                                               si_inst.dn_dv,
                                               atol=2e-2)
def test02_point_sample_direction(variant_scalar_spectral, spectrum_key):
    # Check the correctness of the sample_direction() method

    from mitsuba.render import SurfaceInteraction3f

    emitter_pos = [10, -1, 2]
    emitter, spectrum = create_emitter_and_spectrum(emitter_pos, spectrum_key)

    # Direction sampling
    it = SurfaceInteraction3f.zero()
    it.p = [0.0, -2.0, 4.5]  # Some position
    it.time = 0.3

    # Direction from the position to the point emitter
    d = -it.p + emitter_pos
    dist = ek.norm(d)
    d /= dist

    # Sample a direction on the emitter
    sample = [0.1, 0.5]
    ds, res = emitter.sample_direction(it, sample)

    assert ds.time == it.time
    assert ds.pdf == 1.0
    assert ds.delta
    assert ek.allclose(ds.d, d)

    # Evalutate the spectrum
    spec = spectrum.eval(it) / (dist**2)
    assert ek.allclose(res, spec)
def test_sample_direction(variant_scalar_spectral, spectrum_key, direction):
    # Check correctness of sample_direction() and pdf_direction() methods

    from mitsuba.render import SurfaceInteraction3f
    from mitsuba.core import Vector3f

    direction = Vector3f(direction)
    emitter = make_emitter(direction, spectrum_key)
    spectrum = make_spectrum(spectrum_key)

    it = SurfaceInteraction3f.zero()
    # Some position inside the unit sphere (i.e. within the emitter's default bounding sphere)
    it.p = [-0.5, 0.3, -0.1]
    it.time = 1.0

    # Sample direction
    samples = [0.85, 0.13]
    ds, res = emitter.sample_direction(it, samples)

    # Direction should point *towards* the illuminated direction
    assert ek.allclose(ds.d, -direction / ek.norm(direction))
    assert ek.allclose(ds.pdf, 1.)
    assert ek.allclose(emitter.pdf_direction(it, ds), 0.)
    assert ek.allclose(ds.time, it.time)

    # Check spectrum (no attenuation vs distance)
    spec = spectrum.eval(it)
    assert ek.allclose(res, spec)
Example #6
0
def test04_normal_weighting_scheme(variant_scalar_rgb):
    from mitsuba.core import Vector3f
    from mitsuba.render import Mesh
    import numpy as np
    """Tests the weighting scheme that is used to compute surface normals."""
    m = Mesh("MyMesh", 5, 2, has_vertex_normals=True)

    vertices = m.vertex_positions_buffer()
    normals = m.vertex_normals_buffer()

    a, b = 1.0, 0.5
    vertices[:] = [0, 0, 0, -a, 1, 0, a, 1, 0, -b, 0, 1, b, 0, 1]

    n0 = Vector3f(0.0, 0.0, -1.0)
    n1 = Vector3f(0.0, 1.0, 0.0)
    angle_0 = ek.pi / 2.0
    angle_1 = ek.acos(3.0 / 5.0)
    n2 = n0 * angle_0 + n1 * angle_1
    n2 /= ek.norm(n2)
    n = np.vstack([n2, n0, n0, n1, n1]).transpose()

    m.faces_buffer()[:] = [0, 1, 2, 0, 3, 4]

    m.recompute_vertex_normals()
    for i in range(5):
        assert ek.allclose(normals[i * 3:(i + 1) * 3], n[:, i], 5e-4)
def test04_direction_sample_construction_single(variant_scalar_rgb):
    from mitsuba.render import Interaction3f, DirectionSample3f, SurfaceInteraction3f

    # Default constructor
    record = DirectionSample3f()
    record.p = [1, 2, 3]
    record.n = [4, 5, 6]
    record.uv = [7, 8]
    record.d = [0, 42, -1]
    record.pdf = 0.002
    record.dist = 0.13

    assert ek.allclose(record.d, [0, 42, -1])
    assert str(record) == """DirectionSample3f[
  p = [1, 2, 3],
  n = [4, 5, 6],
  uv = [7, 8],
  time = 0,
  pdf = 0.002,
  delta = 0,
  object = nullptr,
  d = [0, 42, -1],
  dist = 0.13
]"""

    # Construct from two interactions: ds.d should start from the reference its.
    its = SurfaceInteraction3f.zero()
    its.p = [20, 3, 40.02]
    its.t = 1
    ref = Interaction3f.zero()
    ref.p = [1.6, -2, 35]
    record = DirectionSample3f(its, ref)
    d = (its.p - ref.p) / ek.norm(its.p - ref.p)
    assert ek.allclose(record.d, d)
Example #8
0
def test01_ray_intersect(variant_scalar_rgb, shape):
    from mitsuba.core import Ray3f
    from mitsuba.render import HitComputeFlags

    s, s_inst = example_scene(shape)

    # grid size
    n = 21
    inv_n = 1.0 / n

    for x in range(n):
        for y in range(n):
            x_coord = (2 * (x * inv_n) - 1)
            y_coord = (2 * (y * inv_n) - 1)
            ray = Ray3f(o=[x_coord, y_coord + 1, -8],
                        d=[0.0, 0.0, 1.0],
                        time=0.0,
                        wavelengths=[])

            si_found = s.ray_test(ray)
            si_found_inst = s_inst.ray_test(ray)

            assert si_found == si_found_inst

            if si_found:
                si = s.ray_intersect(
                    ray, HitComputeFlags.All | HitComputeFlags.dNSdUV)
                si_inst = s_inst.ray_intersect(
                    ray, HitComputeFlags.All | HitComputeFlags.dNSdUV)

                assert si.prim_index == si_inst.prim_index
                assert si.instance is None
                assert si_inst.instance is not None
                assert ek.allclose(si.t, si_inst.t, atol=2e-2)
                assert ek.allclose(si.time, si_inst.time, atol=2e-2)
                assert ek.allclose(si.p, si_inst.p, atol=2e-2)
                assert ek.allclose(si.sh_frame.n,
                                   si_inst.sh_frame.n,
                                   atol=2e-2)
                assert ek.allclose(si.dp_du, si_inst.dp_du, atol=2e-2)
                assert ek.allclose(si.dp_dv, si_inst.dp_dv, atol=2e-2)
                assert ek.allclose(si.uv, si_inst.uv, atol=2e-2)
                assert ek.allclose(si.wi, si_inst.wi, atol=2e-2)

                if ek.norm(si.dn_du) > 0.0 and ek.norm(si.dn_dv) > 0.0:
                    assert ek.allclose(si.dn_du, si_inst.dn_du, atol=2e-2)
                    assert ek.allclose(si.dn_dv, si_inst.dn_dv, atol=2e-2)
def test_sample_ray(variant_scalar_spectral, direction, spectrum_key):
    from mitsuba.core import Vector2f, Vector3f, sample_shifted
    from mitsuba.render import SurfaceInteraction3f

    emitter = make_emitter(direction=direction, spectrum_key=spectrum_key)
    spectrum = make_spectrum(spectrum_key)
    direction = Vector3f(direction)

    time = 1.0

    for wavelength_sample, spatial_sample, directional_sample in zip(
        cycle([0.3, 0.7]),
        [
            [0.85, 0.13],
            [0.16, 0.50],
            [0.00, 1.00],
            [0.32, 0.87],
            [0.16, 0.44],
            [0.17, 0.44],
            [0.22, 0.81],
            [0.12, 0.82],
            [0.99, 0.42],
            [0.72, 0.40],
            [0.01, 0.61],
        ],
        cycle([[0.3, 0.2]])
    ):
        ray, _ = emitter.sample_ray(
            time, wavelength_sample, spatial_sample, directional_sample)

        # Check that ray direction is what is expected
        assert ek.allclose(ray.d, direction / ek.norm(direction))

        # Check that ray origin is outside of bounding sphere
        # Bounding sphere is centered at world origin and has radius 1 without scene
        assert ek.norm(ray.o) >= 1.

        # Check that passed irradiance spectrum is used for wavelength sampling
        it = SurfaceInteraction3f.zero()
        wav, spec = spectrum.sample_spectrum(
            it, sample_shifted(wavelength_sample))
        assert ek.allclose(ray.wavelengths, wav)
Example #10
0
def test_sample_ray(variant_scalar_rgb, spatial_sample, direction):
    import enoki as ek
    from mitsuba.core import Vector2f, Vector3f

    emitter = make_emitter(direction=direction)
    direction = Vector3f(direction)
    
    time = 1.0
    wavelength_sample = 0.3
    directional_sample = [0.3, 0.2]

    ray, wavelength = emitter.sample_ray(
        time, wavelength_sample, spatial_sample, directional_sample)

    # Check that ray direction is what is expected
    assert ek.allclose(ray.d, direction / ek.norm(direction))

    # Check that ray origin is outside of bounding sphere
    # Bounding sphere is centered at world origin and has radius 1 without scene
    assert ek.norm(ray.o) >= 1.
Example #11
0
def abs_(a0):
    if not a0.IsArithmetic:
        raise Exception("abs(): requires arithmetic operands!")
    if not a0.IsSpecial or a0.IsMatrix:
        ar, sr = _check1(a0)
        for i in range(sr):
            ar[i] = _ek.abs(a0[i])
        return ar
    elif a0.IsSpecial:
        return _ek.norm(a0)
    else:
        raise Exception('abs(): unsupported array type!')
Example #12
0
def test_sample_direction(variant_scalar_spectral, spectrum_key, it_pos,
                          wavelength_sample, cutoff_angle, lookat):
    # Check the correctness of the sample_direction() method

    import math
    from mitsuba.core import sample_shifted, Transform4f
    from mitsuba.render import SurfaceInteraction3f

    cutoff_angle_rad = math.radians(cutoff_angle)
    beam_width_rad = cutoff_angle_rad * 0.75
    inv_transition_width = 1 / (cutoff_angle_rad - beam_width_rad)
    emitter, spectrum = create_emitter_and_spectrum(lookat, cutoff_angle,
                                                    spectrum_key)
    eval_t = 0.3
    trafo = Transform4f(emitter.world_transform().eval(eval_t))

    # Create a surface iteration
    it = SurfaceInteraction3f.zero()
    it.p = it_pos
    it.time = eval_t

    # Sample a wavelength from spectrum
    wav, spec = spectrum.sample(it, sample_shifted(wavelength_sample))
    it.wavelengths = wav

    # Direction from the position to the point emitter
    d = -it.p + lookat["pos"]
    dist = ek.norm(d)
    d /= dist

    # Calculate angle between lookat direction and ray direction
    angle = ek.acos((trafo.inverse().transform_vector(-d))[2])
    angle = ek.select(
        ek.abs(angle - beam_width_rad) < 1e-3, beam_width_rad, angle)
    angle = ek.select(
        ek.abs(angle - cutoff_angle_rad) < 1e-3, cutoff_angle_rad, angle)

    # Sample a direction from the emitter
    ds, res = emitter.sample_direction(it, [0, 0])

    # Evalutate the spectrum
    spec = spectrum.eval(it)
    spec = ek.select(
        angle <= beam_width_rad, spec,
        spec * ((cutoff_angle_rad - angle) * inv_transition_width))
    spec = ek.select(angle <= cutoff_angle_rad, spec, 0)

    assert ds.time == it.time
    assert ds.pdf == 1.0
    assert ds.delta
    assert ek.allclose(ds.d, d)
    assert ek.allclose(res, spec / (dist**2))
def test02_intersection_partials(variant_scalar_rgb):
    from mitsuba.core import Frame3f, Ray3f, RayDifferential3f
    from mitsuba.render import SurfaceInteraction3f

    # Test the texture partial computation with some random data

    o = [0.44650541, 0.16336525, 0.74225088]
    d = [0.2956123, 0.67325977, 0.67774232]
    time = 0.5
    w = []
    r = RayDifferential3f(o, d, time, w)
    r.o_x = r.o + [0.1, 0, 0]
    r.o_y = r.o + [0, 0.1, 0]
    r.d_x = r.d
    r.d_y = r.d
    r.has_differentials = True

    si = SurfaceInteraction3f()
    si.p = r(10)
    si.dp_du = [0.5514372, 0.84608955, 0.41559092]
    si.dp_dv = [0.14551054, 0.54917541, 0.39286475]
    si.n = ek.cross(si.dp_du, si.dp_dv)
    si.n /= ek.norm(si.n)
    si.t = 0

    si.compute_partials(r)

    # Positions reached via computed partials
    px1 = si.dp_du * si.duv_dx[0] + si.dp_dv * si.duv_dx[1]
    py1 = si.dp_du * si.duv_dy[0] + si.dp_dv * si.duv_dy[1]

    # Manually
    px2 = r.o_x + r.d_x * \
        ((ek.dot(si.n, si.p) - ek.dot(si.n, r.o_x)) / ek.dot(si.n, r.d_x))
    py2 = r.o_y + r.d_y * \
        ((ek.dot(si.n, si.p) - ek.dot(si.n, r.o_y)) / ek.dot(si.n, r.d_y))
    px2 -= si.p
    py2 -= si.p

    assert (ek.allclose(px1, px2))
    assert (ek.allclose(py1, py2))

    si.dp_du = [0, 0, 0]
    si.compute_partials(r)

    assert (ek.allclose(px1, px2))
    assert (ek.allclose(py1, py2))

    si.compute_partials(r)

    assert (ek.allclose(si.duv_dx, [0, 0]))
    assert (ek.allclose(si.duv_dy, [0, 0]))
Example #14
0
def log_(a0):
    if not a0.IsFloat:
        raise Exception("log(): requires floating point operands!")
    ar, sr = _check1(a0)
    if not a0.IsSpecial:
        for i in range(sr):
            ar[i] = _ek.log(a0[i])
    elif a0.IsComplex:
        ar.real = .5 * _ek.log(_ek.squared_norm(a0))
        ar.imag = _ek.arg(a0)
    elif a0.IsQuaternion:
        qi_n = _ek.normalize(a0.imag)
        rq = _ek.norm(a0)
        acos_rq = _ek.acos(a0.real / rq)
        log_rq = _ek.log(rq)
        ar.imag = qi_n * acos_rq
        ar.real = log_rq
    else:
        raise Exception("log(): unsupported array type!")
    return ar
Example #15
0
def compute_groundtruth(make_scene, integrator, spp, passes, epsilon):
    """Render groundtruth radiance and gradient image using finite difference"""
    from mitsuba.python.autodiff import render

    def render_offset(offset):
        scene = make_scene(integrator, spp, offset)
        fsize = scene.sensors()[0].film().size()

        values = render(scene)
        for i in range(passes-1):
            values += render(scene)
        values /= passes

        return values.numpy().reshape(fsize[1], fsize[0], -1)

    gradient = (render_offset(epsilon) - render_offset(-epsilon)) / (2.0 * ek.norm(epsilon))

    image = render_offset(0.0)

    return image, gradient[:, :, [0]]
Example #16
0
    def sample_direction(self, ref, sample, active):
        trafo = self.m_world_transform.eval(ref.time, active)

        ds = DirectionSample3f()
        ds.p = trafo.matrix[3][:3]
        ds.n = 0
        ds.uv = 0
        ds.time = ref.time
        ds.pdf = 1
        ds.delta = True
        ds.d = ds.p - ref.p
        ds.dist = ek.norm(ds.d)
        inv_dist = ek.rcp(ds.dist)
        ds.d *= inv_dist

        si = SurfaceInteraction3f()
        si.wavelengths = ref.wavelengths

        spec = self.m_intensity.eval(si, active) * (inv_dist * inv_dist)
        return (ds, spec)
def test04_normal_weighting_scheme(variant_scalar_rgb):
    from mitsuba.core import Struct, float_dtype, Vector3f
    from mitsuba.render import Mesh
    import numpy as np
    """Tests the weighting scheme that is used to compute surface normals."""
    vertex_struct = Struct() \
        .append("x", Struct.Type.Float32) \
        .append("y", Struct.Type.Float32) \
        .append("z", Struct.Type.Float32) \
        .append("nx", Struct.Type.Float32) \
        .append("ny", Struct.Type.Float32) \
        .append("nz", Struct.Type.Float32)

    index_struct = Struct() \
        .append("i0", Struct.Type.UInt32) \
        .append("i1", Struct.Type.UInt32) \
        .append("i2", Struct.Type.UInt32)

    m = Mesh("MyMesh", vertex_struct, 5, index_struct, 2)
    v = m.vertices()

    a, b = 1.0, 0.5
    v['x'] = Float([0, -a, a, -b, b])
    v['y'] = Float([0, 1, 1, 0, 0])
    v['z'] = Float([0, 0, 0, 1, 1])

    n0 = Vector3f(0.0, 0.0, -1.0)
    n1 = Vector3f(0.0, 1.0, 0.0)
    angle_0 = ek.pi / 2.0
    angle_1 = ek.acos(3.0 / 5.0)
    n2 = n0 * angle_0 + n1 * angle_1
    n2 /= ek.norm(n2)
    n = np.vstack([n2, n0, n0, n1, n1])

    f = m.faces()
    f[0] = (0, 1, 2)
    f[1] = (0, 3, 4)
    m.recompute_vertex_normals()
    assert ek.allclose(v['nx'], n[:, 0], 5e-4)
    assert ek.allclose(v['ny'], n[:, 1], 5e-4)
    assert ek.allclose(v['nz'], n[:, 2], 5e-4)
Example #18
0
def exp_(a0):
    if not a0.IsFloat:
        raise Exception("exp(): requires floating point operands!")
    ar, sr = _check1(a0)
    if not a0.IsSpecial:
        for i in range(sr):
            ar[i] = _ek.exp(a0[i])
    elif a0.IsComplex:
        s, c = _ek.sincos(a0.imag)
        exp_r = _ek.exp(a0.real)
        ar.real = exp_r * c
        ar.imag = exp_r * s
    elif a0.IsQuaternion:
        qi = a0.imag
        ri = _ek.norm(qi)
        exp_w = _ek.exp(a0.real)
        s, c = _ek.sincos(ri)
        ar.imag = qi * (s * exp_w / ri)
        ar.real = c * exp_w
    else:
        raise Exception("exp(): unsupported array type!")
    return ar
Example #19
0
def test_sampling(variant_scalar_rgb, center, radius):
    """
    We construct an irradiance meter attached to a sphere and assert that sampled
    rays originate at the sphere's surface
    """
    from mitsuba.core import Vector3f

    center_v = Vector3f(center)
    sphere = example_shape(radius, center_v)
    sensor = sphere.sensor()
    num_samples = 100

    wav_samples = np.random.rand(num_samples)
    pos_samples = np.random.rand(num_samples, 2)
    dir_samples = np.random.rand(num_samples, 2)

    for i in range(100):
        ray = sensor.sample_ray_differential(0.0, wav_samples[i],
                                             pos_samples[i], dir_samples[i])[0]

        # assert that the ray starts at the sphere surface
        assert ek.allclose(ek.norm(center_v - ray.o), radius)
        # assert that all rays point away from the sphere center
        assert ek.dot(ek.normalize(ray.o - center_v), ray.d) > 0.0
Example #20
0
def sqrt_(a0):
    if not a0.IsFloat:
        raise Exception("sqrt(): requires floating point operands!")
    ar, sr = _check1(a0)
    if not a0.IsSpecial:
        for i in range(sr):
            ar[i] = _ek.sqrt(a0[i])
    elif a0.IsComplex:
        n = abs(a0)
        m = a0.real >= 0
        zero = _ek.eq(n, 0)
        t1 = _ek.sqrt(.5 * (n + abs(a0.real)))
        t2 = .5 * a0.imag / t1
        im = _ek.select(m, t2, _ek.copysign(t1, a0.imag))
        ar.real = _ek.select(m, t1, abs(t2))
        ar.imag = _ek.select(zero, 0, im)
    elif a0.IsQuaternion:
        ri = _ek.norm(a0.imag)
        cs = _ek.sqrt(a0.Complex(a0.real, ri))
        ar.imag = a0.imag * (_ek.rcp(ri) * cs.imag)
        ar.real = cs.real
    else:
        raise Exception("sqrt(): unsupported array type!")
    return ar
Example #21
0
 def timestep(self, pos, vel, dt=0.02, mu=.1, g=9.81):
     acc = -mu * vel * ek.norm(vel) - m.Array2f(0, g)
     pos_out = pos + dt * vel
     vel_out = vel + dt * acc
     return pos_out, vel_out
Example #22
0
 def eval(self, value):
     self.value = value
     self.inv_norm = ek.rcp(ek.norm(value))
     return value * self.inv_norm
pos += sampler.next_2d()

# Sample rays starting from the camera sensor
rays, weights = sensor.sample_ray_differential(time=0,
                                               sample1=sampler.next_1d(),
                                               sample2=pos * scale,
                                               sample3=0)

# Intersect rays with the scene geometry
surface_interaction = scene.ray_intersect(rays)

dragon_c = scene.shapes()[6].bbox().center()
c = Vector3f(dragon_c)
d = c - surface_interaction.p
result = ek.norm(d)
max_dist = ek.hmax(result)
result /= max_dist
result = 0.3 * (1.0 - result**(2.0))

result[~surface_interaction.is_valid()] = 0

block = ImageBlock(film.crop_size(),
                   channel_count=5,
                   filter=film.reconstruction_filter(),
                   border=False)
block.clear()
# ImageBlock expects RGB values (Array of size (n, 3))
block.put(pos, rays.wavelengths, Vector3f(result, result, result), 1)

# Write out the result from the ImageBlock