diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index f571604..dfb3b2d 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -16,3 +16,6 @@ target_link_libraries(chapter_07 PRIVATE raytracing gcov) add_executable(chapter_09 chapter_09.cpp) target_link_libraries(chapter_09 PRIVATE raytracing gcov OpenMP::OpenMP_CXX) + +add_executable(chapter_10 chapter_10.cpp) +target_link_libraries(chapter_10 PRIVATE raytracing gcov) diff --git a/apps/chapter_10.cpp b/apps/chapter_10.cpp new file mode 100644 index 0000000..ad21a34 --- /dev/null +++ b/apps/chapter_10.cpp @@ -0,0 +1,106 @@ +/*! + * chapter_10.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: 29/02/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 + +#include +#include + +#include + +/* ------------------------------------------------------------------------- */ + +using namespace Raytracer; +using namespace std; + +/* ------------------------------------------------------------------------- */ + +int main(void) +{ + World the_world; + Camera the_camera; + Canvas the_canvas; + Plane *the_floor; + Sphere *the_middle, *the_right, *the_left; + chrono::time_point the_start, the_end; + + printf("Chapter 10 example.\n"); + + // Floor is an extremely flattened sphere with a matte texture. + the_floor = new Plane(); + Material &the_floor_material = the_floor->material(); + the_floor_material.set_color(Color(1, 0.9, 0.9)); + the_floor_material.set_specular(0); + the_floor_material.set_pattern(new StripePattern(Color(1, 1, 1), Color(0, 0, 0))); + the_world.add_object(the_floor); + + // The large sphere in the middle is a unit sphere, translated upward slightly and colored green. + the_middle = new Sphere(); + the_middle->set_transform(Matrix::translation(-0.5, 1, 0.5)); + Material &the_middle_material = the_middle->material(); + the_middle_material.set_color(Color(0.1, 1, 0.5)); + the_middle_material.set_diffuse(0.7); + the_middle_material.set_specular(0.3); + the_world.add_object(the_middle); + + // The smaller green sphere on the right is scaled in half + the_right = new Sphere(); + the_right->set_transform(Matrix::translation(1.5, 0.5, -0.5) * Matrix::scaling(0.5, 0.5, 0.5)); + Material &the_right_material = the_right->material(); + the_right_material.set_color(Color(0.5, 1, 0.1)); + the_right_material.set_diffuse(0.7); + the_right_material.set_specular(0.3); + the_world.add_object(the_right); + + // The smallest sphere is scaled by a third, before being translated + the_left = new Sphere(); + the_left->set_transform(Matrix::translation(-1.5, 0.33, -0.75) * Matrix::scaling(0.33, 0.33, 0.33)); + Material &the_left_material = the_left->material(); + the_left_material.set_color(Color(1, 0.8, 0.1)); + the_left_material.set_diffuse(0.7); + the_left_material.set_specular(0.3); + the_world.add_object(the_left); + + // The Light source is white, shining from above and to the left + the_world.set_light(PointLight(Tuple::Point(-10, 10, -10), Color(1, 1, 1))); + + // Configure the camera. + // the_camera = Camera(100, 50, std::numbers::pi / 2); + the_camera = Camera(320, 200, std::numbers::pi / 2); + the_camera.set_transform( + Matrix::view_transform(Tuple::Point(0, 1.5, -3.5), Tuple::Point(0, 1, 0), Tuple::Vector(0, 1, 0))); + + the_start = chrono::high_resolution_clock::now(); + the_canvas = the_camera.render(the_world); + the_end = chrono::high_resolution_clock::now(); + + the_canvas.save_to_file("chapter10.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/renderer/material.cpp b/raytracing/src/renderer/material.cpp index 8548b46..571961a 100644 --- a/raytracing/src/renderer/material.cpp +++ b/raytracing/src/renderer/material.cpp @@ -38,7 +38,13 @@ using namespace Raytracer; /* ------------------------------------------------------------------------- */ -Material::Material(void) : m_color(1, 1, 1), m_ambient(0.1), m_diffuse(0.9), m_specular(0.9), m_shininess(200) +Material::Material(void) : + m_color(1, 1, 1), + m_ambient(0.1), + m_diffuse(0.9), + m_specular(0.9), + m_shininess(200), + m_pattern(nullptr) { } @@ -49,6 +55,7 @@ bool Material::operator==(const Material &a_material) const return (m_color == a_material.m_color) && double_equal(m_ambient, a_material.m_ambient) && double_equal(m_diffuse, a_material.m_diffuse) && double_equal(m_specular, a_material.m_specular) && double_equal(m_shininess, a_material.m_shininess); + // TODO m_pattern } /* ------------------------------------------------------------------------- */ @@ -123,15 +130,38 @@ void Material::set_shininess(double a_value) /* ------------------------------------------------------------------------- */ +const StripePattern *Material::pattern(void) const +{ + return m_pattern; +} + +/* ------------------------------------------------------------------------- */ + +void Material::set_pattern(StripePattern *a_pattern) +{ + m_pattern = a_pattern; +} + +/* ------------------------------------------------------------------------- */ + Color Material::lighting(const PointLight &a_light, const Tuple &a_point, const Tuple &an_eyev, const Tuple &a_normalv, bool is_shadowed) const { Color the_effective_color; Tuple the_light_v, the_reflect_v; - Color the_ambient, the_diffuse, the_specular; + Color the_color, the_ambient, the_diffuse, the_specular; + + if (m_pattern != nullptr) + { + the_color = m_pattern->stripe_at(a_point); + } + else + { + the_color = m_color; + } // Combine the surface color with the light's color - the_effective_color = m_color * a_light.intensity(); + the_effective_color = the_color * a_light.intensity(); // Find the direction to the light source the_light_v = (a_light.position() - a_point).normalize(); diff --git a/raytracing/src/renderer/material.h b/raytracing/src/renderer/material.h index 530f8e3..f89f2c0 100644 --- a/raytracing/src/renderer/material.h +++ b/raytracing/src/renderer/material.h @@ -31,6 +31,7 @@ #include "core/color.h" #include "core/tuple.h" #include "lights/point-light.h" +#include "stripe-pattern.h" /* ------------------------------------------------------------------------- */ @@ -58,6 +59,9 @@ namespace Raytracer const double &shininess(void) const; void set_shininess(double a_value); + const StripePattern *pattern(void) const; + void set_pattern(StripePattern *a_pattern); + Color lighting(const PointLight &a_light, const Tuple &a_point, const Tuple &an_eyev, const Tuple &a_normalv, bool is_shadowed) const; @@ -67,6 +71,7 @@ namespace Raytracer double m_diffuse; double m_specular; double m_shininess; + StripePattern *m_pattern; }; }; // namespace Raytracer diff --git a/raytracing/src/renderer/stripe-pattern.cpp b/raytracing/src/renderer/stripe-pattern.cpp index 6799651..2723615 100644 --- a/raytracing/src/renderer/stripe-pattern.cpp +++ b/raytracing/src/renderer/stripe-pattern.cpp @@ -44,6 +44,13 @@ StripePattern::StripePattern(const Color &a_color_a, const Color &a_color_b) : m /* ------------------------------------------------------------------------- */ +bool StripePattern::operator==(const StripePattern &a_pattern) const +{ + return (m_a == a_pattern.m_a) && (m_b == a_pattern.m_b); +} + +/* ------------------------------------------------------------------------- */ + const Color &StripePattern::a(void) const { return m_a; @@ -58,7 +65,7 @@ const Color &StripePattern::b(void) const /* ------------------------------------------------------------------------- */ -const Color &StripePattern::stripe_at(Tuple a_point) const +const Color &StripePattern::stripe_at(const Tuple &a_point) const { if (((int)std::floor(a_point.x()) % 2) == 0) return m_a; diff --git a/raytracing/src/renderer/stripe-pattern.h b/raytracing/src/renderer/stripe-pattern.h index e133ca0..4e563a8 100644 --- a/raytracing/src/renderer/stripe-pattern.h +++ b/raytracing/src/renderer/stripe-pattern.h @@ -40,10 +40,12 @@ namespace Raytracer public: StripePattern(const Color &a_color_a, const Color &a_color_b); + bool operator==(const StripePattern &a_pattern) const; + const Color &a(void) const; const Color &b(void) const; - const Color &stripe_at(Tuple a_point) const; + const Color &stripe_at(const Tuple &a_point) const; private: Color m_a; diff --git a/tests/10_patterns.cpp b/tests/10_patterns.cpp index 3d4682d..6baa8a0 100644 --- a/tests/10_patterns.cpp +++ b/tests/10_patterns.cpp @@ -123,3 +123,55 @@ SCENARIO("A stripe pattern alternates in x", "[features/patterns.feature]") } } } + +/* ------------------------------------------------------------------------- */ + +SCENARIO("Lightning with a pattern applied", "[features/materials.feature]") +{ + Material m; + GIVEN("m.pattern <- strip_pattern(color(1, 1, 1), color(0, 0, 0))") + { + m.set_pattern(new StripePattern(Color(1, 1, 1), Color(0, 0, 0))); + AND_GIVEN("m.ambient <- 1") + { + m.set_ambient(1); + AND_GIVEN("m.diffuse <- 0") + { + m.set_diffuse(0); + AND_GIVEN("m.specular <- 0") + { + m.set_specular(0); + AND_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)); + + WHEN("c1 <- lighting(m, light, point(0.9, 0, 0), eyev, normalv, false)") + { + Color c1 = m.lighting(light, Tuple::Point(0.9, 0, 0), eyev, normalv, false); + AND_WHEN("c2 <- lighting(m, light, point(1.1, 0, 0), eyev, normalv, false)") + { + Color c2 = m.lighting(light, Tuple::Point(1.1, 0, 0), eyev, normalv, false); + THEN("c1 = color(1, 1, 1)") + { + REQUIRE(c1 == Color(1, 1, 1)); + } + AND_THEN("c2 = color(0, 0, 0)") + { + REQUIRE(c2 == Color(0, 0, 0)); + } + } + } + } + } + } + } + } + } + } +}