world_test.cpp 11 KB

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