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)
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}")
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
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))
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))
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