Ejemplo n.º 1
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))
Ejemplo n.º 2
0
def test_sample_ray(variant_packet_spectral, spectrum_key, wavelength_sample,
                    pos_sample, cutoff_angle, lookat):
    # Check the correctness of the sample_ray() method

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

    cutoff_angle_rad = math.radians(cutoff_angle)
    cos_cutoff_angle_rad = math.cos(cutoff_angle_rad)
    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))

    # Sample a local direction and calculate local angle
    dir_sample = pos_sample  # not being used anyway
    local_dir = warp.square_to_uniform_cone(pos_sample, cos_cutoff_angle_rad)
    angle = ek.acos(local_dir[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 ray (position, direction, wavelengths) from the emitter
    ray, res = emitter.sample_ray(eval_t, wavelength_sample, pos_sample,
                                  dir_sample)

    # Sample wavelengths on the spectrum
    it = SurfaceInteraction3f.zero()
    wav, spec = spectrum.sample(it, sample_shifted(wavelength_sample))
    it.wavelengths = wav
    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 ek.allclose(
        res, spec / warp.square_to_uniform_cone_pdf(
            trafo.inverse().transform_vector(ray.d), cos_cutoff_angle_rad))
    assert ek.allclose(ray.time, eval_t)
    assert ek.all(local_dir.z >= cos_cutoff_angle_rad)
    assert ek.allclose(ray.wavelengths, wav)
    assert ek.allclose(ray.d, trafo.transform_vector(local_dir))
    assert ek.allclose(ray.o, lookat["pos"])
Ejemplo n.º 3
0
def test05_scalar(t):
    if not ek.is_array_v(t) or ek.array_size_v(t) == 0:
        return
    get_class(t.__module__)

    if ek.is_mask_v(t):
        assert ek.all_nested(t(True))
        assert ek.any_nested(t(True))
        assert ek.none_nested(t(False))
        assert ek.all_nested(t(False) ^ t(True))
        assert ek.all_nested(ek.eq(t(False), t(False)))
        assert ek.none_nested(ek.eq(t(True), t(False)))

    if ek.is_arithmetic_v(t):
        assert t(1) + t(1) == t(2)
        assert t(3) - t(1) == t(2)
        assert t(2) * t(2) == t(4)
        assert ek.min(t(2), t(3)) == t(2)
        assert ek.max(t(2), t(3)) == t(3)

        if ek.is_signed_v(t):
            assert t(2) * t(-2) == t(-4)
            assert ek.abs(t(-2)) == t(2)

        if ek.is_integral_v(t):
            assert t(6) // t(2) == t(3)
            assert t(7) % t(2) == t(1)
            assert t(7) >> 1 == t(3)
            assert t(7) << 1 == t(14)
            assert t(1) | t(2) == t(3)
            assert t(1) ^ t(3) == t(2)
            assert t(1) & t(3) == t(1)
        else:
            assert t(6) / t(2) == t(3)
            assert ek.sqrt(t(4)) == t(2)
            assert ek.fmadd(t(1), t(2), t(3)) == t(5)
            assert ek.fmsub(t(1), t(2), t(3)) == t(-1)
            assert ek.fnmadd(t(1), t(2), t(3)) == t(1)
            assert ek.fnmsub(t(1), t(2), t(3)) == t(-5)
            assert (t(1) & True) == t(1)
            assert (t(1) & False) == t(0)
            assert (t(1) | False) == t(1)

        assert ek.all_nested(t(3) > t(2))
        assert ek.all_nested(ek.eq(t(2), t(2)))
        assert ek.all_nested(ek.neq(t(3), t(2)))
        assert ek.all_nested(t(1) >= t(1))
        assert ek.all_nested(t(2) < t(3))
        assert ek.all_nested(t(1) <= t(1))
        assert ek.select(ek.eq(t(2), t(2)), t(4), t(5)) == t(4)
        assert ek.select(ek.eq(t(3), t(2)), t(4), t(5)) == t(5)
        t2 = t(2)
        assert ek.hsum(t2) == t.Value(2 * len(t2))
        assert ek.dot(t2, t2) == t.Value(4 * len(t2))
        assert ek.dot_async(t2, t2) == t(4 * len(t2))

        value = t(1)
        value[ek.eq(value, t(1))] = t(2)
        value[ek.eq(value, t(3))] = t(5)
        assert value == t(2)
Ejemplo n.º 4
0
def test15_abs(m):
    x = m.Float(-2, 2)
    ek.enable_grad(x)
    y = ek.abs(x)
    ek.backward(y)
    assert ek.allclose(ek.detach(y), [2, 2])
    assert ek.allclose(ek.grad(x), [-1, 1])
Ejemplo n.º 5
0
def test02_eval_grad(variant_scalar_rgb):
    # Tests evaluating the texture gradient under different rotation
    from mitsuba.render import SurfaceInteraction3f
    from mitsuba.core.xml import load_string
    from mitsuba.core import Vector2f
    import numpy as np
    import enoki as ek
    delta = 1e-4
    si = SurfaceInteraction3f()
    for u01 in np.random.rand(10, 1):
        angle = 360.0*u01[0]
        bitmap = load_string("""
        <texture type="bitmap" version="2.0.0">
            <string name="filename" value="resources/data/common/textures/noise_8x8.png"/>
            <transform name="to_uv">
                <rotate angle="%f"/>
            </transform>
        </texture>""" % angle).expand()[0]
        for uv in np.random.rand(10, 2):
            si.uv = Vector2f(uv)
            f = bitmap.eval_1(si)
            si.uv = uv + Vector2f(delta, 0)
            fu = bitmap.eval_1(si)
            si.uv = uv + Vector2f(0, delta)
            fv = bitmap.eval_1(si)
            gradient_finite_difference = Vector2f((fu - f)/delta, (fv - f)/delta)
            gradient_analytic = bitmap.eval_1_grad(si)
            assert ek.allclose(0, ek.abs(gradient_finite_difference/gradient_analytic - 1.0), atol = 1e04)
Ejemplo n.º 6
0
def quat_to_euler(q):
    name = _ek.detail.array_name('Array', q.Type, [3], q.IsScalar)
    module = _modules.get(q.__module__)
    Array3f = getattr(module, name)

    sinp = 2 * _ek.fmsub(q.w, q.y, q.z * q.x)
    gimbal_lock = _ek.abs(sinp) > (1.0 - 5e-8)

    # roll (x-axis rotation)
    q_y_2 = _ek.sqr(q.y)
    sinr_cosp = 2 * _ek.fmadd(q.w, q.x, q.y * q.z)
    cosr_cosp = _ek.fnmadd(2, _ek.fmadd(q.x, q.x, q_y_2), 1)
    roll = _ek.select(gimbal_lock, 2 * _ek.atan2(q.x, q.w),
                      _ek.atan2(sinr_cosp, cosr_cosp))

    # pitch (y-axis rotation)
    pitch = _ek.select(gimbal_lock, _ek.copysign(0.5 * _ek.Pi, sinp),
                       _ek.asin(sinp))

    # yaw (z-axis rotation)
    siny_cosp = 2 * _ek.fmadd(q.w, q.z, q.x * q.y)
    cosy_cosp = _ek.fnmadd(2, _ek.fmadd(q.z, q.z, q_y_2), 1)
    yaw = _ek.select(gimbal_lock, 0, _ek.atan2(siny_cosp, cosy_cosp))

    return Array3f(roll, pitch, yaw)
Ejemplo n.º 7
0
def test02_radical_inverse_vectorized(variant_scalar_rgb):
    from mitsuba.core import RadicalInverse

    v = RadicalInverse()
    for index, prime in enumerate(gen_primes()):
        if index >= 1024:
            break
        result = v.eval(index, ek.arange(10, dtype=ek.uint64))
        for i in range(len(result)):
            assert ek.abs(r_inv(prime, i) - result[i]) < 1e-7
Ejemplo n.º 8
0
def check_contents(stream):
    if type(stream) is not ZStream:
        stream.seek(0)
    for v in contents:
        if type(v) is str:
            assert v == stream.read_string()
        elif type(v) is int:
            assert v == stream.read_int64()
        elif type(v) is float:
            assert ek.abs(stream.read_single() - v) / v < 1e-5
        elif type(v) is bool:
            assert v == stream.read_bool()
Ejemplo n.º 9
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!')
def test02_type_is_preserved(variant_scalar_rgb):
    from mitsuba.core import Properties as Prop

    p = Prop()
    fill_properties(p)
    assert p['prop_1'] == 1
    assert p['prop_2'] == '1'
    assert p['prop_3'] == False
    assert ek.abs(p['prop_4']-3.14) < 1e-6

    # Updating an existing property but using a different type
    p['prop_2'] = 2
    assert p['prop_2'] == 2
Ejemplo n.º 11
0
def test02_radical_inverse_vectorized(variant_scalar_rgb):
    from mitsuba.core import RadicalInverse

    try:
        from mitsuba.packet_rgb.core.qmc import RadicalInverseP
    except ImportError:
        pytest.skip("packet_rgb mode not enabled")

    v = RadicalInverse()
    v_p = RadicalInverseP()
    for index in range(1024):
        result = v_p.eval_scrambled(index, ek.arange(10, dtype=ek.uint64))
        for i in range(len(result)):
            assert ek.abs(v.eval_scrambled(index, i) - result[i]) < 1e-7
Ejemplo n.º 12
0
def test01_radical_inverse(variant_scalar_rgb):
    from mitsuba.core import RadicalInverse

    v = RadicalInverse()
    assert (v.eval(0, 0) == 0)
    assert (v.eval(0, 1) == 0.5)
    assert (v.eval(0, 2) == 0.25)
    assert (v.eval(0, 3) == 0.75)

    for index, prime in enumerate(gen_primes()):
        if index >= 1024:
            break
        for i in range(10):
            assert ek.abs(r_inv(prime, i) - v.eval(index, i)) < 1e-7
Ejemplo n.º 13
0
def test08_misc():
    for i in range(-5, 5):
        for j in range(-5, 5):
            a = ek.sqrt(C(i, j))
            b = cmath.sqrt(complex(i, j))
            assert ek.allclose(a, C(b))

            assert ek.allclose(ek.conj(a), C(b.conjugate()))
            assert ek.allclose(ek.abs(a), C(abs(b)))

            if i != 0 and j != 0:
                a = ek.rsqrt(C(i, j))
                b = C(1 / cmath.sqrt(complex(i, j)))
                assert ek.allclose(a, b)
Ejemplo n.º 14
0
def test04_operators():
    I3 = ek.scalar.Array3i
    F3 = ek.scalar.Array3f

    assert (I3(1, 2, 3) + I3(0, 1, 0) == I3(1, 3, 3))
    assert (I3(1, 2, 3) - I3(0, 1, 0) == I3(1, 1, 3))
    assert (I3(1, 2, 3) * I3(0, 1, 0) == I3(0, 2, 0))
    assert (I3(1, 2, 3) // I3(2, 2, 2) == I3(0, 1, 1))
    assert (I3(1, 2, 3) % I3(2, 2, 2) == I3(1, 0, 1))
    assert (I3(1, 2, 3) << 1 == I3(2, 4, 6))
    assert (I3(1, 2, 3) >> 1 == I3(0, 1, 1))
    assert (I3(1, 2, 3) & 1 == I3(1, 0, 1))
    assert (I3(1, 2, 3) | 1 == I3(1, 3, 3))
    assert (I3(1, 2, 3) ^ 1 == I3(0, 3, 2))
    assert (-I3(1, 2, 3) == I3(-1, -2, -3))
    assert (~I3(1, 2, 3) == I3(-2, -3, -4))
    assert (ek.abs(I3(1, -2, 3)) == I3(1, 2, 3))
    assert (abs(I3(1, -2, 3)) == I3(1, 2, 3))
    assert (ek.abs(I3(1, -2, 3)) == I3(1, 2, 3))
    assert (ek.fmadd(F3(1, 2, 3), F3(2, 3, 1), F3(1, 1, 1)) == F3(3, 7, 4))
    assert (ek.fmsub(F3(1, 2, 3), F3(2, 3, 1), F3(1, 1, 1)) == F3(1, 5, 2))
    assert (ek.fnmadd(F3(1, 2, 3), F3(2, 3, 1), F3(1, 1, 1)) == F3(-1, -5, -2))
    assert (ek.fnmsub(F3(1, 2, 3), F3(2, 3, 1), F3(1, 1, 1)) == F3(-3, -7, -4))
Ejemplo n.º 15
0
def test03_ray_intersect_transform(variant_scalar_rgb):
    if mitsuba.core.MTS_ENABLE_EMBREE:
        pytest.skip("EMBREE enabled")

    from mitsuba.core import Ray3f

    for r in [1, 3]:
        s = example_scene(radius=r,
                          extra="""<transform name="to_world">
                                       <rotate y="1.0" angle="30"/>
                                       <translate x="0.0" y="1.0" z="0.0"/>
                                   </transform>""")
        # grid size
        n = 21
        inv_n = 1.0 / n

        for x in range(n):
            for y in range(n):
                x_coord = r * (2 * (x * inv_n) - 1)
                y_coord = r * (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)

                assert si_found == (x_coord ** 2 + y_coord ** 2 <= r * r) \
                    or ek.abs(x_coord ** 2 + y_coord ** 2 - r * r) < 1e-8

                if si_found:
                    ray = Ray3f(o=[x_coord, y_coord + 1, -8],
                                d=[0.0, 0.0, 1.0],
                                time=0.0,
                                wavelengths=[])
                    si = s.ray_intersect(ray)
                    ray_u = Ray3f(ray)
                    ray_v = Ray3f(ray)
                    eps = 1e-4
                    ray_u.o += si.dp_du * eps
                    ray_v.o += si.dp_dv * eps
                    si_u = s.ray_intersect(ray_u)
                    si_v = s.ray_intersect(ray_v)
                    if si_u.is_valid():
                        du = (si_u.uv - si.uv) / eps
                        assert ek.allclose(du, [1, 0], atol=2e-2)
                    if si_v.is_valid():
                        dv = (si_v.uv - si.uv) / eps
                        assert ek.allclose(dv, [0, 1], atol=2e-2)
Ejemplo n.º 16
0
def test03_distance(variant_scalar_rgb):
    from mitsuba.core import BoundingBox3f as BBox

    assert BBox([1, 2, 3], [2, 3, 5]).distance(BBox([4, 2, 3], [5, 3, 5])) == 2

    assert ek.abs(
        BBox([1, 2, 3], [2, 3, 5]).distance(BBox([3, 4, 6], [7, 7, 7])) -
        ek.sqrt(3)) < 1e-6

    assert BBox([1, 2, 3],
                [2, 3, 5]).distance(BBox([1.1, 2.2, 3.3],
                                         [1.8, 2.8, 3.8])) == 0

    assert BBox([1, 2, 3], [2, 3, 5]).distance([1.5, 2.5, 3.5]) == 0

    assert BBox([1, 2, 3], [2, 3, 5]).distance([3, 2.5, 3.5]) == 1
def test03_ray_intersect_transform(variant_scalar_rgb):
    from mitsuba.core import xml, Ray3f, Transform4f

    for r in [1, 3]:
        s = xml.load_dict({
            "type":
            "sphere",
            "radius":
            r,
            "to_world":
            Transform4f.translate([0, 1, 0]) *
            Transform4f.rotate([0, 1, 0], 30.0)
        })

        # grid size
        n = 21
        inv_n = 1.0 / n

        for x in range(n):
            for y in range(n):
                x_coord = r * (2 * (x * inv_n) - 1)
                y_coord = r * (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)

                assert si_found == (x_coord ** 2 + y_coord ** 2 <= r * r) \
                    or ek.abs(x_coord ** 2 + y_coord ** 2 - r * r) < 1e-8

                if si_found:
                    si = s.ray_intersect(ray)
                    ray_u = Ray3f(ray)
                    ray_v = Ray3f(ray)
                    eps = 1e-4
                    ray_u.o += si.dp_du * eps
                    ray_v.o += si.dp_dv * eps
                    si_u = s.ray_intersect(ray_u)
                    si_v = s.ray_intersect(ray_v)
                    if si_u.is_valid():
                        du = (si_u.uv - si.uv) / eps
                        assert ek.allclose(du, [1, 0], atol=2e-2)
                    if si_v.is_valid():
                        dv = (si_v.uv - si.uv) / eps
                        assert ek.allclose(dv, [0, 1], atol=2e-2)
Ejemplo n.º 18
0
    def sincos(x):
        Float = type(x)
        Int = ek.int_array_t(Float)

        xa = ek.abs(x)

        j = Int(xa * 1.2732395447351626862)

        j = (j + Int(1)) & ~Int(1)

        y = Float(j)

        Shift = Float.Type.Size * 8 - 3

        sign_sin = ek.reinterpret_array(Float, j << Shift) ^ x
        sign_cos = ek.reinterpret_array(Float, (~(j - Int(2)) << Shift))

        y = xa - y * 0.78515625 \
               - y * 2.4187564849853515625e-4 \
               - y * 3.77489497744594108e-8

        z = y * y
        z |= ek.eq(xa, ek.Infinity)

        s = poly2(z, -1.6666654611e-1,
                  8.3321608736e-3,
                  -1.9515295891e-4) * z

        c = poly2(z, 4.166664568298827e-2,
                  -1.388731625493765e-3,
                  2.443315711809948e-5) * z

        s = ek.fmadd(s, y, y)
        c = ek.fmadd(c, z, ek.fmadd(z, -0.5, 1))

        polymask = ek.eq(j & Int(2), ek.zero(Int))

        return (
            ek.mulsign(ek.select(polymask, s, c), sign_sin),
            ek.mulsign(ek.select(polymask, c, s), sign_cos)
        )
Ejemplo n.º 19
0
def quat_to_euler(q):
    q_y_2 = _ek.sqr(q.y)

    sinr_cosp = 2 * _ek.fmadd(q.w, q.x, q.y * q.z)
    cosr_cosp = _ek.fnmadd(2, _ek.fmadd(q.x, q.x, q_y_2), 1)
    roll = _ek.atan2(sinr_cosp, cosr_cosp)

    # pitch (y-axis rotation)
    sinp = 2 * _ek.fmsub(q.w, q.y, q.z * q.x)
    if (_ek.abs(sinp) >= 1.0):
        pitch = _ek.copysign(0.5 * _ek.Pi, sinp)
    else:
        pitch = _ek.asin(sinp)

    # yaw (z-axis rotation)
    siny_cosp = 2 * _ek.fmadd(q.w, q.z, q.x * q.y)
    cosy_cosp = _ek.fnmadd(2, _ek.fmadd(q.z, q.z, q_y_2), 1)
    yaw = _ek.atan2(siny_cosp, cosy_cosp)

    name = _ek.detail.array_name('Array', q.Type, [3], q.IsScalar)
    module = _modules.get(q.__module__)
    Array3f = getattr(module, name)

    return Array3f(roll, pitch, yaw)
Ejemplo n.º 20
0
def rlgamma(a, x):
    'Regularized lower incomplete gamma function based on CEPHES'

    eps = 1e-15
    big = 4.503599627370496e15
    biginv = 2.22044604925031308085e-16

    if a < 0 or x < 0:
        raise "out of range"

    if x == 0:
        return 0

    ax = (a * ek.log(x)) - x - ek.lgamma(a)

    if ax < -709.78271289338399:
        return 1.0 if a < x else 0.0

    if x <= 1 or x <= a:
        r2 = a
        c2 = 1
        ans2 = 1

        while True:
            r2 = r2 + 1
            c2 = c2 * x / r2
            ans2 += c2

            if c2 / ans2 <= eps:
                break

        return ek.exp(ax) * ans2 / a

    c = 0
    y = 1 - a
    z = x + y + 1
    p3 = 1
    q3 = x
    p2 = x + 1
    q2 = z * x
    ans = p2 / q2

    while True:
        c += 1
        y += 1
        z += 2
        yc = y * c
        p = (p2 * z) - (p3 * yc)
        q = (q2 * z) - (q3 * yc)

        if q != 0:
            nextans = p / q
            error = ek.abs((ans - nextans) / nextans)
            ans = nextans
        else:
            error = 1

        p3 = p2
        p2 = p
        q3 = q2
        q2 = q

        # normalize fraction when the numerator becomes large
        if ek.abs(p) > big:
            p3 *= biginv
            p2 *= biginv
            q3 *= biginv
            q2 *= biginv

        if error <= eps:
            break;

    return 1 - ek.exp(ax) * ans
Ejemplo n.º 21
0
def test03_develop(variant_scalar_rgb, file_format, tmpdir):
    from mitsuba.core.xml import load_string
    from mitsuba.core import Bitmap, Struct, ReconstructionFilter, float_dtype
    from mitsuba.render import ImageBlock
    import numpy as np
    """Create a test image. Develop it to a few file format, each time reading
    it back and checking that contents are unchanged."""
    np.random.seed(12345 + ord(file_format[0]))
    # Note: depending on the file format, the alpha channel may be automatically removed.
    film = load_string("""<film version="2.0.0" type="hdrfilm">
            <integer name="width" value="41"/>
            <integer name="height" value="37"/>
            <string name="file_format" value="{}"/>
            <string name="pixel_format" value="rgba"/>
            <string name="component_format" value="float32"/>
            <rfilter type="box"/>
        </film>""".format(file_format))
    # Regardless of the output file format, values are stored as XYZAW (5 channels).
    contents = np.random.uniform(size=(film.size()[1], film.size()[0], 5))
    # RGBE and will only reconstruct well images that have similar scales on
    # all channel (because exponent is shared between channels).
    if file_format is "rgbe":
        contents = 1 + 0.1 * contents
    # Use unit weights.
    contents[:, :, 4] = 1.0

    block = ImageBlock(film.size(), 5, film.reconstruction_filter())

    block.clear()
    for x in range(film.size()[1]):
        for y in range(film.size()[0]):
            block.put([y + 0.5, x + 0.5], contents[x, y, :])

    film.prepare(['X', 'Y', 'Z', 'A', 'W'])
    film.put(block)

    with pytest.raises(RuntimeError):
        # Should raise when the destination file hasn't been specified.
        film.develop()

    filename = str(tmpdir.join('test_image.' + file_format))
    film.set_destination_file(filename)
    film.develop()

    # Read back and check contents
    other = Bitmap(filename).convert(Bitmap.PixelFormat.XYZAW,
                                     Struct.Type.Float32,
                                     srgb_gamma=False)
    img = np.array(other, copy=False)

    if False:
        import matplotlib.pyplot as plt
        plt.figure()
        plt.subplot(1, 3, 1)
        plt.imshow(contents[:, :, :3])
        plt.subplot(1, 3, 2)
        plt.imshow(img[:, :, :3])
        plt.subplot(1, 3, 3)
        plt.imshow(ek.sum(ek.abs(img[:, :, :3] - contents[:, :, :3]), axis=2),
                   cmap='coolwarm')
        plt.colorbar()
        plt.show()

    if file_format == "exr":
        assert ek.allclose(img, contents, atol=1e-5)
    else:
        if file_format == "rgbe":
            assert ek.allclose(img[:, :, :3], contents[:, :, :3], atol=1e-2), \
                   '\n{}\nvs\n{}\n'.format(img[:4, :4, :3], contents[:4, :4, :3])
        else:
            assert ek.allclose(img[:, :, :3], contents[:, :, :3], atol=1e-5)
        # Alpha channel was ignored, alpha and weights should default to 1.0.
        assert ek.allclose(img[:, :, 3:5], 1.0, atol=1e-6)
def test02_sample_pol_local(variant_scalar_mono_polarized):
    from mitsuba.core import Frame3f, Transform4f, Spectrum, UnpolarizedSpectrum, Vector3f
    from mitsuba.core.xml import load_string
    from mitsuba.render import BSDFContext, TransportMode, SurfaceInteraction3f, fresnel_conductor
    from mitsuba.render.mueller import stokes_basis, rotate_mueller_basis

    def spectrum_from_stokes(v):
        res = Spectrum(0.0)
        for i in range(4):
            res[i, 0] = v[i]
        return res

    # Create a Silver conductor BSDF and reflect different polarization states
    # at a 45˚ angle.
    # All tests take place directly in local BSDF coordinates. Here we only
    # want to make sure that the output of this looks like what you would
    # expect from a Mueller matrix describing specular reflection on a mirror.

    eta = 0.136125 + 4.010625j  # IoR values of Ag at 635.816284nm
    bsdf = load_string("""<bsdf version='2.0.0' type='conductor'>
                              <spectrum name="eta" value="{}"/>
                              <spectrum name="k" value="{}"/>
                          </bsdf>""".format(eta.real, eta.imag))

    theta_i = 45 * ek.pi / 180
    wi = Vector3f([-ek.sin(theta_i), 0, ek.cos(theta_i)])

    ctx = BSDFContext()
    ctx.mode = TransportMode.Importance
    si = SurfaceInteraction3f()
    si.p = [0, 0, 0]
    si.wi = wi
    n = [0, 0, 1]
    si.sh_frame = Frame3f(n)

    bs, M_local = bsdf.sample(ctx, si, 0.0, [0.0, 0.0])
    wo = bs.wo

    # Rotate into standard coordinates for specular reflection
    bi_s = ek.normalize(ek.cross(n, -wi))
    bi_p = ek.normalize(ek.cross(-wi, bi_s))
    bo_s = ek.normalize(ek.cross(n, wo))
    bo_p = ek.normalize(ek.cross(wo, bi_s))

    M_local = rotate_mueller_basis(M_local, -wi, stokes_basis(-wi), bi_p, wo,
                                   stokes_basis(wo), bo_p)

    # Apply to unpolarized light and verify that it is equivalent to normal Fresnel
    a0 = M_local @ spectrum_from_stokes([1, 0, 0, 0])
    F = fresnel_conductor(ek.cos(theta_i),
                          ek.scalar.Complex2f(eta.real, eta.imag))
    a0 = a0[0, 0]
    assert ek.allclose(a0[0], F, atol=1e-3)

    # Apply to horizontally polarized light (linear at 0˚)
    # Test that it is..
    # 1) still fully polarized (though with reduced intensity)
    # 2) still horizontally polarized
    a1 = M_local @ spectrum_from_stokes([1, +1, 0, 0])
    assert ek.allclose(a1[0, 0], ek.abs(a1[1, 0]), atol=1e-3)  # 1)
    a1 /= a1[0, 0]
    assert ek.allclose(a1, spectrum_from_stokes([1, 1, 0, 0]), atol=1e-3)  # 2)

    # Apply to vertically polarized light (linear at 90˚)
    # Test that it is..
    # 1) still fully polarized (though with reduced intensity)
    # 2) still vertically polarized
    a2 = M_local @ spectrum_from_stokes([1, -1, 0, 0])
    assert ek.allclose(a2[0, 0], ek.abs(a2[1, 0]), atol=1e-3)  # 1)
    a2 /= a2[0, 0]
    assert ek.allclose(a2, spectrum_from_stokes([1, -1, 0, 0]),
                       atol=1e-3)  # 2)

    # Apply to (positive) diagonally polarized light (linear at +45˚)
    # Test that..
    # 1) The polarization is flipped to -45˚
    # 2) There is now also some (left) circular polarization
    a3 = M_local @ spectrum_from_stokes([1, 0, +1, 0])
    assert ek.all(a3[2, 0] < UnpolarizedSpectrum(0.0))
    assert ek.all(a3[3, 0] < UnpolarizedSpectrum(0.0))

    # Apply to (negative) diagonally polarized light (linear at -45˚)
    # Test that..
    # 1) The polarization is flipped to +45˚
    # 2) There is now also some (right) circular polarization
    a4 = M_local @ spectrum_from_stokes([1, 0, -1, 0])
    assert ek.all(a4[2, 0] > UnpolarizedSpectrum(0.0))
    assert ek.all(a4[3, 0] > UnpolarizedSpectrum(0.0))

    # Apply to right circularly polarized light
    # Test that the polarization is flipped to left circular
    a5 = M_local @ spectrum_from_stokes([1, 0, 0, +1])
    assert ek.all(a5[3, 0] < UnpolarizedSpectrum(0.0))

    # Apply to left circularly polarized light
    # Test that the polarization is flipped to right circular
    a6 = M_local @ spectrum_from_stokes([1, 0, 0, -1])
    assert ek.all(a6[3, 0] > UnpolarizedSpectrum(0.0))
def test02_sample_pol_world(variant_scalar_mono_polarized):
    from mitsuba.core import Frame3f, Spectrum, UnpolarizedSpectrum
    from mitsuba.core.xml import load_string
    from mitsuba.render import BSDFContext, TransportMode, SurfaceInteraction3f, fresnel_conductor
    from mitsuba.render.mueller import stokes_basis, rotate_mueller_basis

    def spectrum_from_stokes(v):
        res = Spectrum(0.0)
        for i in range(4):
            res[i, 0] = v[i]
        return res

    # Create a Silver conductor BSDF and reflect different polarization states
    # at a 45˚ angle.
    # This test takes place in world coordinates and thus involves additional
    # coordinate system rotations.
    #
    # The setup is as follows:
    # - The mirror is positioned at [0, 0, 0], angled s.t. the surface normal
    #   is along [1, 1, 0].
    # - Incident ray w1 = [-1, 0, 0] strikes the mirror at a 45˚ angle and
    #   reflects into direction w2 = [0, 1, 0]
    # - The corresponding Stokes bases are b1 = [0, 1, 0] and b2 = [1, 0, 0].

    # Setup
    eta = 0.136125 + 4.010625j  # IoR values of Ag at 635.816284nm
    bsdf = load_string("""<bsdf version='2.0.0' type='conductor'>
                              <spectrum name="eta" value="{}"/>
                              <spectrum name="k" value="{}"/>
                          </bsdf>""".format(eta.real, eta.imag))
    ctx = BSDFContext()
    ctx.mode = TransportMode.Importance
    si = SurfaceInteraction3f()
    si.p = [0, 0, 0]
    si.n = ek.normalize([1.0, 1.0, 0.0])
    si.sh_frame = Frame3f(si.n)

    # Incident / outgoing directions and their Stokes bases
    w1 = ek.scalar.Vector3f([-1, 0, 0])
    b1 = [0, 1, 0]
    w2 = ek.scalar.Vector3f([0, 1, 0])
    b2 = [1, 0, 0]

    # Perform actual reflection
    si.wi = si.to_local(-w1)
    bs, M_tmp = bsdf.sample(ctx, si, 0.0, [0.0, 0.0])
    M_world = si.to_world_mueller(M_tmp, -si.wi, bs.wo)

    # Test that outgoing direction is as predicted
    assert ek.allclose(si.to_world(bs.wo), w2, atol=1e-5)

    # Align reference frames s.t. polarization is expressed w.r.t. b1 & b2
    M_world = rotate_mueller_basis(M_world, w1, stokes_basis(w1), b1, w2,
                                   stokes_basis(w2), b2)

    # Apply to unpolarized light and verify that it is equivalent to normal Fresnel
    a0 = M_world @ spectrum_from_stokes([1, 0, 0, 0])
    F = fresnel_conductor(si.wi[2], ek.scalar.Complex2f(eta.real, eta.imag))
    a0 = a0[0, 0]
    assert ek.allclose(a0[0], F, atol=1e-3)

    # Apply to horizontally polarized light (linear at 0˚)
    # Test that it is..
    # 1) still fully polarized (though with reduced intensity)
    # 2) still horizontally polarized
    a1 = M_world @ spectrum_from_stokes([1, +1, 0, 0])
    assert ek.allclose(a1[0, 0], ek.abs(a1[1, 0]), atol=1e-3)  # 1)
    a1 /= a1[0, 0]
    assert ek.allclose(a1, spectrum_from_stokes([1, 1, 0, 0]), atol=1e-3)  # 2)

    # Apply to vertically polarized light (linear at 90˚)
    # Test that it is..
    # 1) still fully polarized (though with reduced intensity)
    # 2) still vertically polarized
    a2 = M_world @ spectrum_from_stokes([1, -1, 0, 0])
    assert ek.allclose(a2[0, 0], ek.abs(a2[1, 0]), atol=1e-3)  # 1)
    a2 /= a2[0, 0]
    assert ek.allclose(a2, spectrum_from_stokes([1, -1, 0, 0]),
                       atol=1e-3)  # 2)

    # Apply to (positive) diagonally polarized light (linear at +45˚)
    # Test that..
    # 1) The polarization is flipped to -45˚
    # 2) There is now also some (left) circular polarization
    a3 = M_world @ spectrum_from_stokes([1, 0, +1, 0])
    assert ek.all(a3[2, 0] < UnpolarizedSpectrum(0.0))
    assert ek.all(a3[3, 0] < UnpolarizedSpectrum(0.0))

    # Apply to (negative) diagonally polarized light (linear at -45˚)
    # Test that..
    # 1) The polarization is flipped to +45˚
    # 2) There is now also some (right) circular polarization
    a4 = M_world @ spectrum_from_stokes([1, 0, -1, 0])
    assert ek.all(a4[2, 0] > UnpolarizedSpectrum(0.0))
    assert ek.all(a4[3, 0] > UnpolarizedSpectrum(0.0))

    # Apply to right circularly polarized light
    # Test that the polarization is flipped to left circular
    a5 = M_world @ spectrum_from_stokes([1, 0, 0, +1])
    assert ek.all(a5[3, 0] < UnpolarizedSpectrum(0.0))

    # Apply to left circularly polarized light
    # Test that the polarization is flipped to right circular
    a6 = M_world @ spectrum_from_stokes([1, 0, 0, -1])
    assert ek.all(a6[3, 0] > UnpolarizedSpectrum(0.0))