From ea33f1b98521d4dff1dc341c097826d0d4c891c7 Mon Sep 17 00:00:00 2001 From: NADAL Jean-Baptiste Date: Thu, 22 Feb 2024 23:00:54 +0100 Subject: [PATCH] [FEAT] add world.color_at --- raytracing/src/intersections.cpp | 20 ++++---- raytracing/src/world.cpp | 25 ++++++++++ raytracing/src/world.h | 1 + tests/07_making_scene.cpp | 82 ++++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+), 8 deletions(-) diff --git a/raytracing/src/intersections.cpp b/raytracing/src/intersections.cpp index d07eb3b..f02e423 100644 --- a/raytracing/src/intersections.cpp +++ b/raytracing/src/intersections.cpp @@ -112,21 +112,25 @@ uint8_t Intersections::count(void) const Intersection Intersections::hit(void) { + Intersection the_current_int; if (m_data.size() == 0) { return {}; } - auto the_it = std::min_element(m_data.begin(), m_data.end(), - [](const Intersection &a, const Intersection &b) - { - // Use < comparison for strict positivity check - return a.distance_t() < b.distance_t() && a.distance_t() > 0; - }); - if (the_it == m_data.end() || the_it->distance_t() <= 0) + for (const Intersection &the_it : m_data) + { + if (the_it.distance_t() > 0 && + (the_current_int.is_nothing() || the_it.distance_t() < the_current_int.distance_t())) + { + the_current_int = the_it; + } + } + + if (the_current_int.is_nothing()) { return {}; } - return *the_it; + return the_current_int; } diff --git a/raytracing/src/world.cpp b/raytracing/src/world.cpp index 9cc9b3b..0411e33 100644 --- a/raytracing/src/world.cpp +++ b/raytracing/src/world.cpp @@ -163,6 +163,31 @@ Color World::shade_hit(const IntersectionData &an_intersection_data) /* ------------------------------------------------------------------------- */ +Color World::color_at(const Ray &a_ray) +{ + Color the_color = Color::Black(); + + Intersections the_intersections = intersect_world(a_ray); + if (the_intersections.count() == 0) + { + return the_color; + } + + Intersection the_intersec = the_intersections.hit(); + if (the_intersec.is_nothing()) + { + return the_color; + } + + IntersectionData the_comps = the_intersec.prepare_computations(a_ray); + + the_color = shade_hit(the_comps); + + return the_color; +} + +/* ------------------------------------------------------------------------- */ + World World::default_world(void) { World the_world; diff --git a/raytracing/src/world.h b/raytracing/src/world.h index 8f55e64..b273ffe 100644 --- a/raytracing/src/world.h +++ b/raytracing/src/world.h @@ -62,6 +62,7 @@ namespace Raytracer Intersections intersect_world(const Ray &a_ray); Color shade_hit(const IntersectionData &an_intersection_data); + Color color_at(const Ray &a_ray); static World default_world(void); diff --git a/tests/07_making_scene.cpp b/tests/07_making_scene.cpp index 52d1af1..82e2588 100644 --- a/tests/07_making_scene.cpp +++ b/tests/07_making_scene.cpp @@ -308,3 +308,85 @@ SCENARIO("Shading an intersection from the inside", "[features/world.feature]") } } } + +/* ------------------------------------------------------------------------- */ + +SCENARIO("The color when a ray misses", "[features/world.feature]") +{ + GIVEN("w <- default_world()") + { + World w = World::default_world(); + AND_GIVEN("r <- ray(point(0, 0, -5), vector(0, 1, 0))") + { + Ray r(Tuple::Point(0, 0, -5), Tuple::Vector(0, 1, 0)); + WHEN("c <- color_at(w, r)") + { + Color c = w.color_at(r); + THEN("c = color(0, 0, 0)") + { + REQUIRE(c == Color(0, 0, 0)); + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("The color when a ray hits", "[features/world.feature]") +{ + GIVEN("w <- default_world()") + { + World w = World::default_world(); + AND_GIVEN("r <- ray(point(0, 0, -5), vector(0, 0, 1))") + { + Ray r(Tuple::Point(0, 0, -5), Tuple::Vector(0, 0, 1)); + WHEN("c <- color_at(w, r)") + { + Color c = w.color_at(r); + THEN("c = color(0.38066, 0.47583, 0.2855)") + { + REQUIRE(c == Color(0.38066, 0.47583, 0.2855)); + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("The color with an intersection behind the ray", "[features/world.feature]") +{ + GIVEN("w <- default_world()") + { + World w = World::default_world(); + AND_GIVEN("outer <- the first object in w") + { + Shape *outer = w.objects(0); + AND_GIVEN("outer.material.ambient <- 1") + { + outer->material().set_ambient(1); + AND_GIVEN("inner <- the second object in w") + { + Shape *inner = w.objects(1); + AND_GIVEN("inner.material.ambient <- 1") + { + inner->material().set_ambient(1); + AND_GIVEN("r <- ray(point(0, 0, 0.75), vector(0, 0, -1))") + { + Ray r(Tuple::Point(0, 0, 0.75), Tuple::Vector(0, 0, -1)); + WHEN("c <- color_at(w, r)") + { + Color c = w.color_at(r); + THEN("c = inner.material.color") + { + REQUIRE(c == inner->material().color()); + } + } + } + } + } + } + } + } +}