Example #1
0
def test01_depth_scalar_stairs(variant_scalar_rgb):
    from mitsuba.core import Ray3f
    from mitsuba.render import SurfaceInteraction3f

    if mitsuba.core.MTS_ENABLE_EMBREE:
        pytest.skip("EMBREE enabled")

    n_steps = 20
    scene = make_synthetic_scene(n_steps)

    n = 128
    inv_n = 1.0 / (n - 1)
    wavelengths = []

    for x in range(n - 1):
        for y in range(n - 1):
            o = [x * inv_n, y * inv_n, 2]
            d = [0, 0, -1]
            r = Ray3f(o, d, 0.5, wavelengths)
            r.mint = 0
            r.maxt = 100

            res_naive = scene.ray_intersect_naive(r)
            res = scene.ray_intersect(r)
            res_shadow = scene.ray_test(r)

            step_idx = ek.floor((y * inv_n) * n_steps)

            assert ek.all(res_shadow)
            assert ek.all(res_shadow == res_naive.is_valid())
            expected = SurfaceInteraction3f()
            expected.t = 2.0 - (step_idx / n_steps)
            compare_results(res_naive, expected, atol=1e-9)
            compare_results(res_naive, res)
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)
def test01_construct(variant_scalar_rgb):
    from mitsuba.core.xml import load_string
    from mitsuba.render import ImageBlock

    im = ImageBlock([33, 12], 4)
    assert im is not None
    assert ek.all(im.offset() == 0)
    im.set_offset([10, 20])
    assert ek.all(im.offset() == [10, 20])

    assert ek.all(im.size() == [33, 12])
    assert im.warn_invalid()
    assert im.border_size() == 0  # Since there's no reconstruction filter
    assert im.channel_count() == 4
    assert im.data() is not None

    rfilter = load_string("""<rfilter version="2.0.0" type="gaussian">
            <float name="stddev" value="15"/>
        </rfilter>""")
    im = ImageBlock([10, 11], 2, filter=rfilter, warn_invalid=False)
    assert im.border_size() == rfilter.border_size()
    assert im.channel_count() == 2
    assert not im.warn_invalid()

    im = ImageBlock([10, 11], 6)
    assert im is not None
    im.channel_count() == 6
Example #4
0
def test03_clone(variant_scalar_rgb, sampler):
    """After cloning, the new sampler should *not* yield the same values: it
    should automatically be seeded differently rather than copying the exact
    state of the original sampler."""
    sampler2 = sampler.clone()
    for i in range(5):
        assert ek.all(sampler.next_1d() != sampler2.next_1d())
        assert ek.all(sampler.next_2d() != sampler2.next_2d())
def test02_inverse(variant_scalar_rgb):
    from mitsuba.core import Transform4f, Matrix4f

    p = [1, 2, 3]

    def check_inverse(tr, expected):
        inv_trafo = tr.inverse()

        assert ek.allclose(inv_trafo.matrix, np.array(expected)), \
               '\n' + str(inv_trafo.matrix) + '\n' + str(expected)

        assert ek.allclose(inv_trafo.transform_point(tr.transform_point(p)),
                           p,
                           rtol=1e-4)

    # --- Scale
    trafo = Transform4f.scale([1.0, 10.0, 500.0])
    assert ek.all(trafo.matrix == np.array([[1, 0, 0, 0], [0, 10, 0, 0],
                                            [0, 0, 500, 0], [0, 0, 0, 1]]))
    assert ek.all(trafo.transform_point(p) == np.array([1, 20, 1500]))
    check_inverse(trafo, [[1, 0, 0, 0], [0, 1 / 10.0, 0, 0],
                          [0, 0, 1 / 500.0, 0], [0, 0, 0, 1]])
    # --- Translation
    trafo = Transform4f.translate([1, 0, 1.5])
    assert ek.all(trafo.matrix == np.array([
        [1, 0, 0, 1],
        [0, 1, 0, 0],
        [0, 0, 1, 1.5],
        [0, 0, 0, 1],
    ]))
    assert ek.all(trafo.transform_point(p) == np.array([2, 2, 4.5]))
    check_inverse(trafo,
                  [[1, 0, 0, -1], [0, 1, 0, 0], [0, 0, 1, -1.5], [0, 0, 0, 1]])
    # --- Perspective                x_fov, near clip, far clip
    trafo = Transform4f.perspective(34.022, 1, 1000)
    # Spot-checked against a known transform from Mitsuba1.
    expected = np.array([[3.26860189, 0, 0, 0], [0, 3.26860189, 0, 0],
                         [0, 0, 1.001001, -1.001001], [0, 0, 1, 0]])
    assert ek.allclose(trafo.matrix, expected)
    check_inverse(trafo, la.inv(expected))

    for i in range(10):
        mtx = np.random.random((4, 4))
        inv_ref = la.inv(mtx)
        trafo = Transform4f(mtx)
        inv_val = trafo.inverse_transpose.T
        assert ek.all(trafo.matrix == mtx)
        assert la.norm(inv_ref - inv_val, 'fro') / la.norm(inv_val,
                                                           'fro') < 5e-4

        p = np.random.random((3, ))
        res = trafo.inverse().transform_point(trafo.transform_point(p))
        assert la.norm(res - p, 2) < 5e-3
def test02_bbox(variant_scalar_rgb):
    from mitsuba.core import xml

    for r in [1, 2, 4]:
        s = xml.load_dict({"type": "sphere", "radius": r})
        b = s.bbox()

        assert b.valid()
        assert ek.allclose(b.center(), [0, 0, 0])
        assert ek.all(b.min == -r)
        assert ek.all(b.max == r)
        assert ek.allclose(b.extents(), [2 * r] * 3)
def test02_small_film(variant_scalar_rgb):
    from mitsuba.render import Spiral

    f = make_film(15, 12)
    s = Spiral(f.size(), f.crop_offset())

    (bo, bs, bi) = s.next_block()
    assert ek.all(bo == [0, 0])
    assert ek.all(bs == [15, 12])
    assert ek.all(bi == 0)
    # The whole image is covered by a single block
    assert ek.all(s.next_block()[1] == 0)
Example #8
0
def test02_bbox(variant_scalar_rgb):
    if mitsuba.core.MTS_ENABLE_EMBREE:
        pytest.skip("EMBREE enabled")

    for r in [1, 2, 4]:
        s = example_sphere(r)
        b = s.bbox()

        assert b.valid()
        assert ek.allclose(b.center(), [0, 0, 0])
        assert ek.all(b.min == -r)
        assert ek.all(b.max == r)
        assert ek.allclose(b.extents(), [2 * r] * 3)
def test02_fresnel_polarized_packet(variant_packet_rgb):
    from mitsuba.render import fresnel

    cos_theta_i = ek.linspace(Float, -1, 1, 20)
    F, cos_theta_t, _, scale = fresnel(cos_theta_i, 1)
    assert ek.all(F == Float.zero(20))
    assert ek.allclose(cos_theta_t, -cos_theta_i, atol=5e-7)
Example #10
0
def test03_depth_packet_stairs(variant_packet_rgb):
    from mitsuba.core import Ray3f as Ray3fX, Properties
    from mitsuba.render import Scene

    if mitsuba.core.MTS_ENABLE_EMBREE:
        pytest.skip("EMBREE enabled")

    props = Properties("scene")
    props["_unnamed_0"] = create_stairs_packet(11)
    scene = Scene(props)

    mitsuba.set_variant("scalar_rgb")
    from mitsuba.core import Ray3f, Vector3f

    n = 4
    inv_n = 1.0 / (n - 1)
    rays = Ray3fX.zero(n * n)
    d = [0, 0, -1]
    wavelengths = []

    for x in range(n):
        for y in range(n):
            o = Vector3f(x * inv_n, y * inv_n, 2)
            o = o * 0.999 + 0.0005
            rays[x * n + y] = Ray3f(o, d, 0, 100, 0.5, wavelengths)

    res_naive = scene.ray_intersect_naive(rays)
    res = scene.ray_intersect(rays)
    res_shadow = scene.ray_test(rays)

    # TODO: spot-check (here, we only check consistency)
    assert ek.all(res_shadow == res.is_valid())
    compare_results(res_naive, res, atol=1e-6)
def test01_position_sample_construction_single(variant_scalar_rgb):
    from mitsuba.render import PositionSample3f, SurfaceInteraction3f

    record = PositionSample3f()
    record.p = [0, 42, 0]
    record.n = [0, 0, 0.4]
    record.uv = [1, 2]
    record.pdf = 0.002
    record.delta = False
    record.time = 0
    record.object = None
    assert str(record) == """PositionSample3f[
  p = [0, 42, 0],
  n = [0, 0, 0.4],
  uv = [1, 2],
  time = 0,
  pdf = 0.002,
  delta = 0,
  object = nullptr
]"""

    # SurfaceInteraction constructor
    si = SurfaceInteraction3f()
    si.time = 50.5
    si.uv = [0.1, 0.8]
    record = PositionSample3f(si)
    assert record.time == si.time \
           and ek.all(record.uv == si.uv) \
           and record.pdf == 0.0
Example #12
0
def test40_safe_functions(m):
    x = ek.linspace(m.Float, 0, 1, 10)
    y = ek.linspace(m.Float, -1, 1, 10)
    z = ek.linspace(m.Float, -1, 1, 10)
    ek.enable_grad(x, y, z)
    x2 = ek.safe_sqrt(x)
    y2 = ek.safe_acos(y)
    z2 = ek.safe_asin(z)
    ek.backward(x2)
    ek.backward(y2)
    ek.backward(z2)
    assert ek.grad(x)[0] == 0
    assert ek.allclose(ek.grad(x)[1], .5 / ek.sqrt(1 / 9))
    assert x[0] == 0
    assert ek.all(ek.isfinite(ek.grad(x)))
    assert ek.all(ek.isfinite(ek.grad(y)))
    assert ek.all(ek.isfinite(ek.grad(z)))
Example #13
0
    def sample_functor(sample, *args):
        n = ek.slices(sample)
        plugin = instantiate(args)
        (si, ctx) = make_context(n)
        bs, weight = plugin.sample(ctx, si, sample[0], [sample[1], sample[2]])

        w = Float.full(1.0, ek.slices(weight))
        w[ek.all(ek.eq(weight, 0))] = 0
        return bs.wo, w
def test03_ray_intersect(variant_scalar_rgb):
    from mitsuba.core import xml, Ray3f, Transform4f

    # Scalar
    scene = xml.load_dict({
        "type": "scene",
        "foo": {
            "type": "rectangle",
            "to_world": Transform4f.scale((2.0, 0.5, 1.0))
        }
    })

    n = 15
    coords = ek.linspace(Float, -1, 1, n)
    rays = [
        Ray3f(o=[a, a, 5], d=[0, 0, -1], time=0.0, wavelengths=[])
        for a in coords
    ]
    si_scalar = []
    valid_count = 0
    for i in range(n):
        its_found = scene.ray_test(rays[i])
        si = scene.ray_intersect(rays[i])

        assert its_found == (abs(coords[i]) <= 0.5)
        assert si.is_valid() == its_found
        si_scalar.append(si)
        valid_count += its_found

    assert valid_count == 7

    try:
        mitsuba.set_variant('packet_rgb')
        from mitsuba.core import xml, Ray3f as Ray3fX
    except ImportError:
        pytest.skip("packet_rgb mode not enabled")

    # Packet
    scene_p = xml.load_dict({
        "type": "scene",
        "foo": {
            "type": "rectangle",
            "to_world": Transform4f.scale((2.0, 0.5, 1.0))
        }
    })

    packet = Ray3fX.zero(n)
    for i in range(n):
        packet[i] = rays[i]

    si_p = scene_p.ray_intersect(packet)
    its_found_p = scene_p.ray_test(packet)

    assert ek.all(si_p.is_valid() == its_found_p)

    for i in range(n):
        assert ek.allclose(si_p.t[i], si_scalar[i].t)
def make_film(width=156, height=232):
    from mitsuba.core.xml import load_string

    f = load_string("""<film type="hdrfilm" version="2.0.0">
            <integer name="width" value="{}"/>
            <integer name="height" value="{}"/>
        </film>""".format(width, height))
    assert f is not None
    assert ek.all(f.size() == [width, height])
    return f
Example #16
0
def test09_eval_parameterization(variant_scalar_rgb, variant_packet_rgb):
    from mitsuba.core.xml import load_string
    shape = load_string('''
    <shape type="obj" version="2.0.0">
        <string name="filename" value="resources/data/common/meshes/rectangle.obj"/>
    </shape>
    ''')
    print(shape)
    si = shape.eval_parameterization([-0.01, 0.5])
    assert not ek.any(si.is_valid())
    si = shape.eval_parameterization([1.0 - 1e-7, 1.0 - 1e-7])
    assert ek.all(si.is_valid())
    assert ek.allclose(si.p, [1, 1, 0])
    si = shape.eval_parameterization([1e-7, 1e-7])
    assert ek.all(si.is_valid())
    assert ek.allclose(si.p, [-1, -1, 0])
    si = shape.eval_parameterization([.2, .3])
    assert ek.all(si.is_valid())
    assert ek.allclose(si.p, [-.6, -.4, 0])
Example #17
0
def test47_nan_propagation(m):
    for i in range(2):
        x = ek.arange(m.Float, 10)
        ek.enable_grad(x)
        f0 = m.Float(0)
        y = ek.select(x < (20 if i == 0 else 0), x, x * (f0 / f0))
        ek.backward(y)
        g = ek.grad(x)
        if i == 0:
            assert g == 1
        else:
            assert ek.all(ek.isnan(g))

    for i in range(2):
        x = ek.arange(m.Float, 10)
        ek.enable_grad(x)
        f0 = m.Float(0)
        y = ek.select(x < (20 if i == 0 else 0), x, x * (f0 / f0))
        ek.forward(x)
        g = ek.grad(y)
        if i == 0:
            assert g == 1
        else:
            assert ek.all(ek.isnan(g))
Example #18
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"])
Example #19
0
def test12_binary_search(cname):
    t = get_class(cname)
    import numpy as np

    data_np = np.float32(np.sort(np.random.normal(size=10000)))
    search_np = np.float32(np.random.normal(size=10000))
    data = t(data_np)
    search = t(search_np)

    index = ek.binary_search(0,
                             len(data) - 1,
                             lambda index: ek.gather(t, data, index) < search)

    value = ek.gather(t, data, index)
    cond = ek.eq(index, len(data) - 1) | (value >= search)
    assert ek.all(cond)
Example #20
0
def test06_discr_bruteforce(variant_packet_rgb):
    # Brute force validation of discrete distribution sampling
    from mitsuba.core import DiscreteDistribution, Float, PCG32, UInt64

    rng = PCG32(initseq=UInt64.arange(50))

    for size in range(2, 20):
        for i in range(2, 50):
            density = Float(rng.next_uint32_bounded(i)[0:size])
            if ek.hsum(density) == 0:
                continue
            ddistr = DiscreteDistribution(density)

            x = ek.linspace(Float, 0, 1, 20)
            y = ddistr.sample(x)
            z = ek.gather(ddistr.cdf(), y - 1, y > 0)
            x *= ddistr.sum()

            # Did we sample the right interval?
            assert ek.all((x > z) | (ek.eq(x, 0) & (x >= z)))
def test02_position_sample_construction_dynamic(variant_packet_rgb):
    from mitsuba.render import PositionSample3f, SurfaceInteraction3f

    n_records = 5

    records = PositionSample3f.zero(n_records)
    # Set properties for all records at once (SoA style)
    # records.n = np.zeros(shape=(n_records, 3))
    # records.pdf = np.zeros(shape=(n_records,))
    # records.uv = np.zeros(shape=(n_records, 2))
    # records.delta = np.zeros(shape=(n_records,), dtype=np.bool)
    records.p = np.array([[1.0, 1.0, 1.0], [0.9, 0.9, 0.9], [0.7, 0.7, 0.7],
                          [1.2, 1.5, 1.1], [1.5, 1.5, 1.5]])
    records.time = [0.0, 0.5, 0.7, 1.0, 1.5]
    assert str(records) == """PositionSample3fX[
  p = [[1, 1, 1],
       [0.9, 0.9, 0.9],
       [0.7, 0.7, 0.7],
       [1.2, 1.5, 1.1],
       [1.5, 1.5, 1.5]],
  n = [[0, 0, 0],
       [0, 0, 0],
       [0, 0, 0],
       [0, 0, 0],
       [0, 0, 0]],
  uv = [[0, 0],
        [0, 0],
        [0, 0],
        [0, 0],
        [0, 0]],
  time = [0, 0.5, 0.7, 1, 1.5],
  pdf = [0, 0, 0, 0, 0],
  delta = [0, 0, 0, 0, 0],
  object = [nullptr, nullptr, nullptr, nullptr, nullptr]
]"""

    # SurfaceInteraction constructor
    si = SurfaceInteraction3f.zero(n_records)
    si.time = [0.0, 0.5, 0.7, 1.0, 1.5]
    records = PositionSample3f(si)
    assert ek.all(records.time == si.time)
Example #22
0
def test02_depth_scalar_bunny(variant_scalar_rgb):
    from mitsuba.core import Ray3f, BoundingBox3f
    from mitsuba.core.xml import load_string
    from mitsuba.render import SurfaceInteraction3f

    if mitsuba.core.MTS_ENABLE_EMBREE:
        pytest.skip("EMBREE enabled")

    scene = load_string("""
        <scene version="0.5.0">
            <shape type="ply">
                <string name="filename" value="resources/data/ply/bunny_lowres.ply"/>
            </shape>
        </scene>
    """)
    b = scene.bbox()

    n = 100
    inv_n = 1.0 / (n - 1)
    wavelengths = []

    for x in range(n):
        for y in range(n):
            o = [
                b.min[0] * (1 - x * inv_n) + b.max[0] * x * inv_n,
                b.min[1] * (1 - y * inv_n) + b.max[1] * y * inv_n, b.min[2]
            ]
            d = [0, 0, 1]
            r = Ray3f(o, d, 0.5, wavelengths)
            r.mint = 0
            r.maxt = 100

            res_naive = scene.ray_intersect_naive(r)
            res = scene.ray_intersect(r)
            res_shadow = scene.ray_test(r)
            assert ek.all(res_shadow == res_naive.is_valid())
            compare_results(res_naive, res)
def test02_crops(variant_scalar_rgb):
    from mitsuba.core.xml import load_string

    film = load_string("""<film version="2.0.0" type="hdrfilm">
            <integer name="width" value="32"/>
            <integer name="height" value="21"/>
            <integer name="crop_width" value="11"/>
            <integer name="crop_height" value="5"/>
            <integer name="crop_offset_x" value="2"/>
            <integer name="crop_offset_y" value="3"/>
            <boolean name="high_quality_edges" value="true"/>
            <string name="pixel_format" value="rgba"/>
        </film>""")
    assert film is not None
    assert ek.all(film.size() == [32, 21])
    assert ek.all(film.crop_size() == [11, 5])
    assert ek.all(film.crop_offset() == [2, 3])
    assert film.has_high_quality_edges()

    # Crop size doesn't adjust its size, so an error should be raised if the
    # resulting crop window goes out of bounds.
    incomplete = """<film version="2.0.0" type="hdrfilm">
            <integer name="width" value="32"/>
            <integer name="height" value="21"/>
            <integer name="crop_offset_x" value="30"/>
            <integer name="crop_offset_y" value="20"/>"""
    with pytest.raises(RuntimeError):
        film = load_string(incomplete + "</film>")
    film = load_string(incomplete + """
            <integer name="crop_width" value="2"/>
            <integer name="crop_height" value="1"/>
        </film>""")
    assert film is not None
    assert ek.all(film.size() == [32, 21])
    assert ek.all(film.crop_size() == [2, 1])
    assert ek.all(film.crop_offset() == [30, 20])
Example #24
0
def test08_morton3(variant_scalar_rgb):
    from mitsuba.core import math
    v0 = [123, 456, 789]
    v1 = math.morton_encode3(v0)
    v2 = math.morton_decode3(v1)
    assert ek.all(v0 == v2)
Example #25
0
def compare_results(res_a, res_b, atol=0.0):
    assert ek.all(res_a.is_valid() == res_b.is_valid())
    if ek.any(res_a.is_valid()):
        assert ek.allclose(res_a.t, res_b.t,
                           atol=atol), "\n%s\n\n%s" % (res_a.t, res_b.t)
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 test03_ray_intersect(variant_scalar_rgb):
    if mitsuba.core.MTS_ENABLE_EMBREE:
        pytest.skip("EMBREE enabled")

    from mitsuba.core.xml import load_string
    from mitsuba.core import Ray3f

    # Scalar
    scene = load_string("""<scene version="2.0.0">
        <shape type="rectangle">
            <transform name="to_world">
                <scale x="2" y="0.5" z="1"/>
            </transform>
        </shape>
    </scene>""")

    n = 15
    coords = ek.linspace(Float, -1, 1, n)
    rays = [
        Ray3f(o=[a, a, 5], d=[0, 0, -1], time=0.0, wavelengths=[])
        for a in coords
    ]
    si_scalar = []
    valid_count = 0
    for i in range(n):
        # print(rays[i])
        its_found = scene.ray_test(rays[i])
        si = scene.ray_intersect(rays[i])

        assert its_found == (abs(coords[i]) <= 0.5)
        assert si.is_valid() == its_found
        si_scalar.append(si)
        valid_count += its_found

    assert valid_count == 7

    try:
        mitsuba.set_variant('packet_rgb')
        from mitsuba.core.xml import load_string as load_string_packet
        from mitsuba.core import Ray3f as Ray3fX
    except ImportError:
        pytest.skip("packet_rgb mode not enabled")

    # Packet
    scene_p = load_string_packet("""<scene version="2.0.0">
        <shape type="rectangle">
            <transform name="to_world">
                <scale x="2" y="0.5" z="1"/>
            </transform>
        </shape>
    </scene>""")

    packet = Ray3fX.zero(n)
    for i in range(n):
        packet[i] = rays[i]

    si_p = scene_p.ray_intersect(packet)
    its_found_p = scene_p.ray_test(packet)

    assert ek.all(si_p.is_valid() == its_found_p)

    for i in range(n):
        assert ek.allclose(si_p.t[i], si_scalar[i].t)
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))
Example #29
0
def test02_sample_vs_pcg32(variant_scalar_rgb, sampler):
    # IndependentSampler uses the default-constructed PCG if no seed is provided.
    ref = ek.scalar.PCG32()
    for i in range(10):
        assert ek.all(sampler.next_1d() == next_float(ref))
        assert ek.all(sampler.next_2d() == [next_float(ref), next_float(ref)])
Example #30
0
    def tabulate_histogram(self):
        """
        Invoke the provided sampling strategy many times and generate a
        histogram in the parameter domain. If ``sample_func`` returns a tuple
        ``(positions, weights)`` instead of just positions, the samples are
        considered to be weighted.
        """

        # Generate a table of uniform variates
        from mitsuba.core import Float, Vector2f, Vector2u, Float32, \
            UInt64, PCG32

        rng = PCG32(initseq=ek.arange(UInt64, self.sample_count))

        samples_in = getattr(mitsuba.core, 'Vector%if' % self.sample_dim)()
        for i in range(self.sample_dim):
            samples_in[i] = rng.next_float32() if Float is Float32 \
                else rng.next_float64()

        self.pdf_start = time.time()

        # Invoke sampling strategy
        samples_out = self.sample_func(samples_in)

        if type(samples_out) is tuple:
            weights_out = samples_out[1]
            samples_out = samples_out[0]
        else:
            weights_out = Float(1.0)

        # Map samples into the parameter domain
        xy = self.domain.map_backward(samples_out)

        # Sanity check
        eps = self.bounds.extents() * 1e-4
        in_domain = ek.all((xy >= self.bounds.min - eps)
                           & (xy <= self.bounds.max + eps))
        if not ek.all(in_domain):
            self._log('Encountered samples outside of the specified '
                      'domain: %s' % str(ek.compress(xy, ~in_domain)))
            self.fail = True

        # Normalize position values
        xy = (xy - self.bounds.min) / self.bounds.extents()
        xy = Vector2u(
            ek.clamp(xy * Vector2f(self.res), 0, Vector2f(self.res - 1)))

        # Compute a histogram of the positions in the parameter domain
        self.histogram = ek.zero(Float, ek.hprod(self.res))

        ek.scatter_add(target=self.histogram,
                       index=xy.x + xy.y * self.res.x,
                       source=weights_out)

        self.pdf_end = time.time()

        histogram_min = ek.hmin(self.histogram)
        if not histogram_min >= 0:
            self._log('Encountered a cell with negative sample '
                      'weights: %f' % histogram_min)
            self.fail = True

        self.histogram_sum = ek.hsum(self.histogram) / self.sample_count
        if self.histogram_sum > 1.1:
            self._log('Sample weights add up to a value greater '
                      'than 1.0: %f' % self.histogram_sum)
            self.fail = True