/* * DoRayMe - a quick and dirty Raytracer * World unit tests * * Created by Manoƫl Trapier * Copyright (c) 2020 986-Studio. * */ #include #include #include #include #include #include #include #include #include #include TEST(WorldTest, Creating_a_world) { World w; ASSERT_EQ(w.getLightCount(), 0); ASSERT_EQ(w.getObjectCount(), 0); } TEST(WorldTest, The_default_world) { World w = DefaultWorld(); Light l = Light(POINT_LIGHT, Point(-10, 10, -10), Colour(1, 1, 1)); Sphere s1 = Sphere(); Sphere s2 = Sphere(); Material s1Mat = Material(); s1Mat.colour = Colour(0.8, 1.0, 0.6); s1Mat.diffuse = 0.7; s1Mat.specular = 0.2; s1.setMaterial(s1Mat); s2.setTransform(scaling(0.5, 0.5,0.5)); Shape *obj0 = w.getObject(0); Shape *obj1 = w.getObject(1); ASSERT_TRUE(w.lightIsIn(l)); ASSERT_EQ(*obj0, s1); ASSERT_EQ(*obj1, s2); } TEST(WorldTest, Intersect_a_world_with_a_ray) { World w = DefaultWorld(); Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1)); Intersect xs; w.intersect(r, xs); ASSERT_EQ(xs.count(), 4); ASSERT_EQ(xs[0].t, 4); ASSERT_EQ(xs[1].t, 4.5); ASSERT_EQ(xs[2].t, 5.5); ASSERT_EQ(xs[3].t, 6); } TEST(WorldTest, Shading_an_intersection) { World w = DefaultWorld(); Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1)); Shape *s = w.getObject(0); Intersection i = Intersection(4, s); Computation comps = i.prepareComputation(r); Tuple c = w.shadeHit(comps); /* Temporary lower the precision */ set_equal_precision(0.00001); ASSERT_EQ(c, Colour(0.38066, 0.47583, 0.2855)); set_equal_precision(FLT_EPSILON); } TEST(WorldTest, The_when_ray_miss) { World w = DefaultWorld(); Ray r = Ray(Point(0, 0, -5), Vector(0, 1, 0)); Tuple c = w.colourAt(r); ASSERT_EQ(c, Colour(0, 0, 0)); } TEST(WorldTest, The_when_ray_hit) { World w = DefaultWorld(); Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1)); Tuple c = w.colourAt(r); /* Temporary lower the precision */ set_equal_precision(0.00001); ASSERT_EQ(c, Colour(0.38066, 0.47583, 0.2855)); set_equal_precision(FLT_EPSILON); } TEST(WorldTest, The_colour_with_an_intersection_behind_the_ray) { World w = DefaultWorld(); Shape *outer = w.getObject(0); outer->material.ambient = 1; Shape *inner = w.getObject(1); inner->material.ambient = 1; Ray r = Ray(Point(0, 0, 0.75), Vector(0, 0, -1)); Tuple c = w.colourAt(r); ASSERT_EQ(c, inner->material.colour); } TEST(WorldTest, There_is_no_shadow_when_nothing_is_collinear_with_point_and_light) { World w = DefaultWorld(); Tuple p = Point(0, 10, 0); ASSERT_FALSE(w.isShadowed(p, w.getLight(0)->position)); } TEST(WorldTest, The_shadow_when_an_object_is_between_the_point_and_the_light) { World w = DefaultWorld(); Tuple p = Point(10, -10, 10); ASSERT_TRUE(w.isShadowed(p, w.getLight(0)->position)); } TEST(WorldTest, There_is_no_shadow_whne_an_object_is_behing_the_light) { World w = DefaultWorld(); Tuple p = Point(-20, 20, -20); ASSERT_FALSE(w.isShadowed(p, w.getLight(0)->position)); } TEST(WorldTest, There_is_no_shadow_when_an_object_is_behing_the_point) { World w = DefaultWorld(); Tuple p = Point(-2, 2, -2); ASSERT_FALSE(w.isShadowed(p, w.getLight(0)->position)); } TEST(WorldTest, Shade_hit_is_given_an_intersection_in_shadow) { World w = World(); Light l = Light(POINT_LIGHT, Point(0, 0, -10), Colour(1, 1, 1)); w.addLight(&l); Sphere s1 = Sphere(); w.addObject(&s1); Sphere s2 = Sphere(); s2.setTransform(translation(0, 0, 10)); w.addObject(&s2); Ray r = Ray(Point(0, 0, 5), Vector(0, 0, 1)); Intersection i = Intersection(4, &s2); Computation comps = i.prepareComputation(r); Tuple c = w.shadeHit(comps); ASSERT_EQ(c, Colour(0.1, 0.1, 0.1)); } TEST(WorldTest, The_reflected_colour_for_a_non_reflective_material) { World w = DefaultWorld(); Ray r = Ray(Point(0, 0, 0), Vector(0, 0, 1)); Shape *shape = w.getObject(1); /* The second object */ shape->material.ambient = 1; /* We use this to get a predictable colour */ Intersection i = Intersection(1, shape); Computation comps = i.prepareComputation(r); Colour colour = w.reflectColour(comps); ASSERT_EQ(colour, Colour(0, 0, 0)); } TEST(WorldTest, The_reflected_colour_for_a_reflective_material) { World w = DefaultWorld(); Plane shape = Plane(); shape.material.reflective = 0.5; shape.setTransform(translation(0, -1, 0)); w.addObject(&shape); Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2)); Intersection i = Intersection(sqrt(2), &shape); Computation comps = i.prepareComputation(r); Colour colour = w.reflectColour(comps); /* Temporary lower the precision */ set_equal_precision(0.00002); ASSERT_EQ(colour, Colour(0.19032, 0.2379, 0.14274)); set_equal_precision(FLT_EPSILON); } TEST(WorldTest, Shade_hit_with_a_reflective_material) { World w = DefaultWorld(); Plane shape = Plane(); shape.material.reflective = 0.5; shape.setTransform(translation(0, -1, 0)); w.addObject(&shape); Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2)); Intersection i = Intersection(sqrt(2), &shape); Computation comps = i.prepareComputation(r); Tuple colour = w.shadeHit(comps); /* Temporary lower the precision */ set_equal_precision(0.00005); ASSERT_EQ(colour, Colour(0.87677, 0.92436, 0.82918)); set_equal_precision(FLT_EPSILON); } TEST(WorldTest, Colour_at_with_mutually_reflective_surfaces) { World w = World(); Light l = Light(POINT_LIGHT, Point(0, 0, 0), Colour(1, 1, 1)); w.addLight(&l); Plane lower = Plane(); lower.material.reflective = 1; lower.setTransform(translation(0, -1, 0)); Plane higher = Plane(); higher.material.reflective = 1; higher.setTransform(translation(0, 1, 0)); w.addObject(&lower); w.addObject(&higher); Ray r = Ray(Point(0, 0, 0), Vector(0, 1, 0)); /* It should just exit, we don't care about the actual colour */ w.colourAt(r); SUCCEED(); } TEST(WorldTest, The_reflected_colour_at_the_maximum_recursion_depth) { World w = DefaultWorld(); Plane shape = Plane(); shape.material.reflective = 0.5; shape.setTransform(translation(0, -1, 0)); w.addObject(&shape); Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2)); Intersection i = Intersection(sqrt(2), &shape); Computation comps = i.prepareComputation(r); Tuple colour = w.reflectColour(comps, 0); /* Temporary lower the precision */ ASSERT_EQ(colour, Colour(0, 0, 0)); } TEST(WorldTest, The_refracted_colour_with_an_opaque_surface) { World w = DefaultWorld(); Shape *shape = w.getObject(0); Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1)); Intersect xs = Intersect(); xs.add(Intersection(4, shape)); xs.add(Intersection(6, shape)); Computation comps = xs[0].prepareComputation(r, &xs); Colour c = w.refractedColour(comps, 5); ASSERT_EQ(c, Colour(0, 0, 0)); } TEST(WorldTest, The_refracted_colour_at_the_maximum_recursive_depth) { World w = DefaultWorld(); Shape *shape = w.getObject(0); shape->material.transparency = 1.0; shape->material.refractiveIndex = 1.5; Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1)); Intersect xs = Intersect(); xs.add(Intersection(4, shape)); xs.add(Intersection(6, shape)); Computation comps = xs[0].prepareComputation(r, &xs); Colour c = w.refractedColour(comps, 0); ASSERT_EQ(c, Colour(0, 0, 0)); } TEST(WorldTest, The_refracted_colour_under_total_internal_reflection) { World w = DefaultWorld(); Shape *shape = w.getObject(0); shape->material.transparency = 1.0; shape->material.refractiveIndex = 1.5; Ray r = Ray(Point(0, 0, sqrt(2)/2), Vector(0, 1, 0)); Intersect xs = Intersect(); xs.add(Intersection(-sqrt(2)/2, shape)); xs.add(Intersection(sqrt(2)/2, shape)); Computation comps = xs[1].prepareComputation(r, &xs); Colour c = w.refractedColour(comps, 5); ASSERT_EQ(c, Colour(0, 0, 0)); } TEST(WorldTest, The_refracted_coloud_with_a_refracted_ray) { World w = DefaultWorld(); Shape *A = w.getObject(0); A->material.ambient = 1.0; A->material.pattern = new TestPattern(); Shape *B = w.getObject(1); B->material.transparency = 1.0; B->material.refractiveIndex = 1.5; Ray r = Ray(Point(0, 0, 0.1), Vector(0, 1, 0)); Intersect xs = Intersect(); xs.add(Intersection(-0.9899, A)); xs.add(Intersection(-0.4899, B)); xs.add(Intersection(0.4899, B)); xs.add(Intersection(0.9899, A)); Computation comps = xs[2].prepareComputation(r, &xs); Colour c = w.refractedColour(comps, 5); /* Temporary lower the precision */ set_equal_precision(0.00005); ASSERT_EQ(c, Colour(0, 0.99888, 0.04725)); set_equal_precision(FLT_EPSILON); } TEST(WorldTest, Shade_hit_with_a_transparent_material) { World w = DefaultWorld(); Plane floor = Plane(); floor.setTransform(translation(0, -1, 0)); floor.material.transparency = 0.5; floor.material.refractiveIndex = 1.5; w.addObject(&floor); Sphere ball = Sphere(); ball.material.colour = Colour(1, 0, 0); ball.material.ambient = 0.5; ball.setTransform(translation(0, -3.5, -0.5)); w.addObject(&ball); Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2)); Intersect xs = Intersect(); xs.add(Intersection(sqrt(2), &floor)); Computation comps = xs[0].prepareComputation(r, &xs); Tuple c = w.shadeHit(comps, 5); /* Temporary lower the precision */ set_equal_precision(0.00001); ASSERT_EQ(c, Colour(0.93642, 0.68642, 0.68642)); set_equal_precision(FLT_EPSILON); } TEST(WorldTest, Shade_hit_with_a_reflective_transparent_material) { World w = DefaultWorld(); Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2)); Plane floor = Plane(); floor.setTransform(translation(0, -1, 0)); floor.material.transparency = 0.5; floor.material.reflective = 0.5; floor.material.refractiveIndex = 1.5; w.addObject(&floor); Sphere ball = Sphere(); ball.material.colour = Colour(1, 0, 0); ball.material.ambient = 0.5; ball.setTransform(translation(0, -3.5, -0.5)); w.addObject(&ball); Intersect xs = Intersect(); xs.add(Intersection(sqrt(2), &floor)); Computation comps = xs[0].prepareComputation(r, &xs); Tuple c = w.shadeHit(comps, 5); /* Temporary lower the precision */ set_equal_precision(0.00001); ASSERT_EQ(c, Colour(0.93391, 0.69643, 0.69243)); set_equal_precision(FLT_EPSILON); } TEST(WorldTest, Is_shadow_test_for_occlusion_between_two_points) { World w = DefaultWorld(); Tuple lightPosition = Point(-10, -10, -10); Point testList[] = { Point(-10, -10, 10), Point(10, 10, 10), Point(-20, -20, 20), Point(-5, -5, 5), }; bool testResult[] = { false, true, false, false, }; int testCount = sizeof(testList)/sizeof((testList)[0]); int i; for(i = 0; i < testCount; i++) { ASSERT_EQ(w.isShadowed(lightPosition, testList[i]), testResult[i]); } }