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
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)
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)
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
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)))
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
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])
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))
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"])
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)
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)
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])
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)
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))
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)])
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