diff --git a/.vscode/settings.json b/.vscode/settings.json index 14b2b14..387e01d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -107,6 +107,7 @@ "/home/jbnadal/sources/jb/raytracer_challenge/build/apps/CMakeFiles/chapter_10.dir", "/home/jbnadal/sources/jb/raytracer_challenge/build/apps/CMakeFiles/chapter_11_p1.dir", "/home/jbnadal/sources/jb/raytracer_challenge/build/apps/CMakeFiles/chapter_11_p2.dir", + "/home/jbnadal/sources/jb/raytracer_challenge/build/apps/CMakeFiles/chapter_12.dir", "/home/jbnadal/sources/jb/raytracer_challenge/build/tests/CMakeFiles/raytracing_test.dir" ] } \ No newline at end of file diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index b81f54e..09fea45 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -27,3 +27,6 @@ target_link_libraries(chapter_11_p1 PRIVATE raytracing gcov OpenMP::OpenMP_CXX) add_executable(chapter_11_p2 chapter_11_p2.cpp) target_link_libraries(chapter_11_p2 PRIVATE raytracing OpenMP::OpenMP_CXX) + +add_executable(chapter_12 chapter_12.cpp) +target_link_libraries(chapter_12 PRIVATE raytracing OpenMP::OpenMP_CXX) diff --git a/apps/chapter_12.cpp b/apps/chapter_12.cpp new file mode 100644 index 0000000..f3b3201 --- /dev/null +++ b/apps/chapter_12.cpp @@ -0,0 +1,273 @@ +/*! + * chapter_12.cpp + * + * Copyright (c) 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: 14/03/2024 + * + */ + +// This is an independent project of an individual developer. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com + +// Coordinate came from: +// https://github.com/sraaphorst/raytracer-kotlin/blob/main/src/main/kotlin/apps/ch12_world.kt + +#include +#include + +#include + +/* ------------------------------------------------------------------------- */ + +using namespace Raytracer; +using namespace std; + +/* ------------------------------------------------------------------------- */ + +int main(void) +{ + World the_world; + Camera the_camera; + Canvas the_canvas; + + Cube *the_floor_ceiling, *the_walls, *the_table_top, *the_leg1, *the_leg2, *the_leg3, *the_leg4; + Cube *the_glass_cube, *the_little_cube1, *the_little_cube2, *the_little_cube3, *the_little_cube4, *the_little_cube5; + Cube *the_frame1, *the_frame2, *the_frame3, *the_frame, *the_mirror; + + chrono::time_point the_start, the_end; + + printf("Chapter 12 example.\n"); + + // Floor Ceiling + the_floor_ceiling = new Cube(); + the_floor_ceiling->set_transform(Matrix::scaling(20, 7, 20) * + Matrix::translation(0, 1, 0)); + Material &the_floor_material = the_floor_ceiling->material(); + the_floor_material.set_pattern(new CheckersPattern(Color::Black(), Color(0.25, 0.25, 0.25))); + the_floor_material.pattern()->set_transform(Matrix::scaling(0.07, 0.07, 0.07)); + the_floor_material.set_ambient(0.25); + the_floor_material.set_diffuse(0.7); + the_floor_material.set_specular(0.9); + the_floor_material.set_shininess(300); + the_floor_material.set_reflective(0.1); + the_world.add_object(the_floor_ceiling); + + // Walls + the_walls = new Cube(); + the_walls->set_transform(Matrix::scaling(10, 10, 10)); + Material &the_wall_material = the_walls->material(); + the_wall_material.set_pattern(new CheckersPattern(Color(0.4863, 0.3765, 0.2941), Color(0.3725, 0.2902, 0.2275))); + the_wall_material.pattern()->set_transform(Matrix::scaling(0.05, 20, 0.05)); + the_wall_material.set_ambient(0.1); + the_wall_material.set_diffuse(0.7); + the_wall_material.set_specular(0.9); + the_wall_material.set_shininess(300); + the_wall_material.set_reflective(0.05); + the_world.add_object(the_walls); + + // Table Color + Color the_table_color = Color(0.5529, 0.4235, 0.3255); + + // Table Top + the_table_top = new Cube(); + the_table_top->set_transform(Matrix::translation(0, 3.1, 0) * + Matrix::scaling(3, 0.1, 2)); + Material &the_table_top_material = the_table_top->material(); + the_table_top_material.set_pattern(new StripePattern(the_table_color, Color(0.6588, 0.5098, 0.4000))); + the_table_top_material.pattern()->set_transform(Matrix::scaling(0.05, 0.05, 0.05) * + Matrix::rotation_y(0.1)); + the_table_top_material.set_ambient(0.1); + the_table_top_material.set_diffuse(0.7); + the_table_top_material.set_specular(0.9); + the_table_top_material.set_shininess(300); + the_table_top_material.set_reflective(0.2); + the_world.add_object(the_table_top); + + // Leg Material + Material the_leg_material; + the_leg_material.set_color(the_table_color); + the_leg_material.set_ambient(0.2); + the_leg_material.set_diffuse(0.7); + + // Leg1 + the_leg1 = new Cube(); + the_leg1->set_transform(Matrix::translation(2.7, 1.5, -1.7) * + Matrix::scaling(0.1, 1.5, 0.1)); + the_leg1->set_material(the_leg_material); + the_world.add_object(the_leg1); + + // Leg2 + the_leg2 = new Cube(); + the_leg2->set_transform(Matrix::translation(2.7, 1.5, 1.7) * + Matrix::scaling(0.1, 1.5, 0.1)); + the_leg2->set_material(the_leg_material); + the_world.add_object(the_leg2); + + // Leg3 + the_leg3 = new Cube(); + the_leg3->set_transform(Matrix::translation(-2.7, 1.5, -1.7) * + Matrix::scaling(0.1, 1.5, 0.1)); + the_leg3->set_material(the_leg_material); + the_world.add_object(the_leg3); + + // Leg4 + the_leg4 = new Cube(); + the_leg4->set_transform(Matrix::translation(-2.7, 1.5, 1.7) * + Matrix::scaling(0.1, 1.5, 0.1)); + the_leg4->set_material(the_leg_material); + the_world.add_object(the_leg4); + + // Glass Cube + the_glass_cube = new Cube(); + the_glass_cube->set_transform(Matrix::translation(0, 3.45001, 0) * + Matrix::rotation_y(0.2) * + Matrix::scaling(0.25, 0.25, 0.25)); + Material &the_glass_cube_material = the_glass_cube->material(); + the_glass_cube_material.set_color(Color(1, 1, 0.8)); + the_glass_cube_material.set_ambient(0.0); + the_glass_cube_material.set_diffuse(0.3); + the_glass_cube_material.set_specular(0.9); + the_glass_cube_material.set_shininess(300); + the_glass_cube_material.set_reflective(0.7); + the_glass_cube_material.set_transparency(0.7); + the_glass_cube_material.set_refractive_index(1.5); + the_world.add_object(the_glass_cube); + + // Little Cube 1 + the_little_cube1 = new Cube(); + the_little_cube1->set_transform(Matrix::translation(1, 3.35, -0.9) * + Matrix::rotation_y(-0.4) * + Matrix::scaling(0.15, 0.15, 0.15)); + Material &the_little_cube1_material = the_little_cube1->material(); + the_little_cube1_material.set_color(Color(1, 0.5, 0.5)); + the_little_cube1_material.set_reflective(0.6); + the_little_cube1_material.set_diffuse(0.4); + the_world.add_object(the_little_cube1); + + // Little Cube 2 + the_little_cube2 = new Cube(); + the_little_cube2->set_transform(Matrix::translation(-1.5, 3.27, 0.3) * + Matrix::rotation_y(0.4) * + Matrix::scaling(0.15, 0.07, 0.15)); + Material &the_little_cube2_material = the_little_cube2->material(); + the_little_cube2_material.set_color(Color(1, 1, 0.5)); + the_world.add_object(the_little_cube2); + + // Little Cube 3 + the_little_cube3 = new Cube(); + the_little_cube3->set_transform(Matrix::translation(0, 3.25, 1) * + Matrix::rotation_y(0.4) * + Matrix::scaling(0.2, 0.05, 0.05)); + Material &the_little_cube3_material = the_little_cube3->material(); + the_little_cube3_material.set_color(Color(0.5, 1, 0.5)); + the_world.add_object(the_little_cube3); + + // Little Cube 4 + the_little_cube4 = new Cube(); + the_little_cube4->set_transform(Matrix::translation(-0.6, 3.4, -1) * + Matrix::rotation_y(0.8) * + Matrix::scaling(0.05, 0.2, 0.05)); + Material &the_little_cube4_material = the_little_cube4->material(); + the_little_cube4_material.set_color(Color(0.5, 0.5, 1)); + the_world.add_object(the_little_cube4); + + // Little Cube 5 + the_little_cube5 = new Cube(); + the_little_cube5->set_transform(Matrix::translation(2, 3.4, 1) * + Matrix::rotation_y(0.8) * + Matrix::scaling(0.05, 0.2, 0.05)); + Material &the_little_cube5_material = the_little_cube5->material(); + the_little_cube5_material.set_color(Color(0.5, 1, 1)); + the_world.add_object(the_little_cube5); + + // Frame 1 + the_frame1 = new Cube(); + the_frame1->set_transform(Matrix::translation(-10, 4, 1) * + Matrix::scaling(0.05, 1, 1)); + Material &the_frame1_material = the_frame1->material(); + the_frame1_material.set_color(Color(0.7098, 0.2471, 0.2196)); + the_frame1_material.set_diffuse(0.6); + the_world.add_object(the_frame1); + + // Frame 2 + the_frame2 = new Cube(); + the_frame2->set_transform(Matrix::translation(-10, 3.4, 2.7) * + Matrix::scaling(0.05, 0.4, 0.4)); + Material &the_frame2_material = the_frame2->material(); + the_frame2_material.set_color(Color(0.2667, 0.2706, 0.6902)); + the_frame2_material.set_diffuse(0.6); + the_world.add_object(the_frame2); + + // Frame 3 + the_frame3 = new Cube(); + the_frame3->set_transform(Matrix::translation(-10, 4.6, 2.7) * + Matrix::scaling(0.05, 0.4, 0.4)); + Material &the_frame3_material = the_frame3->material(); + the_frame3_material.set_color(Color(0.3098, 0.5961, 0.3098)); + the_frame3_material.set_diffuse(0.6); + the_world.add_object(the_frame3); + + // Frame + the_frame = new Cube(); + the_frame->set_transform(Matrix::translation(-2, 3.5, 9.95) * + Matrix::scaling(5, 1.5, 0.05)); + Material &the_frame_material = the_frame->material(); + the_frame_material.set_color(Color(0.3882, 0.2627, 0.1882)); + the_frame_material.set_diffuse(0.7); + the_world.add_object(the_frame); + + // Mirror + the_mirror = new Cube(); + the_mirror->set_transform(Matrix::translation(-2, 3.5, 9.95) * + Matrix::scaling(4.8, 1.4, 0.06)); + Material &the_mirror_material = the_mirror->material(); + the_mirror_material.set_color(Color::Black()); + the_mirror_material.set_ambient(0.0); + the_mirror_material.set_diffuse(0.0); + the_mirror_material.set_specular(0.0); + the_mirror_material.set_shininess(300); + the_mirror_material.set_reflective(1.0); + the_world.add_object(the_mirror); + + // The Light source is white, shining from above and to the left + the_world.set_light(PointLight(Tuple::Point(0, 6.9, -5), Color(1, 1, 0.9))); + + // Configure the camera. + // the_camera = Camera(100, 50, 0.7805); + // the_camera = Camera(320, 200, 0.7805); + // the_camera = Camera(640, 480, 0.7805); + the_camera = Camera(960, 480, 0.7805); + // the_camera = Camera(2400, 1200, 0.7805); + + the_camera.set_transform( + Matrix::view_transform(Tuple::Point(8, 6, -8), Tuple::Point(0, 3, 0), Tuple::Vector(0, 1, 0))); + + the_start = chrono::high_resolution_clock::now(); + the_camera.show_progress_bar(); + the_canvas = the_camera.render(the_world); + the_end = chrono::high_resolution_clock::now(); + + the_canvas.save_to_file("chapter_12.ppm"); + + chrono::duration the_elapsed_time = the_end - the_start; + printf("Execution Time: %f secondes\n", the_elapsed_time.count()); + + return 0; +} diff --git a/raytracing/src/shapes/cube.cpp b/raytracing/src/shapes/cube.cpp index 35dd931..37b57a7 100644 --- a/raytracing/src/shapes/cube.cpp +++ b/raytracing/src/shapes/cube.cpp @@ -41,7 +41,7 @@ using namespace Raytracer; Intersections Cube::local_intersect(const Ray &a_ray) { - double the_tmin, the_max; + double the_tmin, the_tmax; Intersections the_intersections; const auto [the_xtmin, the_xtmax] = check_axis(a_ray.origin().x(), a_ray.direction().x()); @@ -49,10 +49,15 @@ Intersections Cube::local_intersect(const Ray &a_ray) const auto [the_ztmin, the_ztmax] = check_axis(a_ray.origin().z(), a_ray.direction().z()); the_tmin = std::max({the_xtmin, the_ytmin, the_ztmin}); - the_max = std::min({the_xtmax, the_ytmax, the_ztmax}); + the_tmax = std::min({the_xtmax, the_ytmax, the_ztmax}); + + if (the_tmin > the_tmax) + { + return the_intersections; + } the_intersections.add(Intersection(the_tmin, this)); - the_intersections.add(Intersection(the_max, this)); + the_intersections.add(Intersection(the_tmax, this)); return the_intersections; } @@ -61,7 +66,18 @@ Intersections Cube::local_intersect(const Ray &a_ray) Tuple Cube::local_normal_at(const Tuple &a_local_point) const { - return Tuple::Vector(0, 1, 0); + double the_maxc = std::max({std::abs(a_local_point.x()), std::abs(a_local_point.y()), std::abs(a_local_point.z())}); + + if (double_equal(the_maxc, std::abs(a_local_point.x()))) + { + return Tuple::Vector(a_local_point.x(), 0, 0); + } + else if (double_equal(the_maxc, std::abs(a_local_point.y()))) + { + return Tuple::Vector(0, a_local_point.y(), 0); + } + + return Tuple::Vector(0, 0, a_local_point.z()); } /* ------------------------------------------------------------------------- */ diff --git a/tests/12_cubes.cpp b/tests/12_cubes.cpp index 2ad1c02..85e614c 100644 --- a/tests/12_cubes.cpp +++ b/tests/12_cubes.cpp @@ -33,7 +33,7 @@ using namespace Raytracer; /* ------------------------------------------------------------------------- */ -class CubeTest +class CubeTestIntersect { public: Tuple origin; @@ -44,6 +44,15 @@ public: /* ------------------------------------------------------------------------- */ +class CubeTestNormal +{ +public: + Tuple point; + Tuple normal; +}; + +/* ------------------------------------------------------------------------- */ + SCENARIO("A Ray intersects a cube", "[features/cubes.feature]") { // | | origin | direction | t1 | t2 | @@ -54,7 +63,7 @@ SCENARIO("A Ray intersects a cube", "[features/cubes.feature]") // | +z | point(0.5, 0, 5) | vector( 0, 0,-1) | 4 | 6 | // | -z | point(0.5, 0, -5) | vector( 0, 0, 1) | 4 | 6 | // | inside | point(0, 0.5, 0) | vector( 0, 0, 1) | -1 | -1 | - CubeTest the_test[7] = { + CubeTestIntersect the_test[] = { { Tuple::Point(5.0, 0.5, 0.0), Tuple::Vector(-1, 0, 0), 4, 6}, {Tuple::Point(-5.0, 0.5, 0.0), Tuple::Vector(1, 0, 0), 4, 6}, { Tuple::Point(0.5, 5.0, 0.0), Tuple::Vector(0, -1, 0), 4, 6}, @@ -92,3 +101,87 @@ SCENARIO("A Ray intersects a cube", "[features/cubes.feature]") } } } + +/* ------------------------------------------------------------------------- */ + +SCENARIO("A Ray misses a cube", "[features/cubes.feature]") +{ + // | point | direction | + // | point(-2, 0, 0) | vector(0.2673, 0.5345, 0.8018) | + // | point(0, -2, 0) | vector(0.8018, 0.2673, 0.5345) | + // | point(0, 0, -2) | vector(0.5345, 0.8018, 0.2673) | + // | point(2, 0, 2) | vector(0, 0, -1) | + // | point(0, 2, 2) | vector(0, -1, 0) | + // | point(2, 2, 0) | vector(-1, 0, 0) | + CubeTestIntersect the_test[] = { + {Tuple::Point(-2, 0, 0), Tuple::Vector(0.2673, 0.5345, 0.8018), -1, -1}, + { Tuple::Point(0, -2, 0), Tuple::Vector(0.8018, 0.2673, 0.5345), -1, -1}, + { Tuple::Point(0, 0, -2), Tuple::Vector(0.5345, 0.8018, 0.2673), -1, -1}, + { Tuple::Point(2, 0, 2), Tuple::Vector(0, 0, -1), -1, -1}, + { Tuple::Point(0, 2, 2), Tuple::Vector(0, -1, 0), -1, -1}, + { Tuple::Point(2, 2, 0), Tuple::Vector(-1, 0, 0), -1, -1} + }; + GIVEN("c <- cube()") + { + Cube c; + AND_GIVEN("r <- ray(, )") + { + WHEN("xs <- local_intersect(c,r)") + { + for (int i = 0; i < 6; i++) + { + Ray r(the_test[i].origin, the_test[i].direction); + Intersections xs = c.local_intersect(r); + THEN("xs.count = 0") + { + REQUIRE(xs.count() == 0); + } + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("The normal on the surface of a cube", "[features/cubes.feature]") +{ + // | origin | normal | + // | point(1, 0.5, -0.8) | vector(1, 0, 0) | + // | point(-1, -0.2, 0.9) | vector(-1, 0, 0) | + // | point(-0.4, 1, -0.1) | vector(0, 1, 0) | + // | point(0.3, -1, -0.7) | vector(0, -1, 0) | + // | point(-0.6, 0.3, 1) | vector(0, 0, 1) | + // | point(0.4, 0.4, -1) | vector(0, 0, -1) | + // | point(1, 1, -1) | vector(1, 0, 0) | + // | point(-1,-1, -1) | vector(-1, 0, 0) | + CubeTestNormal the_test[] = { + { Tuple::Point(1, 0.5, -0.8), Tuple::Vector(1, 0, 0)}, + { Tuple::Point(-1, -0.2, 0.9), Tuple::Vector(-1, 0, 0)}, + {Tuple::Point(-0.4, 1, -0.1), Tuple::Vector(0, 1, 0)}, + { Tuple::Point(0.3, -1, -0.7), Tuple::Vector(0, -1, 0)}, + {Tuple::Point(-0.6, 0.3, 1), Tuple::Vector(0, 0, 1)}, + { Tuple::Point(0.4, 0.4, -1), Tuple::Vector(0, 0, -1)}, + { Tuple::Point(1, 1, -1), Tuple::Vector(1, 0, 0)}, + { Tuple::Point(-1, -1, -1), Tuple::Vector(-1, 0, 0)} + }; + GIVEN("c <- cube()") + { + Cube c; + AND_GIVEN("p <- )") + { + WHEN("normal <- local_normal_at(p)") + { + for (int i = 0; i < 8; i++) + { + Tuple p = the_test[i].point; + Tuple normal = c.local_normal_at(p); + THEN("normal = ") + { + REQUIRE(normal == the_test[i].normal); + } + } + } + } + } +}