world_test.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. /*
  2. * DoRayMe - a quick and dirty Raytracer
  3. * World unit tests
  4. *
  5. * Created by Manoël Trapier
  6. * Copyright (c) 2020 986-Studio.
  7. *
  8. */
  9. #include <world.h>
  10. #include <light.h>
  11. #include <sphere.h>
  12. #include <material.h>
  13. #include <transformation.h>
  14. #include <worldbuilder.h>
  15. #include <testpattern.h>
  16. #include <math.h>
  17. #include <gtest/gtest.h>
  18. #include <plane.h>
  19. TEST(WorldTest, Creating_a_world)
  20. {
  21. World w;
  22. ASSERT_EQ(w.lightCount, 0);
  23. ASSERT_EQ(w.objectCount, 0);
  24. }
  25. TEST(WorldTest, The_default_world)
  26. {
  27. World w = DefaultWorld();
  28. Light l = Light(POINT_LIGHT, Point(-10, 10, -10), Colour(1, 1, 1));
  29. Sphere s1 = Sphere();
  30. Sphere s2 = Sphere();
  31. Material s1Mat = Material();
  32. s1Mat.colour = Colour(0.8, 1.0, 0.6);
  33. s1Mat.diffuse = 0.7;
  34. s1Mat.specular = 0.2;
  35. s1.setMaterial(s1Mat);
  36. s2.setTransform(scaling(0.5, 0.5,0.5));
  37. ASSERT_TRUE(w.lightIsIn(l));
  38. ASSERT_TRUE(w.objectIsIn(s1));
  39. ASSERT_TRUE(w.objectIsIn(s2));
  40. };
  41. TEST(WorldTest, Intersect_a_world_with_a_ray)
  42. {
  43. World w = DefaultWorld();
  44. Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
  45. Intersect xs = w.intersect(r);
  46. ASSERT_EQ(xs.count(), 4);
  47. ASSERT_EQ(xs[0].t, 4);
  48. ASSERT_EQ(xs[1].t, 4.5);
  49. ASSERT_EQ(xs[2].t, 5.5);
  50. ASSERT_EQ(xs[3].t, 6);
  51. }
  52. TEST(WorldTest, Shading_an_intersection)
  53. {
  54. World w = DefaultWorld();
  55. Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
  56. Shape *s = w.getObject(0);
  57. Intersection i = Intersection(4, s);
  58. Computation comps = i.prepareComputation(r);
  59. Tuple c = w.shadeHit(comps);
  60. /* Temporary lower the precision */
  61. set_equal_precision(0.00001);
  62. ASSERT_EQ(c, Colour(0.38066, 0.47583, 0.2855));
  63. set_equal_precision(FLT_EPSILON);
  64. }
  65. TEST(WorldTest, The_when_ray_miss)
  66. {
  67. World w = DefaultWorld();
  68. Ray r = Ray(Point(0, 0, -5), Vector(0, 1, 0));
  69. Tuple c = w.colourAt(r);
  70. ASSERT_EQ(c, Colour(0, 0, 0));
  71. }
  72. TEST(WorldTest, The_when_ray_hit)
  73. {
  74. World w = DefaultWorld();
  75. Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
  76. Tuple c = w.colourAt(r);
  77. /* Temporary lower the precision */
  78. set_equal_precision(0.00001);
  79. ASSERT_EQ(c, Colour(0.38066, 0.47583, 0.2855));
  80. set_equal_precision(FLT_EPSILON);
  81. }
  82. TEST(WorldTest, The_colour_with_an_intersection_behind_the_ray)
  83. {
  84. World w = DefaultWorld();
  85. Shape *outer = w.getObject(0);
  86. outer->material.ambient = 1;
  87. Shape *inner = w.getObject(1);
  88. inner->material.ambient = 1;
  89. Ray r = Ray(Point(0, 0, 0.75), Vector(0, 0, -1));
  90. Tuple c = w.colourAt(r);
  91. ASSERT_EQ(c, inner->material.colour);
  92. }
  93. TEST(WorldTest, There_is_no_shadow_when_nothing_is_collinear_with_point_and_light)
  94. {
  95. World w = DefaultWorld();
  96. Tuple p = Point(0, 10, 0);
  97. ASSERT_FALSE(w.isShadowed(p, w.getLight(0)->position));
  98. }
  99. TEST(WorldTest, The_shadow_when_an_object_is_between_the_point_and_the_light)
  100. {
  101. World w = DefaultWorld();
  102. Tuple p = Point(10, -10, 10);
  103. ASSERT_TRUE(w.isShadowed(p, w.getLight(0)->position));
  104. }
  105. TEST(WorldTest, There_is_no_shadow_whne_an_object_is_behing_the_light)
  106. {
  107. World w = DefaultWorld();
  108. Tuple p = Point(-20, 20, -20);
  109. ASSERT_FALSE(w.isShadowed(p, w.getLight(0)->position));
  110. }
  111. TEST(WorldTest, There_is_no_shadow_when_an_object_is_behing_the_point)
  112. {
  113. World w = DefaultWorld();
  114. Tuple p = Point(-2, 2, -2);
  115. ASSERT_FALSE(w.isShadowed(p, w.getLight(0)->position));
  116. }
  117. TEST(WorldTest, Shade_hit_is_given_an_intersection_in_shadow)
  118. {
  119. World w = World();
  120. Light l = Light(POINT_LIGHT, Point(0, 0, -10), Colour(1, 1, 1));
  121. w.addLight(&l);
  122. Sphere s1 = Sphere();
  123. w.addObject(&s1);
  124. Sphere s2 = Sphere();
  125. s2.setTransform(translation(0, 0, 10));
  126. w.addObject(&s2);
  127. Ray r = Ray(Point(0, 0, 5), Vector(0, 0, 1));
  128. Intersection i = Intersection(4, &s2);
  129. Computation comps = i.prepareComputation(r);
  130. Tuple c = w.shadeHit(comps);
  131. ASSERT_EQ(c, Colour(0.1, 0.1, 0.1));
  132. };
  133. TEST(WorldTest, The_reflected_colour_for_a_non_reflective_material)
  134. {
  135. World w = DefaultWorld();
  136. Ray r = Ray(Point(0, 0, 0), Vector(0, 0, 1));
  137. Shape *shape = w.getObject(1); /* The second object */
  138. shape->material.ambient = 1; /* We use this to get a predictable colour */
  139. Intersection i = Intersection(1, shape);
  140. Computation comps = i.prepareComputation(r);
  141. Colour colour = w.reflectColour(comps);
  142. ASSERT_EQ(colour, Colour(0, 0, 0));
  143. }
  144. TEST(WorldTest, The_reflected_colour_for_a_reflective_material)
  145. {
  146. World w = DefaultWorld();
  147. Plane shape = Plane();
  148. shape.material.reflective = 0.5;
  149. shape.setTransform(translation(0, -1, 0));
  150. w.addObject(&shape);
  151. Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2));
  152. Intersection i = Intersection(sqrt(2), &shape);
  153. Computation comps = i.prepareComputation(r);
  154. Colour colour = w.reflectColour(comps);
  155. /* Temporary lower the precision */
  156. set_equal_precision(0.00002);
  157. ASSERT_EQ(colour, Colour(0.19032, 0.2379, 0.14274));
  158. set_equal_precision(FLT_EPSILON);
  159. }
  160. TEST(WorldTest, Shade_hit_with_a_reflective_material)
  161. {
  162. World w = DefaultWorld();
  163. Plane shape = Plane();
  164. shape.material.reflective = 0.5;
  165. shape.setTransform(translation(0, -1, 0));
  166. w.addObject(&shape);
  167. Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2));
  168. Intersection i = Intersection(sqrt(2), &shape);
  169. Computation comps = i.prepareComputation(r);
  170. Tuple colour = w.shadeHit(comps);
  171. /* Temporary lower the precision */
  172. set_equal_precision(0.00005);
  173. ASSERT_EQ(colour, Colour(0.87677, 0.92436, 0.82918));
  174. set_equal_precision(FLT_EPSILON);
  175. }
  176. TEST(WorldTest, Colour_at_with_mutually_reflective_surfaces)
  177. {
  178. World w = World();
  179. Light l = Light(POINT_LIGHT, Point(0, 0, 0), Colour(1, 1, 1));
  180. w.addLight(&l);
  181. Plane lower = Plane();
  182. lower.material.reflective = 1;
  183. lower.setTransform(translation(0, -1, 0));
  184. Plane higher = Plane();
  185. higher.material.reflective = 1;
  186. higher.setTransform(translation(0, 1, 0));
  187. w.addObject(&lower);
  188. w.addObject(&higher);
  189. Ray r = Ray(Point(0, 0, 0), Vector(0, 1, 0));
  190. /* It should just exit, we don't care about the actual colour */
  191. w.colourAt(r);
  192. SUCCEED();
  193. }
  194. TEST(WorldTest, The_reflected_colour_at_the_maximum_recursion_depth)
  195. {
  196. World w = DefaultWorld();
  197. Plane shape = Plane();
  198. shape.material.reflective = 0.5;
  199. shape.setTransform(translation(0, -1, 0));
  200. w.addObject(&shape);
  201. Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2));
  202. Intersection i = Intersection(sqrt(2), &shape);
  203. Computation comps = i.prepareComputation(r);
  204. Tuple colour = w.reflectColour(comps, 0);
  205. /* Temporary lower the precision */
  206. ASSERT_EQ(colour, Colour(0, 0, 0));
  207. }
  208. TEST(WorldTest, The_refracted_colour_with_an_opaque_surface)
  209. {
  210. World w = DefaultWorld();
  211. Shape *shape = w.getObject(0);
  212. Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
  213. Intersect xs = Intersect();
  214. xs.add(Intersection(4, shape));
  215. xs.add(Intersection(6, shape));
  216. Computation comps = xs[0].prepareComputation(r, &xs);
  217. Colour c = w.refractedColour(comps, 5);
  218. ASSERT_EQ(c, Colour(0, 0, 0));
  219. }
  220. TEST(WorldTest, The_refracted_colour_at_the_maximum_recursive_depth)
  221. {
  222. World w = DefaultWorld();
  223. Shape *shape = w.getObject(0);
  224. shape->material.transparency = 1.0;
  225. shape->material.refractiveIndex = 1.5;
  226. Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
  227. Intersect xs = Intersect();
  228. xs.add(Intersection(4, shape));
  229. xs.add(Intersection(6, shape));
  230. Computation comps = xs[0].prepareComputation(r, &xs);
  231. Colour c = w.refractedColour(comps, 0);
  232. ASSERT_EQ(c, Colour(0, 0, 0));
  233. }
  234. TEST(WorldTest, The_refracted_colour_under_total_internal_reflection)
  235. {
  236. World w = DefaultWorld();
  237. Shape *shape = w.getObject(0);
  238. shape->material.transparency = 1.0;
  239. shape->material.refractiveIndex = 1.5;
  240. Ray r = Ray(Point(0, 0, sqrt(2)/2), Vector(0, 1, 0));
  241. Intersect xs = Intersect();
  242. xs.add(Intersection(-sqrt(2)/2, shape));
  243. xs.add(Intersection(sqrt(2)/2, shape));
  244. Computation comps = xs[1].prepareComputation(r, &xs);
  245. Colour c = w.refractedColour(comps, 5);
  246. ASSERT_EQ(c, Colour(0, 0, 0));
  247. }
  248. TEST(WorldTest, The_refracted_coloud_with_a_refracted_ray)
  249. {
  250. World w = DefaultWorld();
  251. Shape *A = w.getObject(0);
  252. A->material.ambient = 1.0;
  253. A->material.pattern = new TestPattern();
  254. Shape *B = w.getObject(1);
  255. B->material.transparency = 1.0;
  256. B->material.refractiveIndex = 1.5;
  257. Ray r = Ray(Point(0, 0, 0.1), Vector(0, 1, 0));
  258. Intersect xs = Intersect();
  259. xs.add(Intersection(-0.9899, A));
  260. xs.add(Intersection(-0.4899, B));
  261. xs.add(Intersection(0.4899, B));
  262. xs.add(Intersection(0.9899, A));
  263. Computation comps = xs[2].prepareComputation(r, &xs);
  264. Colour c = w.refractedColour(comps, 5);
  265. /* Temporary lower the precision */
  266. set_equal_precision(0.00005);
  267. ASSERT_EQ(c, Colour(0, 0.99888, 0.04725));
  268. set_equal_precision(FLT_EPSILON);
  269. }
  270. TEST(WorldTest, Shade_hit_with_a_transparent_material)
  271. {
  272. World w = DefaultWorld();
  273. Plane floor = Plane();
  274. floor.setTransform(translation(0, -1, 0));
  275. floor.material.transparency = 0.5;
  276. floor.material.refractiveIndex = 1.5;
  277. w.addObject(&floor);
  278. Sphere ball = Sphere();
  279. ball.material.colour = Colour(1, 0, 0);
  280. ball.material.ambient = 0.5;
  281. ball.setTransform(translation(0, -3.5, -0.5));
  282. w.addObject(&ball);
  283. Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2));
  284. Intersect xs = Intersect();
  285. xs.add(Intersection(sqrt(2), &floor));
  286. Computation comps = xs[0].prepareComputation(r, &xs);
  287. Tuple c = w.shadeHit(comps, 5);
  288. /* Temporary lower the precision */
  289. set_equal_precision(0.00001);
  290. ASSERT_EQ(c, Colour(0.93642, 0.68642, 0.68642));
  291. set_equal_precision(FLT_EPSILON);
  292. }
  293. TEST(WorldTest, Shade_hit_with_a_reflective_transparent_material)
  294. {
  295. World w = DefaultWorld();
  296. Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2));
  297. Plane floor = Plane();
  298. floor.setTransform(translation(0, -1, 0));
  299. floor.material.transparency = 0.5;
  300. floor.material.reflective = 0.5;
  301. floor.material.refractiveIndex = 1.5;
  302. w.addObject(&floor);
  303. Sphere ball = Sphere();
  304. ball.material.colour = Colour(1, 0, 0);
  305. ball.material.ambient = 0.5;
  306. ball.setTransform(translation(0, -3.5, -0.5));
  307. w.addObject(&ball);
  308. Intersect xs = Intersect();
  309. xs.add(Intersection(sqrt(2), &floor));
  310. Computation comps = xs[0].prepareComputation(r, &xs);
  311. Tuple c = w.shadeHit(comps, 5);
  312. /* Temporary lower the precision */
  313. set_equal_precision(0.00001);
  314. ASSERT_EQ(c, Colour(0.93391, 0.69643, 0.69243));
  315. set_equal_precision(FLT_EPSILON);
  316. }
  317. TEST(WorldTest, Is_shadow_test_for_occlusion_between_two_points)
  318. {
  319. World w = DefaultWorld();
  320. Tuple lightPosition = Point(-10, -10, -10);
  321. Point testList[] = {
  322. Point(-10, -10, 10),
  323. Point(10, 10, 10),
  324. Point(-20, -20, 20),
  325. Point(-5, -5, 5),
  326. };
  327. bool testResult[] = {
  328. false,
  329. true,
  330. false,
  331. false,
  332. };
  333. int testCount = sizeof(testList)/sizeof((testList)[0]);
  334. int i;
  335. for(i = 0; i < testCount; i++)
  336. {
  337. ASSERT_EQ(w.isShadowed(lightPosition, testList[i]), testResult[i]);
  338. }
  339. }