예제 #1
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}")
예제 #2
0
    def test_translations(self):
        tr1 = translation(Vec(1.0, 2.0, 3.0))
        assert tr1.is_consistent()

        tr2 = translation(Vec(4.0, 6.0, 8.0))
        assert tr1.is_consistent()

        prod = tr1 * tr2
        assert prod.is_consistent()

        expected = translation(Vec(5.0, 8.0, 11.0))
        assert prod.is_close(expected)
예제 #3
0
    def testTransformation(self):
        sphere = Sphere(transformation=translation(Vec(10.0, 0.0, 0.0)))

        ray1 = Ray(origin=Point(10, 0, 2), dir=-VEC_Z)
        intersection1 = sphere.ray_intersection(ray1)
        assert intersection1
        assert HitRecord(
            world_point=Point(10.0, 0.0, 1.0),
            normal=Normal(0.0, 0.0, 1.0),
            surface_point=Vec2d(0.0, 0.0),
            t=1.0,
            ray=ray1,
            material=sphere.material,
        ).is_close(intersection1)

        ray2 = Ray(origin=Point(13, 0, 0), dir=-VEC_X)
        intersection2 = sphere.ray_intersection(ray2)
        assert intersection2
        assert HitRecord(
            world_point=Point(11.0, 0.0, 0.0),
            normal=Normal(1.0, 0.0, 0.0),
            surface_point=Vec2d(0.0, 0.5),
            t=2.0,
            ray=ray2,
            material=sphere.material,
        ).is_close(intersection2)

        # Check if the sphere failed to move by trying to hit the untransformed shape
        assert not sphere.ray_intersection(
            Ray(origin=Point(0, 0, 2), dir=-VEC_Z))

        # Check if the *inverse* transformation was wrongly applied
        assert not sphere.ray_intersection(
            Ray(origin=Point(-10, 0, 0), dir=-VEC_Z))
예제 #4
0
    def testFlatRenderer(self):
        sphere_color = Color(1.0, 2.0, 3.0)
        sphere = Sphere(transformation=translation(Vec(2, 0, 0)) *
                        scaling(Vec(0.2, 0.2, 0.2)),
                        material=Material(brdf=DiffuseBRDF(
                            pigment=UniformPigment(sphere_color))))
        image = HdrImage(width=3, height=3)
        camera = OrthogonalCamera()
        tracer = ImageTracer(image=image, camera=camera)
        world = World()
        world.add_shape(sphere)
        renderer = FlatRenderer(world=world)
        tracer.fire_all_rays(renderer)

        assert image.get_pixel(0, 0).is_close(BLACK)
        assert image.get_pixel(1, 0).is_close(BLACK)
        assert image.get_pixel(2, 0).is_close(BLACK)

        assert image.get_pixel(0, 1).is_close(BLACK)
        assert image.get_pixel(1, 1).is_close(sphere_color)
        assert image.get_pixel(2, 1).is_close(BLACK)

        assert image.get_pixel(0, 2).is_close(BLACK)
        assert image.get_pixel(1, 2).is_close(BLACK)
        assert image.get_pixel(2, 2).is_close(BLACK)
예제 #5
0
    def testRayIntersections(self):
        world = World()

        sphere1 = Sphere(transformation=translation(VEC_X * 2))
        sphere2 = Sphere(transformation=translation(VEC_X * 8))
        world.add_shape(sphere1)
        world.add_shape(sphere2)

        intersection1 = world.ray_intersection(
            Ray(origin=Point(0.0, 0.0, 0.0), dir=VEC_X))
        assert intersection1
        assert intersection1.world_point.is_close(Point(1.0, 0.0, 0.0))

        intersection2 = world.ray_intersection(
            Ray(origin=Point(10.0, 0.0, 0.0), dir=-VEC_X))

        assert intersection2
        assert intersection2.world_point.is_close(Point(9.0, 0.0, 0.0))
예제 #6
0
    def test_quick_ray_intersection(self):
        world = World()

        sphere1 = Sphere(transformation=translation(VEC_X * 2))
        sphere2 = Sphere(transformation=translation(VEC_X * 8))
        world.add_shape(sphere1)
        world.add_shape(sphere2)

        assert not world.is_point_visible(point=Point(10.0, 0.0, 0.0),
                                          observer_pos=Point(0.0, 0.0, 0.0))
        assert not world.is_point_visible(point=Point(5.0, 0.0, 0.0),
                                          observer_pos=Point(0.0, 0.0, 0.0))
        assert world.is_point_visible(point=Point(5.0, 0.0, 0.0),
                                      observer_pos=Point(4.0, 0.0, 0.0))
        assert world.is_point_visible(point=Point(0.5, 0.0, 0.0),
                                      observer_pos=Point(0.0, 0.0, 0.0))
        assert world.is_point_visible(point=Point(0.0, 10.0, 0.0),
                                      observer_pos=Point(0.0, 0.0, 0.0))
        assert world.is_point_visible(point=Point(0.0, 0.0, 10.0),
                                      observer_pos=Point(0.0, 0.0, 0.0))
예제 #7
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
예제 #8
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))
예제 #9
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))
예제 #10
0
 def test_transform(self):
     ray = Ray(origin=Point(1.0, 2.0, 3.0), dir=Vec(6.0, 5.0, 4.0))
     transformation = translation(Vec(10.0, 11.0, 12.0)) * rotation_x(90.0)
     transformed = ray.transform(transformation)
     assert transformed.origin.is_close(Point(11.0, 8.0, 14.0))
     assert transformed.dir.is_close(Vec(6.0, -4.0, 5.0))
예제 #11
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
예제 #12
0
파일: shapes.py 프로젝트: PMende/algorist
 def _default_matrix(self):
     '''Sets a default transformation matrix for the shape
     '''
     R1 = transformations.rotation(self.angle)
     T1 = transformations.translation(self.xy)
     return transformations.compose(R1, T1)