Exemple #1
0
    def test_rotations(self):
        assert rotation_x(0.1).is_consistent()
        assert rotation_y(0.1).is_consistent()
        assert rotation_z(0.1).is_consistent()

        assert (rotation_x(angle_deg=90) * VEC_Y).is_close(VEC_Z)
        assert (rotation_y(angle_deg=90) * VEC_Z).is_close(VEC_X)
        assert (rotation_z(angle_deg=90) * VEC_X).is_close(VEC_Y)
Exemple #2
0
def demo(width, height, angle_deg, orthogonal, pfm_output, png_output):
    image = HdrImage(width, height)

    # Create a world and populate it with a few shapes
    world = World()

    for x in [-0.5, 0.5]:
        for y in [-0.5, 0.5]:
            for z in [-0.5, 0.5]:
                world.add(
                    Sphere(transformation=translation(Vec(x, y, z)) *
                           scaling(Vec(0.1, 0.1, 0.1))))

    # Place two other balls in the bottom/left part of the cube, so
    # that we can check if there are issues with the orientation of
    # the image
    world.add(
        Sphere(transformation=translation(Vec(0.0, 0.0, -0.5)) *
               scaling(Vec(0.1, 0.1, 0.1))))
    world.add(
        Sphere(transformation=translation(Vec(0.0, 0.5, 0.0)) *
               scaling(Vec(0.1, 0.1, 0.1))))

    # Initialize a camera
    camera_tr = rotation_z(angle_deg=angle_deg) * translation(
        Vec(-1.0, 0.0, 0.0))
    if orthogonal:
        camera = OrthogonalCamera(aspect_ratio=width / height,
                                  transformation=camera_tr)
    else:
        camera = PerspectiveCamera(aspect_ratio=width / height,
                                   transformation=camera_tr)

    # Run the ray-tracer

    tracer = ImageTracer(image=image, camera=camera)

    def compute_color(ray: Ray) -> Color:
        if world.ray_intersection(ray):
            return WHITE
        else:
            return BLACK

    tracer.fire_all_rays(compute_color)

    # Save the HDR image
    with open(pfm_output, "wb") as outf:
        image.write_pfm(outf)
    print(f"HDR demo image written to {pfm_output}")

    # Apply tone-mapping to the image
    image.normalize_image(factor=1.0)
    image.clamp_image()

    # Save the LDR image
    with open(png_output, "wb") as outf:
        image.write_ldr_image(outf, "PNG")
    print(f"PNG demo image written to {png_output}")
Exemple #3
0
def parse_transformation(input_file, scene: Scene):
    result = Transformation()

    while True:
        transformation_kw = expect_keywords(input_file, [
            KeywordEnum.IDENTITY,
            KeywordEnum.TRANSLATION,
            KeywordEnum.ROTATION_X,
            KeywordEnum.ROTATION_Y,
            KeywordEnum.ROTATION_Z,
            KeywordEnum.SCALING,
        ])

        if transformation_kw == KeywordEnum.IDENTITY:
            pass  # Do nothing (this is a primitive form of optimization!)
        elif transformation_kw == KeywordEnum.TRANSLATION:
            expect_symbol(input_file, "(")
            result *= translation(parse_vector(input_file, scene))
            expect_symbol(input_file, ")")
        elif transformation_kw == KeywordEnum.ROTATION_X:
            expect_symbol(input_file, "(")
            result *= rotation_x(expect_number(input_file, scene))
            expect_symbol(input_file, ")")
        elif transformation_kw == KeywordEnum.ROTATION_Y:
            expect_symbol(input_file, "(")
            result *= rotation_y(expect_number(input_file, scene))
            expect_symbol(input_file, ")")
        elif transformation_kw == KeywordEnum.ROTATION_Z:
            expect_symbol(input_file, "(")
            result *= rotation_z(expect_number(input_file, scene))
            expect_symbol(input_file, ")")
        elif transformation_kw == KeywordEnum.SCALING:
            expect_symbol(input_file, "(")
            result *= scaling(parse_vector(input_file, scene))
            expect_symbol(input_file, ")")

        # We must peek the next token to check if there is another transformation that is being
        # chained or if the sequence ends. Thus, this is a LL(1) parser.
        next_kw = input_file.read_token()
        if (not isinstance(next_kw, SymbolToken)) or (next_kw.symbol != "*"):
            # Pretend you never read this token and put it back!
            input_file.unread_token(next_kw)
            break

    return result
Exemple #4
0
    def test_perspective_camera_transform(self):
        cam = PerspectiveCamera(transformation=translation(-VEC_Y * 2.0) *
                                rotation_z(pi / 2.0))

        ray = cam.fire_ray(0.5, 0.5)
        assert ray.at(1.0).is_close(Point(0.0, -2.0, 0.0))
Exemple #5
0
    def test_orthogonal_camera_transform(self):
        cam = OrthogonalCamera(transformation=translation(-VEC_Y * 2.0) *
                               rotation_z(angle_deg=90))

        ray = cam.fire_ray(0.5, 0.5)
        assert ray.at(1.0).is_close(Point(0.0, -2.0, 0.0))
Exemple #6
0
    def test_parser(self):
        stream = StringIO("""
        float clock(150)
    
        material sky_material(
            diffuse(uniform(<0, 0, 0>)),
            uniform(<0.7, 0.5, 1>)
        )
    
        # Here is a comment
    
        material ground_material(
            diffuse(checkered(<0.3, 0.5, 0.1>,
                              <0.1, 0.2, 0.5>, 4)),
            uniform(<0, 0, 0>)
        )
    
        material sphere_material(
            specular(uniform(<0.5, 0.5, 0.5>)),
            uniform(<0, 0, 0>)
        )
    
        plane (sky_material, translation([0, 0, 100]) * rotation_y(clock))
        plane (ground_material, identity)
    
        sphere(sphere_material, translation([0, 0, 1]))
    
        camera(perspective, rotation_z(30) * translation([-4, 0, 1]), 1.0, 2.0)
        """)

        scene = parse_scene(input_file=InputStream(stream))

        # Check that the float variables are ok

        assert len(scene.float_variables) == 1
        assert "clock" in scene.float_variables.keys()
        assert scene.float_variables["clock"] == 150.0

        # Check that the materials are ok

        assert len(scene.materials) == 3
        assert "sphere_material" in scene.materials
        assert "sky_material" in scene.materials
        assert "ground_material" in scene.materials

        sphere_material = scene.materials["sphere_material"]
        sky_material = scene.materials["sky_material"]
        ground_material = scene.materials["ground_material"]

        assert isinstance(sky_material.brdf, DiffuseBRDF)
        assert isinstance(sky_material.brdf.pigment, UniformPigment)
        assert sky_material.brdf.pigment.color.is_close(Color(0, 0, 0))

        assert isinstance(ground_material.brdf, DiffuseBRDF)
        assert isinstance(ground_material.brdf.pigment, CheckeredPigment)
        assert ground_material.brdf.pigment.color1.is_close(
            Color(0.3, 0.5, 0.1))
        assert ground_material.brdf.pigment.color2.is_close(
            Color(0.1, 0.2, 0.5))
        assert ground_material.brdf.pigment.num_of_steps == 4

        assert isinstance(sphere_material.brdf, SpecularBRDF)
        assert isinstance(sphere_material.brdf.pigment, UniformPigment)
        assert sphere_material.brdf.pigment.color.is_close(Color(
            0.5, 0.5, 0.5))

        assert isinstance(sky_material.emitted_radiance, UniformPigment)
        assert sky_material.emitted_radiance.color.is_close(
            Color(0.7, 0.5, 1.0))
        assert isinstance(ground_material.emitted_radiance, UniformPigment)
        assert ground_material.emitted_radiance.color.is_close(Color(0, 0, 0))
        assert isinstance(sphere_material.emitted_radiance, UniformPigment)
        assert sphere_material.emitted_radiance.color.is_close(Color(0, 0, 0))

        # Check that the shapes are ok

        assert len(scene.world.shapes) == 3
        assert isinstance(scene.world.shapes[0], Plane)
        assert scene.world.shapes[0].transformation.is_close(
            translation(Vec(0, 0, 100)) * rotation_y(150.0))
        assert isinstance(scene.world.shapes[1], Plane)
        assert scene.world.shapes[1].transformation.is_close(Transformation())
        assert isinstance(scene.world.shapes[2], Sphere)
        assert scene.world.shapes[2].transformation.is_close(
            translation(Vec(0, 0, 1)))

        # Check that the camera is ok

        assert isinstance(scene.camera, PerspectiveCamera)
        assert scene.camera.transformation.is_close(
            rotation_z(30) * translation(Vec(-4, 0, 1)))
        assert pytest.approx(1.0) == scene.camera.aspect_ratio
        assert pytest.approx(2.0) == scene.camera.screen_distance