def test03_ray_intersect_instance(variants_all_rgb): from mitsuba.core import xml, Ray3f, ScalarVector3f, ScalarTransform4f as T """Check that we can the correct instance pointer when tracing a ray""" scene = xml.load_dict({ 'type': 'scene', 'group_0': { 'type': 'shapegroup', 'shape': { 'type': 'rectangle' } }, 'instance_00': { 'type': 'instance', "group": { "type": "ref", "id": "group_0" }, 'to_world': T.translate([-0.5, -0.5, 0.0]) * T.scale(0.5) }, 'instance_01': { 'type': 'instance', "group": { "type": "ref", "id": "group_0" }, 'to_world': T.translate([-0.5, 0.5, 0.0]) * T.scale(0.5) }, 'instance_10': { 'type': 'instance', "group": { "type": "ref", "id": "group_0" }, 'to_world': T.translate([0.5, -0.5, 0.0]) * T.scale(0.5) }, 'shape': { 'type': 'rectangle', 'to_world': T.translate([0.5, 0.5, 0.0]) * T.scale(0.5) } }) ray = Ray3f([-0.5, -0.5, -12], [0.0, 0.0, 1.0], 0.0, []) pi = scene.ray_intersect_preliminary(ray) assert '[0.5, 0, 0, -0.5]' in str(pi) assert '[0, 0.5, 0, -0.5]' in str(pi) ray = Ray3f([-0.5, 0.5, -12], [0.0, 0.0, 1.0], 0.0, []) pi = scene.ray_intersect_preliminary(ray) assert '[0.5, 0, 0, -0.5]' in str(pi) assert '[0, 0.5, 0, 0.5]' in str(pi) ray = Ray3f([0.5, -0.5, -12], [0.0, 0.0, 1.0], 0.0, []) pi = scene.ray_intersect_preliminary(ray) assert '[0.5, 0, 0, 0.5]' in str(pi) assert '[0, 0.5, 0, -0.5]' in str(pi) ray = Ray3f([0.5, 0.5, -12], [0.0, 0.0, 1.0], 0.0, []) pi = scene.ray_intersect_preliminary(ray) assert 'instance = nullptr' in str(pi) or 'instance = [nullptr]' in str(pi)
def test_onedim_experiment_kernel_dict(modes_all): """ Test non-trivial kernel dict generation behaviour. """ from mitsuba.core import ScalarTransform4f ctx = KernelDictContext() # Surface width is appropriately inherited from atmosphere exp = OneDimExperiment(atmosphere=HomogeneousAtmosphere( width=ureg.Quantity(42.0, "km"))) kernel_dict = exp.kernel_dict(ctx) assert np.allclose( kernel_dict["surface"]["to_world"].matrix, ScalarTransform4f.scale([21000, 21000, 1]).matrix, ) # Setting atmosphere to None exp = OneDimExperiment( atmosphere=None, surface={ "type": "lambertian", "width": 100.0, "width_units": "m" }, measures=[ { "type": "distant", "id": "distant_measure" }, { "type": "radiancemeter", "origin": [1, 0, 0], "id": "radiancemeter" }, ], ) # -- Surface width is not overridden kernel_dict = exp.kernel_dict(ctx) assert np.allclose( kernel_dict["surface"]["to_world"].matrix, ScalarTransform4f.scale([50, 50, 1]).matrix, ) # -- Atmosphere is not in kernel dictionary assert "atmosphere" not in kernel_dict # -- Measures get no external medium assigned assert "medium" not in kernel_dict["distant_measure"] assert "medium" not in kernel_dict["radiancemeter"]
def bsdfs(self, ctx: KernelDictContext) -> KernelDict: from mitsuba.core import ScalarTransform4f returndict = KernelDict({ f"bsdf_{self.id}": { "type": "blendbsdf", "inner_bsdf": onedict_value(self.central_patch.bsdfs(ctx=ctx)), "outer_bsdf": onedict_value(self.background_surface.bsdfs(ctx=ctx)), "weight": { "type": "bitmap", "filename": str( eradiate.path_resolver.resolve( "textures/rami4atm_experiment_surface_mask.bmp")), "filter_type": "nearest", "wrap_mode": "clamp", }, } }) scale = self._compute_scale_parameter(ctx=ctx) trafo = ScalarTransform4f.scale(scale) * ScalarTransform4f.translate(( -0.5 + (0.5 / scale), -0.5 + (0.5 / scale), 0, )) returndict[f"bsdf_{self.id}"]["weight"]["to_uv"] = trafo return returndict
def shapes(self, ctx: KernelDictContext) -> t.Dict: from mitsuba.core import ScalarTransform4f if ctx.ref: bsdf = {"type": "ref", "id": f"bsdf_{self.id}"} else: bsdf = self.bsdfs(ctx=ctx)[f"bsdf_{self.id}"] if self.mesh_units is None: scaling_factor = 1.0 else: kernel_length = uck.get("length") scaling_factor = (1.0 * self.mesh_units).m_as(kernel_length) base_dict = { "filename": str(self.mesh_filename), "bsdf": bsdf, "to_world": ScalarTransform4f.scale(scaling_factor), } if self.mesh_filename.suffix == ".obj": base_dict["type"] = "obj" elif self.mesh_filename.suffix == ".ply": base_dict["type"] = "ply" else: raise ValueError( f"unsupported file extension '{self.mesh_filename.suffix}'") return {self.id: base_dict}
def example_scene(shape, scale=1.0, translate=[0, 0, 0], angle=0.0): from mitsuba.core import xml, ScalarTransform4f as T to_world = T.translate(translate) * T.rotate([0, 1, 0], angle) * T.scale(scale) shape2 = shape.copy() shape2['to_world'] = to_world s = xml.load_dict({'type': 'scene', 'shape': shape2}) s_inst = xml.load_dict({ 'type': 'scene', 'group_0': { 'type': 'shapegroup', 'shape': shape }, 'instance': { 'type': 'instance', "group": { "type": "ref", "id": "group_0" }, 'to_world': to_world } }) return s, s_inst
def test_ramiatm_experiment_surface_adjustment(mode_mono): """Create a Rami4ATM experiment and assert the central patch surface is created with the correct parameters, according to the canopy and atmosphere.""" from mitsuba.core import ScalarTransform4f ctx = KernelDictContext() s = Rami4ATMExperiment( atmosphere=HomogeneousAtmosphere(width=ureg.Quantity(42.0, "km")), canopy=DiscreteCanopy.homogeneous( lai=3.0, leaf_radius=0.1 * ureg.m, l_horizontal=10.0 * ureg.m, l_vertical=2.0 * ureg.m, padding=0, ), surface=CentralPatchSurface(central_patch=LambertianSurface(), background_surface=LambertianSurface()), ) expected_trafo = ScalarTransform4f.scale( 1400) * ScalarTransform4f.translate((-0.499642857, -0.499642857, 0.0)) kernel_dict = s.kernel_dict(ctx=ctx) assert np.allclose(kernel_dict["bsdf_surface"]["weight"]["to_uv"].matrix, expected_trafo.matrix)
def test01_create(variant_scalar_rgb): from mitsuba.core import xml, ScalarTransform4f s = xml.load_dict({"type": "sphere"}) assert s is not None assert s.primitive_count() == 1 assert ek.allclose(s.surface_area(), 4 * ek.pi) # Test transforms order in constructor rot = ScalarTransform4f.rotate([1.0, 0.0, 0.0], 35) s1 = xml.load_dict({ "type": "sphere", "radius": 2.0, "center": [1, 0, 0], "to_world": rot }) s2 = xml.load_dict({ "type": "sphere", "to_world": rot * ScalarTransform4f.translate([1, 0, 0]) * ScalarTransform4f.scale(2) }) assert str(s1) == str(s2)
def shapes(self, ctx: KernelDictContext) -> t.Dict: """ Return shape plugin specifications. Parameters ---------- ctx : :class:`.KernelDictContext` A context data structure containing parameters relevant for kernel dictionary generation. Returns ------- dict A dictionary suitable for merge with a :class:`~eradiate.scenes.core.KernelDict` containing all the shapes in the abstract tree. """ from mitsuba.core import ScalarTransform4f kernel_length = uck.get("length") kernel_height = self.trunk_height.m_as(kernel_length) kernel_radius = self.trunk_radius.m_as(kernel_length) leaf_cloud = self.leaf_cloud.translated( [0.0, 0.0, kernel_height] * kernel_length + self.leaf_cloud_extra_offset.to(kernel_length)) if ctx.ref: bsdf = {"type": "ref", "id": f"bsdf_{self.id}"} else: bsdf = self.bsdfs(ctx=ctx)[f"bsdf_{self.id}"] shapes_dict = leaf_cloud.shapes(ctx=ctx) shapes_dict[f"trunk_cyl_{self.id}"] = { "type": "cylinder", "bsdf": bsdf, "radius": kernel_radius, "p0": [0, 0, -0.1], "p1": [0, 0, kernel_height], } shapes_dict[f"trunk_cap_{self.id}"] = { "type": "disk", "bsdf": bsdf, "to_world": ScalarTransform4f.scale(kernel_radius) * ScalarTransform4f.translate(((0, 0, kernel_height))), } return shapes_dict
def test_ramiatm_experiment_kernel_dict(mode_mono, padding): from mitsuba.core import ScalarTransform4f ctx = KernelDictContext() # Surface width is appropriately inherited from canopy, when no atmosphere is present s = Rami4ATMExperiment( atmosphere=None, canopy=DiscreteCanopy.homogeneous( lai=3.0, leaf_radius=0.1 * ureg.m, l_horizontal=10.0 * ureg.m, l_vertical=2.0 * ureg.m, padding=padding, ), measures=[ { "type": "distant", "id": "distant_measure" }, { "type": "radiancemeter", "origin": [1, 0, 0], "id": "radiancemeter" }, ], ) kernel_scene = s.kernel_dict(ctx) assert np.allclose( kernel_scene["surface"]["to_world"].transform_point([1, -1, 0]), [5 * (2 * padding + 1), -5 * (2 * padding + 1), 0], ) # -- Measures get no external medium assigned assert "medium" not in kernel_scene["distant_measure"] assert "medium" not in kernel_scene["radiancemeter"] # Surface width is appropriately inherited from atmosphere s = Rami4ATMExperiment( atmosphere=HomogeneousAtmosphere(width=ureg.Quantity(42.0, "km")), canopy=DiscreteCanopy.homogeneous( lai=3.0, leaf_radius=0.1 * ureg.m, l_horizontal=10.0 * ureg.m, l_vertical=2.0 * ureg.m, padding=padding, ), ) kernel_dict = s.kernel_dict(ctx) assert np.allclose( kernel_dict["surface"]["to_world"].matrix, ScalarTransform4f.scale([21000, 21000, 1]).matrix, )
def shapes(self, ctx: KernelDictContext) -> t.Dict: """ Return shape plugin specifications. Parameters ---------- ctx : :class:`.KernelDictContext` A context data structure containing parameters relevant for kernel dictionary generation. Returns ------- dict A dictionary suitable for merge with a :class:`.KernelDict` containing all the shapes in the leaf cloud. """ from mitsuba.core import ScalarTransform4f, coordinate_system kernel_length = uck.get("length") shapes_dict = {} if ctx.ref: bsdf = {"type": "ref", "id": f"bsdf_{self.id}"} else: bsdf = self.bsdfs(ctx=ctx)[f"bsdf_{self.id}"] for i_leaf, (position, normal, radius) in enumerate( zip( self.leaf_positions.m_as(kernel_length), self.leaf_orientations, self.leaf_radii.m_as(kernel_length), ) ): _, up = coordinate_system(normal) to_world = ScalarTransform4f.look_at( origin=position, target=position + normal, up=up ) * ScalarTransform4f.scale(radius) shapes_dict[f"{self.id}_leaf_{i_leaf}"] = { "type": "disk", "bsdf": bsdf, "to_world": to_world, } return shapes_dict
def test_centralpatch_scale_kernel_dict(mode_mono): from mitsuba.core import ScalarTransform4f cs = CentralPatchSurface( width=3000.0 * ureg.km, central_patch=LambertianSurface(width=100 * ureg.km), id="surface", ) ctx = KernelDictContext() kernel_dict = cs.bsdfs(ctx=ctx) assert np.allclose( kernel_dict["bsdf_surface"]["weight"]["to_uv"].matrix, (ScalarTransform4f.scale(10) * ScalarTransform4f.translate( (-0.45, -0.45, 0))).matrix, )
def kernel_item(self) -> t.Dict: """Return kernel item.""" from mitsuba.core import ScalarTransform4f xmin = self.xmin.m_as(uck.get("length")) xmax = self.xmax.m_as(uck.get("length")) ymin = self.ymin.m_as(uck.get("length")) ymax = self.ymax.m_as(uck.get("length")) z = self.z.m_as(uck.get("length")) dx = xmax - xmin dy = ymax - ymin to_world = ScalarTransform4f.translate([ 0.5 * dx + xmin, 0.5 * dy + ymin, z ]) * ScalarTransform4f.scale([0.5 * dx, 0.5 * dy, 1.0]) return {"type": "rectangle", "to_world": to_world}
def map_cube(xmin: float, xmax: float, ymin: float, ymax: float, zmin: float, zmax: float) -> "mitsuba.core.ScalarTransform4f": r""" Map the cube :math:`[-1, 1]^3` to :math:`[x_\mathrm{min}, x_\mathrm{max}] \times [y_\mathrm{min}, y_\mathrm{max}] \times [z_\mathrm{min}, z_\mathrm{max}]`. Parameters ---------- xmin : float Minimum X value. xmax : float Maximum X value. ymin : float Minimum Y value. ymax : float Maximum Y value. zmin : float Minimum Z value. zmax : float Maximum Z value. Returns ------- :class:`mitsuba.core.ScalarTransform4f` Computed transform matrix. Warnings -------- You must select a Mitsuba variant before calling this function. """ from mitsuba.core import ScalarTransform4f half_dx = (xmax - xmin) * 0.5 half_dy = (ymax - ymin) * 0.5 half_dz = (zmax - zmin) * 0.5 scale_trafo = ScalarTransform4f.scale([half_dx, half_dy, half_dz]) translate_trafo = ScalarTransform4f.translate( [xmin + half_dx, ymin + half_dy, half_dz + zmin]) return translate_trafo * scale_trafo
def shapes(self, ctx: KernelDictContext) -> KernelDict: """ Return shape plugin specifications only. Parameters ---------- ctx : :class:`.KernelDictContext` A context data structure containing parameters relevant for kernel dictionary generation. Returns ------- :class:`.KernelDict` A kernel dictionary containing all the shapes attached to the surface. """ from mitsuba.core import ScalarTransform4f, ScalarVector3f if ctx.ref: bsdf = {"type": "ref", "id": f"bsdf_{self.id}"} else: bsdf = self.bsdfs(ctx)[f"bsdf_{self.id}"] w = self.kernel_width(ctx).m_as(uck.get("length")) z = self.altitude.m_as(uck.get("length")) translate_trafo = ScalarTransform4f.translate( ScalarVector3f(0.0, 0.0, z)) scale_trafo = ScalarTransform4f.scale( ScalarVector3f(w / 2.0, w / 2.0, 1.0)) trafo = translate_trafo * scale_trafo return KernelDict({ f"shape_{self.id}": { "type": "rectangle", "to_world": trafo, "bsdf": bsdf, } })
def test_maximum_scene_size(mode_mono_double, json_metadata): r""" Maximum scene size (``path``) ============================= This test searches for an order of magnitude of the maximum size a scene using a ``distant`` sensor without ray origin control can have. An arbitrary threshold is used as the pass/fail criterion for regression control. Rationale --------- - Geometry: a square surface with increasing sizes from 1.0 to 1e9. and a Lambertian BRDF with reflectance :math:`\rho = 0.5`. - Illumination: a directional light source at the zenith with radiance :math:`L_\mathrm{i} = 1.0`. - Sensor: a ``distant`` sensor targeting (0, 0, 0) with default ray origin control. Expected behaviour ------------------ For all scene sizes below the parametrized size :code:`min_expected_size` the computational results must be equal to the theoretical prediction within a relative tolerance of 1e-5. """ from mitsuba.core import ScalarTransform4f, ScalarVector3f min_expected_size = 1e2 results = dict() spp = 1 rho = 0.5 li = 1.0 expected = rho * li / np.pi for scene_size in sorted( [10.0 ** i for i in range(1, 9)] + [2.0 * 10 ** i for i in range(1, 8)] + [5.0 * 10 ** i for i in range(1, 8)] ): kernel_dict = KernelDict( { "type": "scene", "bsdf_surface": { "type": "diffuse", "reflectance": rho, }, "surface": { "type": "rectangle", "to_world": ScalarTransform4f.scale( ScalarVector3f(scene_size, scene_size, 1) ), "bsdf": {"type": "ref", "id": "bsdf_surface"}, }, "illumination": { "type": "directional", "direction": [0, 0, -1], "irradiance": li, }, "measure": { "type": "distant", "id": "measure", "ray_target": [0, 0, 0], "sampler": {"type": "independent", "sample_count": spp}, "film": { "type": "hdrfilm", "width": 32, "height": 32, "pixel_format": "luminance", "component_format": "float32", "rfilter": {"type": "box"}, }, }, "integrator": {"type": "path"}, } ) result = mitsuba_run(kernel_dict)["values"]["measure"].img results[scene_size] = np.allclose(result, expected, rtol=1e-5) # Report test metrics passed_sizes = [size for size in results if results[size]] maxsize = np.max(passed_sizes) json_metadata["metrics"] = { "test_maximum_scene_size": { "name": "Maximum scene size", "description": "The maximum scene size is:", "value": f"{float(maxsize):1.1e}", "units": "length units", } } # Final assertion assert np.all( [ result for result in [ results[size] for size in results if size <= min_expected_size ] ] )
params = traverse(scene) print(params) positions_buf = params['grid_mesh.vertex_positions_buf'] positions_initial = ravel(positions_buf) normals_initial = ravel(params['grid_mesh.vertex_normals_buf']) vertex_count = ek.slices(positions_initial) filename = 'scene/diffuser_surface_1.jpg' Thread.thread().file_resolver().append(os.path.dirname(filename)) # Create a texture with the reference displacement map disp_tex_1 = xml.load_dict({ "type" : "bitmap", "filename": "diffuser_surface_1.jpg", "to_uv" : ScalarTransform4f.scale([1, -1, 1]) # texture is upside-down }).expand()[0] # Create a texture with another displacement map disp_tex_2 = xml.load_dict({ "type" : "bitmap", "filename": "diffuser_surface_2.jpg", "to_uv" : ScalarTransform4f.scale([1, -1, 1]) # texture is upside-down }).expand()[0] # Create a fake surface interaction with an entry per vertex on the mesh mesh_si = SurfaceInteraction3f.zero(vertex_count) mesh_si.uv = ravel(params['grid_mesh.vertex_texcoords_buf'], dim=2) # Evaluate the displacement map for the entire mesh disp_tex_diffuser_1 = disp_tex_1.eval_1(mesh_si)
def add_object(self, scene_dict): """ Add registered meshes to given scene file format """ if len(self.bssrdf) != len(self.mesh): exit("The number of registerd mesh and bssrdf are different!") num_mesh = len(self.bssrdf) num_obj = len(self.bssrdf_obj) for i in range(num_obj): i += 1 scene_dict["obj_" + str(i)] = { "type": "shapegroup" } for i in range(num_mesh): i += 1 bssrdf = self.bssrdf[i] mesh = self.mesh[i] axis = None if(mesh["rotate"]["axis"] == "x"): axis = [1, 0, 0] elif(mesh["rotate"]["axis"] == "y"): axis = [0, 1, 0] elif(mesh["rotate"]["axis"] == "z"): axis = [0, 0, 1] angle = mesh["rotate"]["angle"] if mesh["type"] == "rectangle": shape = { "type": mesh["type"], "to_world": ScalarTransform4f.translate(mesh["translate"]) * ScalarTransform4f.rotate(axis, angle) * ScalarTransform4f.scale(mesh["scale"]), } else: shape = { "type": mesh["type"], "filename": mesh["filename"], "to_world": ScalarTransform4f.translate(mesh["translate"]) * ScalarTransform4f.rotate(axis, angle) * ScalarTransform4f.scale(mesh["scale"]), } bssrdf["trans"] = mesh["translate"] if(mesh["rotate"]["axis"] == "x"): bssrdf["rotate_x"] = angle elif(mesh["rotate"]["axis"] == "y"): bssrdf["rotate_y"] = angle elif(mesh["rotate"]["axis"] == "z"): bssrdf["rotate_z"] = angle bssrdf["height_max"] = mesh["height_max"] bsdf = { "bsdf_" + str(i): bssrdf } shape.update(bsdf) for j in range(num_obj): j += 1 if i in self.bssrdf_obj[j]: scene_dict["obj_" + str(j)][str(i)] = shape for i in range(num_obj): i += 1 scene_dict["instance_" + str(i)] = { "type": "instance", "group":{ "type": "ref", "id": "obj_" + str(i) } } return scene_dict
"type": "gaussian" } } }, "emitter": { "type": "envmap", "filename": "C:/Users/mineg/mitsuba2/myscripts/render/dict/envmap.exr", "to_world": ScalarTransform4f.rotate([1,0,0], angle=90) }, "checker": { "type": "checkerboard", "color0": 0.4, "color1": 0.2, "to_uv": ScalarTransform4f.scale(2) }, "planemat": { "type": "diffuse", "reflectance": { "type": "ref", "id": "checker" } }, "floor": { "type": "rectangle", "to_world": ScalarTransform4f.scale(150), "floor_bsdf": { "type": "ref",