intersect_test.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. /*
  2. * DoRayMe - a quick and dirty Raytracer
  3. * Intersect unit tests
  4. *
  5. * Created by Manoël Trapier
  6. * Copyright (c) 2020 986-Studio.
  7. *
  8. */
  9. #include <intersect.h>
  10. #include <intersection.h>
  11. #include <sphere.h>
  12. #include <plane.h>
  13. #include <triangle.h>
  14. #include <transformation.h>
  15. #include <gtest/gtest.h>
  16. TEST(IntersectTest, Creating_an_intersect_and_do_some_check)
  17. {
  18. Intersect i;
  19. ASSERT_EQ(i.count(), 0);
  20. i.add(Intersection(1.0, nullptr));
  21. i.add(Intersection(4.2, nullptr));
  22. ASSERT_EQ(i.count(), 2);
  23. ASSERT_EQ(i[0].t, 1.0);
  24. ASSERT_EQ(i[1].t, 4.2);
  25. }
  26. TEST(IntersectTest, An_intersection_encapsulate_t_and_object)
  27. {
  28. Sphere s = Sphere();
  29. Intersection i = Intersection(3.5, &s);
  30. ASSERT_EQ(i.t, 3.5);
  31. ASSERT_EQ(i.object, (Shape *)&s);
  32. }
  33. TEST(IntersectTest, Aggregating_intersections)
  34. {
  35. Sphere s = Sphere();
  36. Intersection i1 = Intersection(1, &s);
  37. Intersection i2 = Intersection(2, &s);
  38. Intersect xs = Intersect();
  39. xs.add(i1);
  40. xs.add(i2);
  41. ASSERT_EQ(xs.count(), 2);
  42. ASSERT_EQ(xs[0].t, 1);
  43. ASSERT_EQ(xs[1].t, 2);
  44. }
  45. TEST(IntersectTest, Intersect_sets_the_object_on_the_intersection)
  46. {
  47. Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
  48. Sphere s = Sphere();
  49. Intersect xs = s.intersect(r);
  50. ASSERT_EQ(xs.count(), 2);
  51. ASSERT_EQ(xs[0].object, (Shape *)&s);
  52. ASSERT_EQ(xs[1].object, (Shape *)&s);
  53. }
  54. TEST(IntersectTest, The_hit_when_all_intersection_have_positive_t)
  55. {
  56. Sphere s = Sphere();
  57. Intersect xs = Intersect();
  58. Intersection i1 = Intersection(1, &s);
  59. Intersection i2 = Intersection(2, &s);
  60. xs.add(i1);
  61. xs.add(i2);
  62. Intersection i = xs.hit();
  63. ASSERT_EQ(i, i1);
  64. }
  65. TEST(IntersectTest, The_hit_when_some_intersection_have_negative_t)
  66. {
  67. Sphere s = Sphere();
  68. Intersect xs = Intersect();
  69. Intersection i1 = Intersection(-1, &s);
  70. Intersection i2 = Intersection(2, &s);
  71. Intersection i3 = Intersection(12, &s);
  72. xs.add(i1);
  73. xs.add(i2);
  74. xs.add(i3);
  75. Intersection i = xs.hit();
  76. ASSERT_EQ(i, i2);
  77. }
  78. TEST(IntersectTest, The_hit_when_all_intersection_have_negative_t)
  79. {
  80. Sphere s = Sphere();
  81. Intersect xs = Intersect();
  82. Intersection i1 = Intersection(-2, &s);
  83. Intersection i2 = Intersection(-1, &s);
  84. xs.add(i1);
  85. xs.add(i2);
  86. Intersection i = xs.hit();
  87. ASSERT_TRUE(i.nothing());
  88. }
  89. TEST(IntersectTest, The_hit_is_always_the_lowest_nonnegative_intersection)
  90. {
  91. Sphere s = Sphere();
  92. Intersect xs = Intersect();
  93. Intersection i1 = Intersection(5, &s);
  94. Intersection i2 = Intersection(7, &s);
  95. Intersection i3 = Intersection(-3, &s);
  96. Intersection i4 = Intersection(2, &s);
  97. xs.add(i1);
  98. xs.add(i2);
  99. xs.add(i3);
  100. xs.add(i4);
  101. Intersection i = xs.hit();
  102. ASSERT_EQ(i, i4);
  103. }
  104. TEST(IntersectTest, Precomputing_the_state_of_an_intersection)
  105. {
  106. Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
  107. Sphere shape = Sphere();
  108. Intersection i = Intersection(4, &shape);
  109. Computation comps = i.prepareComputation(r);
  110. ASSERT_EQ(comps.t, i.t);
  111. ASSERT_EQ(comps.object, i.object);
  112. ASSERT_EQ(comps.hitPoint, Point(0, 0, -1));
  113. ASSERT_EQ(comps.eyeVector, Vector(0, 0, -1));
  114. ASSERT_EQ(comps.normalVector, Vector(0, 0, -1));
  115. }
  116. TEST(IntersectTest, The_hit_when_an_intersection_occurs_on_the_outside)
  117. {
  118. Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
  119. Sphere shape = Sphere();
  120. Intersection i = Intersection(4, &shape);
  121. Computation comps = i.prepareComputation(r);
  122. ASSERT_EQ(comps.inside, false);
  123. }
  124. TEST(IntersectTest, The_hit_when_an_intersection_occurs_on_the_inside)
  125. {
  126. Ray r = Ray(Point(0, 0, 0), Vector(0, 0, 1));
  127. Sphere shape = Sphere();
  128. Intersection i = Intersection(1, &shape);
  129. Computation comps = i.prepareComputation(r);
  130. ASSERT_EQ(comps.hitPoint, Point(0, 0, 1));
  131. ASSERT_EQ(comps.eyeVector, Vector(0, 0, -1));
  132. ASSERT_EQ(comps.inside, true);
  133. /* Normal vector would have been (0, 0, 1); but is inverted ! */
  134. ASSERT_EQ(comps.normalVector, Vector(0, 0, -1));
  135. }
  136. TEST(IntersectTest, The_hit_should_offset_the_point)
  137. {
  138. Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
  139. Sphere shape = Sphere();
  140. shape.setTransform(translation(0, 0, 1));
  141. Intersection i = Intersection(5, &shape);
  142. Computation comps = i.prepareComputation(r);
  143. /* Normal vector would have been (0, 0, 1); but is inverted ! */
  144. ASSERT_LT(comps.overHitPoint.z, -getEpsilon() / 2);
  145. ASSERT_GT(comps.hitPoint.z, comps.overHitPoint.z);
  146. }
  147. TEST(IntersectTest, Precomputing_the_reflection_vector)
  148. {
  149. Plane s = Plane();
  150. Ray r = Ray(Point(0, 1, -1), Vector(0, -sqrt(2) / 2, sqrt(2) / 2));
  151. Intersection i = Intersection(sqrt(2), &s);
  152. Computation comps = i.prepareComputation(r);
  153. ASSERT_EQ(comps.reflectVector, Vector(0, sqrt(2) / 2, sqrt(2) / 2));
  154. }
  155. TEST(IntersectTest, Finding_n1_and_n2_at_various_intersections)
  156. {
  157. int i;
  158. double n1_res[6] = { 1.0, 1.5, 2.0, 2.5, 2.5, 1.5 };
  159. double n2_res[6] = { 1.5, 2.0, 2.5, 2.5, 1.5, 1.0 };
  160. GlassSphere A = GlassSphere();
  161. A.setTransform(scaling(2, 2, 2));
  162. A.material.refractiveIndex = 1.5;
  163. GlassSphere B = GlassSphere();
  164. B.setTransform(translation(0, 0, -0.25));
  165. B.material.refractiveIndex = 2.0;
  166. GlassSphere C = GlassSphere();
  167. C.setTransform(translation(0, 0, 0.25));
  168. C.material.refractiveIndex = 2.5;
  169. Ray r = Ray(Point(0, 0, -4), Vector(0, 0, 1));
  170. Intersect xs = Intersect();
  171. xs.add(Intersection(2.0, &A));
  172. xs.add(Intersection(2.75, &B));
  173. xs.add(Intersection(3.25, &C));
  174. xs.add(Intersection(4.75, &B));
  175. xs.add(Intersection(5.25, &C));
  176. xs.add(Intersection(6, &A));
  177. for(i = 0; i < xs.count(); i++)
  178. {
  179. Intersection inter = xs[i];
  180. Computation comps = inter.prepareComputation(r, &xs);
  181. ASSERT_EQ(comps.n1, n1_res[i]);
  182. ASSERT_EQ(comps.n2, n2_res[i]);
  183. }
  184. }
  185. TEST(IntersectTest, The_under_point_is_offset_below_the_surface)
  186. {
  187. Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
  188. GlassSphere shape = GlassSphere();
  189. shape.setTransform(translation(0, 0, 1));
  190. Intersection i = Intersection(5, &shape);
  191. Intersect xs = Intersect();
  192. xs.add(i);
  193. Computation comps = i.prepareComputation(r, &xs);
  194. ASSERT_TRUE(double_equal(comps.underHitPoint.z, getEpsilon() / 2));
  195. ASSERT_LT(comps.hitPoint.z, comps.underHitPoint.z);
  196. }
  197. TEST(IntersectTest, The_Schlick_approximation_under_total_internal_reflection)
  198. {
  199. GlassSphere shape = GlassSphere();
  200. Ray r = Ray(Point(0, 0, sqrt(2)/2), Vector(0, 1, 0));
  201. Intersect xs = Intersect();
  202. xs.add(Intersection(-sqrt(2)/2, &shape));
  203. xs.add(Intersection(sqrt(2)/2, &shape));
  204. Computation comps = xs[1].prepareComputation(r, &xs);
  205. double reflectance = comps.schlick();
  206. ASSERT_EQ(reflectance, 1.0);
  207. }
  208. TEST(IntersectTest, The_Schlick_approximation_with_a_perpendicular_viewing_angle)
  209. {
  210. GlassSphere shape = GlassSphere();
  211. Ray r = Ray(Point(0, 0, 0), Vector(0, 1, 0));
  212. Intersect xs = Intersect();
  213. xs.add(Intersection(-1, &shape));
  214. xs.add(Intersection(1, &shape));
  215. Computation comps = xs[1].prepareComputation(r, &xs);
  216. double reflectance = comps.schlick();
  217. ASSERT_TRUE(double_equal(reflectance, 0.04));
  218. }
  219. TEST(IntersectTest, The_Schlick_approximation_with_small_angle_and_n2_gt_n1)
  220. {
  221. GlassSphere shape = GlassSphere();
  222. Ray r = Ray(Point(0, 0.99, -2), Vector(0, 0, 1));
  223. Intersect xs = Intersect();
  224. xs.add(Intersection(1.8589, &shape));
  225. Computation comps = xs[0].prepareComputation(r, &xs);
  226. double reflectance = comps.schlick();
  227. /* Temporary lower the precision */
  228. set_equal_precision(0.00001);
  229. ASSERT_TRUE(double_equal(reflectance, 0.48873));
  230. set_equal_precision(FLT_EPSILON);
  231. }
  232. TEST(IntersectTest, An_intersection_can_encapsulage_u_and_v)
  233. {
  234. Triangle s = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0));
  235. Intersection i = Intersection(3.5, &s, 0.2, 0.4);
  236. ASSERT_EQ(i.u, 0.2);
  237. ASSERT_EQ(i.v, 0.4);
  238. }