def test_input(value, valid): expected = (r'.*unreferenced property ."test_number".*' if valid else r'could not parse floating point value "{}".'.format(value)) with pytest.raises(Exception, match=expected): xml.load_string("""<scene version="2.0.0"> <float name="test_number" value="{}"/> </scene>""".format(value))
def test12_missing_attribute(variant_scalar_rgb): from mitsuba.core import xml with pytest.raises(Exception) as e: xml.load_string("""<scene version="2.0.0"> <integer name="a"/></scene>""") e.match('missing attribute "value" in element "integer".')
def test03_invalid_root_node(variant_scalar_rgb): from mitsuba.core import xml with pytest.raises(Exception) as e: xml.load_string('<?xml version="1.0"?><integer name="a" ' 'value="10"></integer>') e.match('root element "integer" must be an object')
def create_emitter_and_spectrum(lookat, cutoff_angle, s_key='d65'): from mitsuba.core.xml import load_string emitter = load_string("""<emitter version='2.0.0' type='spot'> <float name='cutoff_angle' value='{ca}'/> <transform name='to_world'> <lookat origin='{px}, {py}, {pz}' target='{tx}, {ty}, {tz}' up='{ux}, {uy}, {uz}'/> </transform> {s} </emitter>""".format(ca=cutoff_angle, px=lookat["pos"][0], py=lookat["pos"][1], pz=lookat["pos"][2], tx=lookat["target"][0], ty=lookat["target"][1], tz=lookat["target"][2], ux=lookat["up"][0], uy=lookat["up"][1], uz=lookat["up"][2], s=spectrum_strings[s_key])) spectrum = load_string(spectrum_strings[s_key]) expanded = spectrum.expand() if len(expanded) == 1: spectrum = expanded[0] return emitter, spectrum
def test01_create(variant_scalar_rgb): from mitsuba.render import BSDFFlags from mitsuba.core.xml import load_string bsdf = load_string("""<bsdf version="2.0.0" type="blendbsdf"> <bsdf type="diffuse"/> <bsdf type="diffuse"/> <spectrum name="weight" value="0.2"/> </bsdf>""") assert bsdf is not None assert bsdf.component_count() == 2 assert bsdf.flags(0) == BSDFFlags.DiffuseReflection | BSDFFlags.FrontSide assert bsdf.flags(1) == BSDFFlags.DiffuseReflection | BSDFFlags.FrontSide assert bsdf.flags() == bsdf.flags(0) | bsdf.flags(1) bsdf = load_string("""<bsdf version="2.0.0" type="blendbsdf"> <bsdf type="roughconductor"/> <bsdf type="diffuse"/> <spectrum name="weight" value="0.2"/> </bsdf>""") assert bsdf is not None assert bsdf.component_count() == 2 assert bsdf.flags(0) == BSDFFlags.GlossyReflection | BSDFFlags.FrontSide assert bsdf.flags(1) == BSDFFlags.DiffuseReflection | BSDFFlags.FrontSide assert bsdf.flags() == bsdf.flags(0) | bsdf.flags(1)
def test14_missing_parameter(variant_scalar_rgb): from mitsuba.core import xml with pytest.raises(Exception) as e: xml.load_string("""<scene version="2.0.0"> <shape type="ply"/> </scene>""") e.match('Property "filename" has not been specified')
def test11_unknown_attribute(variant_scalar_rgb): from mitsuba.core import xml with pytest.raises(Exception) as e: xml.load_string("""<scene version="2.0.0"> <shape type="ply" param2="abc"> </shape></scene>""") e.match('unexpected attribute "param2" in element "shape".')
def test10_unknown_id(variant_scalar_rgb): from mitsuba.core import xml with pytest.raises(Exception) as e: xml.load_string("""<scene version="2.0.0"> <ref id="unknown"/> </scene>""") e.match('reference to unknown object "unknown"')
def test06_reserved_id(variant_scalar_rgb): from mitsuba.core import xml with pytest.raises(Exception) as e: xml.load_string('<scene version="2.0.0">' + '<shape type="ply" id="_test"/></scene>') e.match('invalid id "_test" in element "shape": leading underscores ' 'are reserved for internal identifiers.')
def test06_reserved_name(variant_scalar_rgb): from mitsuba.core import xml with pytest.raises(Exception) as e: xml.load_string('<scene version="2.0.0">' + '<shape type="ply">' + '<integer name="_test" value="1"/></shape></scene>') e.match('invalid parameter name "_test" in element "integer": ' 'leading underscores are reserved for internal identifiers.')
def test18_invalid_boolean(variant_scalar_rgb): from mitsuba.core import xml with pytest.raises(Exception) as e: xml.load_string("""<scene version="2.0.0"> <boolean name="10" value="a"/> </scene>""") e.match('could not parse boolean value "a"' ' -- must be "true" or "false".')
def test04_valid_root_node(variant_scalar_rgb): from mitsuba.core import xml from mitsuba.render import Scene obj1 = xml.load_string('<?xml version="1.0"?>\n<scene version="2.0.0">' '</scene>') obj2 = xml.load_string('<scene version="2.0.0"></scene>') assert type(obj1) is Scene assert type(obj2) is Scene
def test08_incorrect_nesting(variant_scalar_rgb): from mitsuba.core import xml with pytest.raises(Exception) as e: xml.load_string("""<scene version="2.0.0"> <shape type="ply"> <translate name="value" x="0" y="1" z="2"/> </shape></scene>""") e.match('transform operations can only occur in a transform node')
def check_scene(xml, count, error=None): xml = """<scene version="2.0.0"> {} </scene>""".format(xml) if error is None: scene = load_string(xml) assert len(scene.emitters()) == count else: with pytest.raises(RuntimeError, match='.*{}.*'.format(error)): load_string(xml)
def test22_fileresolver_unchanged(variant_scalar_rgb): from mitsuba.core import xml, Thread fs_backup = Thread.thread().file_resolver() xml.load_string("""<scene version="2.0.0"> <path value="/tmp"/> </scene>""") assert fs_backup == Thread.thread().file_resolver()
def test15_incorrect_parameter_type(variant_scalar_rgb): from mitsuba.core import xml with pytest.raises(Exception) as e: xml.load_string("""<scene version="2.0.0"> <shape type="ply"> <float name="filename" value="1.0"/> </shape></scene>""") e.match('The property "filename" has the wrong type' ' \\(expected <string>\\).')
def test07_incorrect_nesting(variant_scalar_rgb): from mitsuba.core import xml with pytest.raises(Exception) as e: xml.load_string("""<scene version="2.0.0"> <shape type="ply"> <integer name="value" value="1"> <float name="value" value="1"/> </integer></shape></scene>""") e.match('node "float" cannot occur as child of a property')
def test21_path_at_root_only(variant_scalar_rgb): from mitsuba.core import xml err_str = 'can only be child of root' with pytest.raises(Exception) as e: xml.load_string("""<scene version="2.0.0"> <bsdf type="dielectric"> <path value="/tmp"/> </bsdf> </scene>""") e.match(err_str)
def create_emitter_and_spectrum(s_key='d65'): from mitsuba.core.xml import load_string emitter = load_string("""<emitter version='2.0.0' type='constant'> {s} </emitter>""".format(s=spectrum_strings[s_key])) spectrum = load_string(spectrum_strings[s_key]) expanded = spectrum.expand() if len(expanded) == 1: spectrum = expanded[0] return emitter, spectrum
def test09_incorrect_nesting(variant_scalar_rgb): from mitsuba.core import xml with pytest.raises(Exception) as e: xml.load_string("""<scene version="2.0.0"> <shape type="ply"> <transform name="toWorld"> <integer name="value" value="10"/> </transform> </shape></scene>""") e.match('transform nodes can only contain transform operations')
def test23_unreferenced_object(variant_scalar_rgb): from mitsuba.core import xml plugins = [('bsdf', 'diffuse'), ('emitter', 'point'), ('shape', 'sphere'), ('sensor', 'perspective')] for interface, name in plugins: with pytest.raises(Exception) as e: xml.load_string("""<{interface} version="2.0.0" type="{name}"> <rgb name="aaa" value="0.5"/> </{interface}>""".format(interface=interface, name=name)) e.match("unreferenced object")
def test05_duplicate_id(variant_scalar_rgb): from mitsuba.core import xml with pytest.raises(Exception) as e: xml.load_string(""" <scene version="2.0.0"> <shape type="ply" id="my_id"/> <shape type="ply" id="my_id"/> </scene> """) e.match('Error while loading "<string>" \\(at line 4, col 14\\):' ' "shape" has duplicate id "my_id" \\(previous was at line 3,' ' col 14\\)')
def test19_invalid_vector(variant_scalar_rgb): from mitsuba.core import xml err_str = 'could not parse floating point value "a"' err_str2 = '"value" attribute must have exactly 1 or 3 elements' err_str3 = 'can\'t mix and match "value" and "x"/"y"/"z" attributes' with pytest.raises(Exception) as e: xml.load_string("""<scene version="2.0.0"> <vector name="10" x="a" y="b" z="c"/> </scene>""") e.match(err_str) with pytest.raises(Exception) as e: xml.load_string("""<scene version="2.0.0"> <vector name="10" value="a, b, c"/> </scene>""") e.match(err_str) with pytest.raises(Exception) as e: xml.load_string("""<scene version="2.0.0"> <vector name="10" value="1, 2"/> </scene>""") e.match(err_str2) with pytest.raises(Exception) as e: xml.load_string("""<scene version="2.0.0"> <vector name="10" value="1, 2, 3" x="4"/> </scene>""") e.match(err_str3)
def create_emitter_and_spectrum(pos, s_key='d65'): from mitsuba.core.xml import load_string emitter = load_string("""<emitter version='2.0.0' type='point'> <point name='position' x='{x}' y='{y}' z='{z}'/> {s} </emitter>""".format(x=pos[0], y=pos[1], z=pos[2], s=spectrum_strings[s_key])) spectrum = load_string(spectrum_strings[s_key]) expanded = spectrum.expand() if len(expanded) == 1: spectrum = expanded[0] return emitter, spectrum
def create_camera(o, d, fov=34, fov_axis='x', s_open=1.5, s_close=5): from mitsuba.core.xml import load_string return load_string("""<sensor version='2.0.0' type='perspective'> <float name='near_clip' value='1'/> <float name='far_clip' value='35'/> <float name='focus_distance' value='15'/> <float name='fov' value='{fov}'/> <string name='fov_axis' value='{fov_axis}'/> <float name='shutter_open' value='{so}'/> <float name='shutter_close' value='{sc}'/> <transform name="to_world"> <lookat origin="{ox}, {oy}, {oz}" target="{tx}, {ty}, {tz}" up =" 0.0, 1.0, 0.0"/> </transform> <film type="hdrfilm"> <integer name="width" value="512"/> <integer name="height" value="256"/> </film> </sensor> """.format(ox=o[0], oy=o[1], oz=o[2], tx=o[0] + d[0], ty=o[1] + d[1], tz=o[2] + d[2], fov=fov, fov_axis=fov_axis, so=s_open, sc=s_close))
def create_medium_scene(phase_function='isotropic', spp=8): scene = load_string(f""" <scene version='2.0.0'> <integrator type="volpath"/> <sensor type="perspective"> <transform name="to_world"> <lookat target="0.0, 0.0, 1.0" origin="0.0, 14.0, 1.0" up ="0.0, 0.0, 1.0"/> </transform> <film type="hdrfilm"> <integer name="width" value="32"/> <integer name="height" value="32"/> <string name="pixel_format" value="rgb" /> </film> <sampler type="independent"> <integer name="sample_count" value="{spp}"/> </sampler> </sensor> <emitter type="constant" /> <shape type="ply"> <string name="filename" value="resources/data/ply/teapot.ply"/> <bsdf type="null" /> <medium name="interior" type="homogeneous"> <rgb name="sigma_t" value="1.0, 1.0, 1.0"/> <rgb name="albedo" value="0.99, 0.4, 0.4"/> <phase type="{phase_function}"/> <boolean name="sample_emitters" value="true"/> </medium> </shape> </scene> """) assert scene is not None return scene
def test11_parameters_grad_enabled(variant_gpu_autodiff_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> ''') assert shape.parameters_grad_enabled() == False # Get the shape's parameters params = traverse(shape) # Only parameters of the shape should affect the result of that method bsdf_param_key = 'bsdf.reflectance.value' ek.set_requires_gradient(params[bsdf_param_key]) params.set_dirty(bsdf_param_key) params.update() assert shape.parameters_grad_enabled() == False # When setting one of the shape's param to require gradient, method should return True shape_param_key = 'vertex_positions_buf' ek.set_requires_gradient(params[shape_param_key]) params.set_dirty(shape_param_key) params.update() assert shape.parameters_grad_enabled() == True
def test02_eval_all(variant_scalar_rgb): from mitsuba.core import Frame3f from mitsuba.render import BSDFFlags, BSDFContext, SurfaceInteraction3f from mitsuba.core.xml import load_string from mitsuba.core.math import InvPi weight = 0.2 bsdf = load_string("""<bsdf version="2.0.0" type="blendbsdf"> <bsdf type="diffuse"> <spectrum name="reflectance" value="0.0"/> </bsdf> <bsdf type="diffuse"> <spectrum name="reflectance" value="1.0"/> </bsdf> <spectrum name="weight" value="{}"/> </bsdf>""".format(weight)) si = SurfaceInteraction3f() si.t = 0.1 si.p = [0, 0, 0] si.n = [0, 0, 1] si.sh_frame = Frame3f(si.n) si.wi = [0, 0, 1] wo = [0, 0, 1] ctx = BSDFContext() # Evaluate the blend of both components expected = (1 - weight) * 0.0 * InvPi + weight * 1.0 * InvPi value = bsdf.eval(ctx, si, wo) assert ek.allclose(value, expected)
def test04_sample_direct(variant_scalar_rgb): from mitsuba.core.xml import load_string from mitsuba.core import Ray3f from mitsuba.render import Interaction3f if mitsuba.core.MTS_ENABLE_EMBREE: pytest.skip("EMBREE enabled") sphere = load_string('<shape type="sphere" version="2.0.0"/>') def sample_cone(sample, cos_theta_max): cos_theta = (1 - sample[1]) + sample[1] * cos_theta_max sin_theta = ek.sqrt(1 - cos_theta * cos_theta) phi = 2 * ek.pi * sample[0] s, c = ek.sin(phi), ek.cos(phi) return [c * sin_theta, s * sin_theta, cos_theta] it = Interaction3f.zero() it.p = [0, 0, -3] it.t = 0 sin_cone_angle = 1.0 / it.p[2] cos_cone_angle = ek.sqrt(1 - sin_cone_angle**2) for xi_1 in ek.linspace(Float, 0, 1, 10): for xi_2 in ek.linspace(Float, 1e-3, 1 - 1e-3, 10): sample = sphere.sample_direction(it, [xi_2, 1 - xi_1]) d = sample_cone([xi_1, xi_2], cos_cone_angle) its = sphere.ray_intersect(Ray3f(it.p, d, 0, [])) assert ek.allclose(d, sample.d, atol=1e-5, rtol=1e-5) assert ek.allclose(its.t, sample.dist, atol=1e-5, rtol=1e-5) assert ek.allclose(its.p, sample.p, atol=1e-5, rtol=1e-5)
def test14_differentiable_surface_interaction_ray_backward( variant_gpu_autodiff_rgb): from mitsuba.core import xml, Ray3f, Vector3f, UInt32 scene = xml.load_string(''' <scene version="2.0.0"> <shape type="obj" id="rect"> <string name="filename" value="resources/data/common/meshes/rectangle.obj"/> </shape> </scene> ''') ray = Ray3f(Vector3f(-0.3, -0.4, -10.0), Vector3f(0.0, 0.0, 1.0), 0, []) pi = scene.ray_intersect_preliminary(ray) ek.set_requires_gradient(ray.o) # If si.p is shifted along the x-axis, so does the ray origin si = pi.compute_surface_interaction(ray) ek.backward(si.p.x) assert ek.allclose(ek.gradient(ray.o), [1, 0, 0]) # If si.t is changed, so does the ray origin along the z-axis si = pi.compute_surface_interaction(ray) ek.backward(si.t) assert ek.allclose(ek.gradient(ray.o), [0, 0, -1])