Exemplo n.º 1
0
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)
Exemplo n.º 2
0
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"]
Exemplo n.º 3
0
    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
Exemplo n.º 4
0
    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}
Exemplo n.º 5
0
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
Exemplo n.º 6
0
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)
Exemplo n.º 8
0
    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
Exemplo n.º 9
0
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,
    )
Exemplo n.º 10
0
    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
Exemplo n.º 11
0
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,
    )
Exemplo n.º 12
0
    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}
Exemplo n.º 13
0
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
Exemplo n.º 14
0
    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,
            }
        })
Exemplo n.º 15
0
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
            ]
        ]
    )
Exemplo n.º 16
0
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)
Exemplo n.º 17
0
    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
Exemplo n.º 18
0
                "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",