/*! * 08_shadows.cpp * * Copyright (c) 2015-2024, NADAL Jean-Baptiste. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA * * @Author: NADAL Jean-Baptiste * @Date: 26/02/2024 * */ /*---------------------------------------------------------------------------*/ #include #include "raytracing.h" using namespace Raytracer; /* ------------------------------------------------------------------------- */ SCENARIO("Lightning with the surface in shadow", "[features/materials.feature]") { Tuple position = Tuple::Point(0, 0, 0); Material m; GIVEN("eyev <- vector(0, 0, -1)") { Tuple eyev = Tuple::Vector(0, 0, -1); AND_GIVEN("normalv <- vector(0, 0, -1)") { Tuple normalv = Tuple::Vector(0, 0, -1); AND_GIVEN("light <- point_light(point(0, 0, -10), color(1, 1, 1))") { PointLight light = PointLight(Tuple::Point(0, 0, -10), Color(1, 1, 1)); AND_GIVEN("in_shadow <- true") { bool in_shadow = true; WHEN("result <- lighting(m, light, position, eyev, normalv, in_shadow)") { Color result = m.lighting(light, position, eyev, normalv, in_shadow); THEN("result = color(0.1, 0.1, 0.1)") { REQUIRE(result == Color(0.1, 0.1, 0.1)); } } } } } } } /* ------------------------------------------------------------------------- */ SCENARIO("There is no shadow when noting is collinear with p&l", "[features/world.feature]") { GIVEN("w <- default_world()") { World w = World::default_world(); AND_GIVEN("p <- point(0, 10, 0)") { Tuple p = Tuple::Point(0, 10, 0); THEN("is_shadowed(w, p) is false") { REQUIRE(w.is_shadowed(p) == false); } } } } /* ------------------------------------------------------------------------- */ SCENARIO("There shadow when an object is between the p&l", "[features/world.feature]") { GIVEN("w <- default_world()") { World w = World::default_world(); AND_GIVEN("p <- point(10, -10, 10)") { Tuple p = Tuple::Point(10, -10, 10); THEN("is_shadowed(w, p) is true") { REQUIRE(w.is_shadowed(p) == true); } } } } /* ------------------------------------------------------------------------- */ SCENARIO("There is no shadow when an object is behind the p&l", "[features/world.feature]") { GIVEN("w <- default_world()") { World w = World::default_world(); AND_GIVEN("p <- point(-20, 20, -20)") { Tuple p = Tuple::Point(-20, 20, -20); THEN("is_shadowed(w, p) is false") { REQUIRE(w.is_shadowed(p) == false); } } } } /* ------------------------------------------------------------------------- */ SCENARIO("There is no shadow when an object is behind the point", "[features/world.feature]") { GIVEN("w <- default_world()") { World w = World::default_world(); AND_GIVEN("p <- point(-2, 2, -2)") { Tuple p = Tuple::Point(-2, 2, -2); THEN("is_shadowed(w, p) is false") { REQUIRE(w.is_shadowed(p) == false); } } } } /* ------------------------------------------------------------------------- */ SCENARIO("shade_hit() is given an intersection in the shadow", "[features/world.feature]") { GIVEN("w <- world()") { World w; AND_GIVEN("w.light <- point_light(point(0, 0, -10), color(1, 1, 1))") { w.set_light(PointLight(Tuple::Point(0, 0, -10), Color(1, 1, 1))); AND_GIVEN("s1 <- sphere()") { Sphere *s1 = new Sphere; AND_GIVEN("s1 is added to w") { w.add_object(s1); AND_GIVEN("s2 <- sphere() with transform(translation(0, 0, 10))") { Sphere *s2 = new Sphere; s2->set_transform(Matrix::translation(0, 0, 10)); AND_GIVEN("s2 is added to w") { w.add_object(s2); AND_GIVEN("r <- ray(point(0, 0, 5), vector(0, 0, 1))") { Ray r(Tuple::Point(0, 0, 5), Tuple::Vector(0, 0, 1)); AND_GIVEN("i <- intersection(4, s2)") { Intersection i(4, *s2); WHEN("comps <- prepare_computatons(i,r)") { IntersectionData comps = i.prepare_computations(r); AND_WHEN("c <- shade_hit(w, comps)") { Color c = w.shade_hit(comps); THEN("c = color (0.1, 0.1, 0.1)") { REQUIRE(c == Color(0.1, 0.1, 0.1)); } } } } } } } } } } } } /* ------------------------------------------------------------------------- */ SCENARIO("The hit should offset the point", "[features/intersections.feature]") { GIVEN("r <- ray(point(0, 0, -5), vector(0, 0, 1))") { Ray r(Tuple::Point(0, 0, -5), Tuple::Vector(0, 0, 1)); AND_GIVEN("shape <- sphere()") { Sphere shape; shape.set_transform(Matrix::translation(0, 0, 1)); AND_GIVEN("i <- intersection(5, shape)") { Intersection i(5, shape); WHEN("comps <- prepare_computatons(i,r)") { IntersectionData comps = i.prepare_computations(r); THEN("comps.over_point.z < -EPSILON/2") { REQUIRE(comps.over_point().z() < -kEpsilon / 2); } AND_THEN("comps.point.z < comps.over_point.z") { REQUIRE(comps.point().z() > comps.over_point().z()); } } } } } }