From 0576ccaccb9eb54f523dbd5fe87e771a63be7d49 Mon Sep 17 00:00:00 2001 From: NADAL Jean-Baptiste Date: Thu, 29 Feb 2024 18:39:45 +0100 Subject: [PATCH] [WIP] stripe-pattern is now ok. --- apps/chapter_06.cpp | 6 +- apps/chapter_10.cpp | 3 +- raytracing/src/core/intersection-data.cpp | 4 +- raytracing/src/core/intersection-data.h | 2 +- raytracing/src/core/intersection.cpp | 4 +- raytracing/src/core/intersection.h | 2 +- raytracing/src/renderer/material.cpp | 9 +- raytracing/src/renderer/material.h | 9 +- raytracing/src/renderer/stripe-pattern.cpp | 18 +++ raytracing/src/renderer/stripe-pattern.h | 8 ++ raytracing/src/renderer/world.cpp | 8 +- raytracing/src/renderer/world.h | 2 + raytracing/src/shapes/shape.cpp | 7 + raytracing/src/shapes/shape.h | 1 + tests/05_rays.cpp | 8 +- tests/06_light_shading.cpp | 10 +- tests/08_shadows.cpp | 2 +- tests/09_planes.cpp | 2 +- tests/10_patterns.cpp | 143 +++++++++++++++++---- 19 files changed, 192 insertions(+), 56 deletions(-) diff --git a/apps/chapter_06.cpp b/apps/chapter_06.cpp index 9bb290e..70ff7b6 100644 --- a/apps/chapter_06.cpp +++ b/apps/chapter_06.cpp @@ -77,10 +77,10 @@ int shadow_sphere(uint8_t a_canvas_pixels, double a_wall_size, uint8_t a_wall_z) if (the_intersec.is_defined()) { Tuple the_point = the_ray.position(the_intersec.distance_t()); - Tuple the_normal = the_intersec.object().normal_at(the_point); + Tuple the_normal = the_intersec.object()->normal_at(the_point); Tuple the_eye = -the_ray.direction(); - Color the_color = - the_intersec.object().material().lighting(the_light, the_point, the_eye, the_normal, false); + Color the_color = the_intersec.object()->material().lighting(nullptr, the_light, the_point, the_eye, + the_normal, false); the_canvas.write_pixel(x, y, the_color); } } diff --git a/apps/chapter_10.cpp b/apps/chapter_10.cpp index ad21a34..aae064c 100644 --- a/apps/chapter_10.cpp +++ b/apps/chapter_10.cpp @@ -54,7 +54,7 @@ int main(void) 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_floor_material.set_pattern(new StripePattern(Color(1, 1, 1), Color(1, 0.9, 0.9))); the_world.add_object(the_floor); // The large sphere in the middle is a unit sphere, translated upward slightly and colored green. @@ -62,6 +62,7 @@ int main(void) 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_pattern(new StripePattern(Color(1, 1, 1), 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); diff --git a/raytracing/src/core/intersection-data.cpp b/raytracing/src/core/intersection-data.cpp index df02dbf..8bd677a 100644 --- a/raytracing/src/core/intersection-data.cpp +++ b/raytracing/src/core/intersection-data.cpp @@ -54,9 +54,9 @@ void IntersectionData::set_distance_t(double a_value) /* ------------------------------------------------------------------------- */ -const Shape &IntersectionData::object(void) const +Shape *IntersectionData::object(void) const { - return *m_shape; + return m_shape; } /* ------------------------------------------------------------------------- */ diff --git a/raytracing/src/core/intersection-data.h b/raytracing/src/core/intersection-data.h index eb67bc5..7b7483c 100644 --- a/raytracing/src/core/intersection-data.h +++ b/raytracing/src/core/intersection-data.h @@ -44,7 +44,7 @@ namespace Raytracer double distance_t(void) const; void set_distance_t(double a_value); - const Shape &object(void) const; + Shape *object(void) const; void set_object(Shape *a_shape); const Tuple &point(void) const; diff --git a/raytracing/src/core/intersection.cpp b/raytracing/src/core/intersection.cpp index 16da343..26e93b8 100644 --- a/raytracing/src/core/intersection.cpp +++ b/raytracing/src/core/intersection.cpp @@ -141,9 +141,9 @@ double Intersection::distance_t(void) const /* ------------------------------------------------------------------------- */ -const Shape &Intersection::object(void) const +const Shape *Intersection::object(void) const { - return *m_shape; + return m_shape; } /* ------------------------------------------------------------------------- */ diff --git a/raytracing/src/core/intersection.h b/raytracing/src/core/intersection.h index c2e394e..649db5d 100644 --- a/raytracing/src/core/intersection.h +++ b/raytracing/src/core/intersection.h @@ -56,7 +56,7 @@ namespace Raytracer bool operator<=(double a_distance) const; double distance_t(void) const; - const Shape &object(void) const; + const Shape *object(void) const; bool is_nothing(void); bool is_defined(void); diff --git a/raytracing/src/renderer/material.cpp b/raytracing/src/renderer/material.cpp index 571961a..c4a3769 100644 --- a/raytracing/src/renderer/material.cpp +++ b/raytracing/src/renderer/material.cpp @@ -31,6 +31,9 @@ #include #include "core/common.h" +#include "shapes/shape.h" + +#include "renderer/stripe-pattern.h" #include "material.h" @@ -144,8 +147,8 @@ void Material::set_pattern(StripePattern *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 Material::lighting(Shape *an_object, 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; @@ -153,7 +156,7 @@ Color Material::lighting(const PointLight &a_light, const Tuple &a_point, const if (m_pattern != nullptr) { - the_color = m_pattern->stripe_at(a_point); + the_color = m_pattern->stripe_at_object(an_object, a_point); } else { diff --git a/raytracing/src/renderer/material.h b/raytracing/src/renderer/material.h index f89f2c0..06ba290 100644 --- a/raytracing/src/renderer/material.h +++ b/raytracing/src/renderer/material.h @@ -30,13 +30,16 @@ #include "core/color.h" #include "core/tuple.h" + #include "lights/point-light.h" -#include "stripe-pattern.h" /* ------------------------------------------------------------------------- */ namespace Raytracer { + class Shape; + class StripePattern; + class Material { public: @@ -62,8 +65,8 @@ namespace Raytracer 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; + Color lighting(Shape *an_object, const PointLight &a_light, const Tuple &a_point, const Tuple &an_eyev, + const Tuple &a_normalv, bool is_shadowed) const; private: Color m_color; diff --git a/raytracing/src/renderer/stripe-pattern.cpp b/raytracing/src/renderer/stripe-pattern.cpp index 2723615..6794d6f 100644 --- a/raytracing/src/renderer/stripe-pattern.cpp +++ b/raytracing/src/renderer/stripe-pattern.cpp @@ -40,6 +40,7 @@ using namespace Raytracer; StripePattern::StripePattern(const Color &a_color_a, const Color &a_color_b) : m_a(a_color_a), m_b(a_color_b) { + m_transform = Matrix::identity(); } /* ------------------------------------------------------------------------- */ @@ -72,3 +73,20 @@ const Color &StripePattern::stripe_at(const Tuple &a_point) const return m_b; } + +/* ------------------------------------------------------------------------- */ + +void StripePattern::set_pattern_transform(const Matrix &a_transform_matrix) +{ + m_transform = a_transform_matrix; +} + +/* ------------------------------------------------------------------------- */ + +const Color &StripePattern::stripe_at_object(Shape *an_object, const Tuple &a_world_point) const +{ + Tuple the_objet_point = an_object->transform().inverse() * a_world_point; + Tuple the_pattern_point = m_transform.inverse() * the_objet_point; + + return stripe_at(the_pattern_point); +} diff --git a/raytracing/src/renderer/stripe-pattern.h b/raytracing/src/renderer/stripe-pattern.h index 4e563a8..c30e107 100644 --- a/raytracing/src/renderer/stripe-pattern.h +++ b/raytracing/src/renderer/stripe-pattern.h @@ -29,8 +29,11 @@ /* ------------------------------------------------------------------------- */ #include "core/color.h" +#include "core/matrix.h" #include "core/tuple.h" +#include "shapes/shape.h" + /* ------------------------------------------------------------------------- */ namespace Raytracer @@ -47,7 +50,12 @@ namespace Raytracer const Color &stripe_at(const Tuple &a_point) const; + void set_pattern_transform(const Matrix &a_transform_matrix); + + const Color &stripe_at_object(Shape *an_object, const Tuple &a_world_point) const; + private: + Matrix m_transform; Color m_a; Color m_b; }; diff --git a/raytracing/src/renderer/world.cpp b/raytracing/src/renderer/world.cpp index 853ffd5..410c828 100644 --- a/raytracing/src/renderer/world.cpp +++ b/raytracing/src/renderer/world.cpp @@ -29,6 +29,8 @@ /* ------------------------------------------------------------------------- */ #include "core/matrix.h" + +#include "shapes/shape.h" #include "shapes/sphere.h" #include "world.h" @@ -158,9 +160,9 @@ Intersections World::intersect_world(const Ray &a_ray) const Color World::shade_hit(const IntersectionData &an_intersection_data) const { bool the_shadowed = is_shadowed(an_intersection_data.over_point()); - return an_intersection_data.object().material().lighting(m_light, an_intersection_data.over_point(), - an_intersection_data.eyev(), - an_intersection_data.normalv(), the_shadowed); + Shape *the_object = an_intersection_data.object(); + return the_object->material().lighting(the_object, m_light, an_intersection_data.over_point(), + an_intersection_data.eyev(), an_intersection_data.normalv(), the_shadowed); } /* ------------------------------------------------------------------------- */ diff --git a/raytracing/src/renderer/world.h b/raytracing/src/renderer/world.h index 3675f66..806da10 100644 --- a/raytracing/src/renderer/world.h +++ b/raytracing/src/renderer/world.h @@ -41,6 +41,8 @@ namespace Raytracer { + class Shape; + class World { public: diff --git a/raytracing/src/shapes/shape.cpp b/raytracing/src/shapes/shape.cpp index d942592..710124f 100644 --- a/raytracing/src/shapes/shape.cpp +++ b/raytracing/src/shapes/shape.cpp @@ -80,6 +80,13 @@ bool Shape::operator==(const Shape &a_shape) const /* ------------------------------------------------------------------------- */ +bool Shape::operator==(const Shape *a_shape) const +{ + return (m_transform == a_shape->m_transform) && (m_material == a_shape->m_material); +} + +/* ------------------------------------------------------------------------- */ + Matrix &Shape::transform(void) { return m_transform; diff --git a/raytracing/src/shapes/shape.h b/raytracing/src/shapes/shape.h index 3ec5de0..91effd6 100644 --- a/raytracing/src/shapes/shape.h +++ b/raytracing/src/shapes/shape.h @@ -51,6 +51,7 @@ namespace Raytracer const Shape &operator=(const Shape &a_shape); bool operator==(const Shape &a_shape) const; + bool operator==(const Shape *a_shape) const; Matrix &transform(void); const Matrix &transform(void) const; diff --git a/tests/05_rays.cpp b/tests/05_rays.cpp index a798fc8..9c86c3c 100644 --- a/tests/05_rays.cpp +++ b/tests/05_rays.cpp @@ -242,7 +242,7 @@ SCENARIO("An intersection encapsulates t and object", "[features/intersections.f } AND_THEN("i.object = s") { - REQUIRE(i.object() == s); + REQUIRE(*i.object() == s); } } } @@ -270,7 +270,7 @@ SCENARIO("An intersection could be affected", "[features/intersections.feature]" } AND_THEN("i2.object = s") { - REQUIRE(i2.object() == s); + REQUIRE(*i2.object() == s); } } } @@ -403,11 +403,11 @@ SCENARIO("Intersect sets the object on the intersection", "[features/spheres.fea } AND_THEN("xs[0].object = s") { - REQUIRE(xs[0].object() == s); + REQUIRE(*xs[0].object() == s); } AND_THEN("xs[1].object = s") { - REQUIRE(xs[1].object() == s); + REQUIRE(*xs[1].object() == s); } } } diff --git a/tests/06_light_shading.cpp b/tests/06_light_shading.cpp index 98ef3b6..cecb865 100644 --- a/tests/06_light_shading.cpp +++ b/tests/06_light_shading.cpp @@ -331,7 +331,7 @@ SCENARIO("Lighting with the eye between the light and the surface", "[features/m PointLight light = PointLight(Tuple::Point(0, 0, -10), Color(1, 1, 1)); WHEN("result <- lighting(m, light, position, eyev, normalv)") { - Color result = m.lighting(light, position, eyev, normalv, false); + Color result = m.lighting(nullptr, light, position, eyev, normalv, false); THEN("result = color(1.9, 1.9, 1.9)") { REQUIRE(result == Color(1.9, 1.9, 1.9)); @@ -360,7 +360,7 @@ SCENARIO("Lighting with eye between the light & surface, eye offset 45°", "[fea PointLight light = PointLight(Tuple::Point(0, 0, -10), Color(1, 1, 1)); WHEN("result <- lighting(m, light, position, eyev, normalv)") { - Color result = m.lighting(light, position, eyev, normalv, false); + Color result = m.lighting(nullptr, light, position, eyev, normalv, false); THEN("result = color(1.0, 1.0, 1.0)") { REQUIRE(result == Color(1.0, 1.0, 1.0)); @@ -389,7 +389,7 @@ SCENARIO("Lighting with the eye opposite surface, light offset 45°", "[features PointLight light = PointLight(Tuple::Point(0, 10, -10), Color(1, 1, 1)); WHEN("result <- lighting(m, light, position, eyev, normalv)") { - Color result = m.lighting(light, position, eyev, normalv, false); + Color result = m.lighting(nullptr, light, position, eyev, normalv, false); THEN("result = color(0.7364, 0.7364, 0.7364)") { REQUIRE(result == Color(0.7364, 0.7364, 0.7364)); @@ -418,7 +418,7 @@ SCENARIO("Lighting with the eye in the path of the reflection vector", "[feature PointLight light = PointLight(Tuple::Point(0, 10, -10), Color(1, 1, 1)); WHEN("result <- lighting(m, light, position, eyev, normalv)") { - Color result = m.lighting(light, position, eyev, normalv, false); + Color result = m.lighting(nullptr, light, position, eyev, normalv, false); THEN("result = color(1.6364, 1.6364, 1.6364)") { REQUIRE(result == Color(1.6364, 1.6364, 1.6364)); @@ -447,7 +447,7 @@ SCENARIO("Lighting with the light behind the surface", "[features/materials.feat PointLight light = PointLight(Tuple::Point(0, 0, 10), Color(1, 1, 1)); WHEN("result <- lighting(m, light, position, eyev, normalv)") { - Color result = m.lighting(light, position, eyev, normalv, false); + Color result = m.lighting(nullptr, light, position, eyev, normalv, false); THEN("result = color(0.1, 0.1, 0.1)") { REQUIRE(result == Color(0.1, 0.1, 0.1)); diff --git a/tests/08_shadows.cpp b/tests/08_shadows.cpp index 2ca90bc..92fe27b 100644 --- a/tests/08_shadows.cpp +++ b/tests/08_shadows.cpp @@ -52,7 +52,7 @@ SCENARIO("Lightning with the surface in shadow", "[features/materials.feature]") bool in_shadow = true; WHEN("result <- lighting(m, light, position, eyev, normalv, in_shadow)") { - Color result = m.lighting(light, position, eyev, normalv, in_shadow); + Color result = m.lighting(nullptr, light, position, eyev, normalv, in_shadow); THEN("result = color(0.1, 0.1, 0.1)") { REQUIRE(result == Color(0.1, 0.1, 0.1)); diff --git a/tests/09_planes.cpp b/tests/09_planes.cpp index 60203a0..544c079 100644 --- a/tests/09_planes.cpp +++ b/tests/09_planes.cpp @@ -347,7 +347,7 @@ SCENARIO("A ray Intersecting a plane from above", "[features/planes.feature]") } AND_THEN("xs[0].object = p") { - REQUIRE(xs[0].object() == p); + REQUIRE(*xs[0].object() == p); } } } diff --git a/tests/10_patterns.cpp b/tests/10_patterns.cpp index 6baa8a0..cecb724 100644 --- a/tests/10_patterns.cpp +++ b/tests/10_patterns.cpp @@ -58,12 +58,15 @@ SCENARIO("A stripe pattern is constant in y", "[features/patterns.feature]") StripePattern pattern(Color::White(), Color::Black()); THEN("stripe_at(pattern, point(0, 0, 0) = white") { + REQUIRE(pattern.stripe_at(Tuple::Point(0, 0, 0)) == Color::White()); } AND_THEN("stripe_at(pattern, point(0, 1, 0) = white") { + REQUIRE(pattern.stripe_at(Tuple::Point(0, 1, 0)) == Color::White()); } AND_THEN("stripe_at(pattern, point(0, 2, 0) = white") { + REQUIRE(pattern.stripe_at(Tuple::Point(0, 2, 0)) == Color::White()); } } } @@ -128,42 +131,48 @@ 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))") + GIVEN("object <- sphere()") { - m.set_pattern(new StripePattern(Color(1, 1, 1), Color(0, 0, 0))); - AND_GIVEN("m.ambient <- 1") + Sphere object; + Material m; + GIVEN("m.pattern <- strip_pattern(color(1, 1, 1), color(0, 0, 0))") { - m.set_ambient(1); - AND_GIVEN("m.diffuse <- 0") + m.set_pattern(new StripePattern(Color(1, 1, 1), Color(0, 0, 0))); + AND_GIVEN("m.ambient <- 1") { - m.set_diffuse(0); - AND_GIVEN("m.specular <- 0") + m.set_ambient(1); + AND_GIVEN("m.diffuse <- 0") { - m.set_specular(0); - AND_GIVEN("eyev <- vector(0, 0, -1)") + m.set_diffuse(0); + AND_GIVEN("m.specular <- 0") { - Tuple eyev = Tuple::Vector(0, 0, -1); - AND_GIVEN("normalv <- vector(0, 0, -1)") + m.set_specular(0); + AND_GIVEN("eyev <- vector(0, 0, -1)") { - Tuple normalv = Tuple::Vector(0, 0, -1); - AND_GIVEN("light <- point_light(point(0, 0, -10), color(1, 1, 1))") + Tuple eyev = Tuple::Vector(0, 0, -1); + AND_GIVEN("normalv <- vector(0, 0, -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)") + Tuple normalv = Tuple::Vector(0, 0, -1); + AND_GIVEN("light <- point_light(point(0, 0, -10), color(1, 1, 1))") { - 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)") + 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 c2 = m.lighting(light, Tuple::Point(1.1, 0, 0), eyev, normalv, false); - THEN("c1 = color(1, 1, 1)") + Color c1 = + m.lighting(&object, light, Tuple::Point(0.9, 0, 0), eyev, normalv, false); + AND_WHEN("c2 <- lighting(m, light, point(1.1, 0, 0), eyev, normalv, false)") { - REQUIRE(c1 == Color(1, 1, 1)); - } - AND_THEN("c2 = color(0, 0, 0)") - { - REQUIRE(c2 == Color(0, 0, 0)); + Color c2 = m.lighting(&object, 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)); + } } } } @@ -175,3 +184,85 @@ SCENARIO("Lightning with a pattern applied", "[features/materials.feature]") } } } + +/* ------------------------------------------------------------------------- */ + +SCENARIO("Stripes with an object transformation", "[features/patterns.feature]") +{ + GIVEN("object <- sphere()") + { + Sphere object; + AND_GIVEN("set_transform(object, scaling(2, 2, 2))") + { + object.set_transform(Matrix::scaling(2, 2, 2)); + AND_GIVEN("pattern <- stripe_pattern(white, black)") + { + StripePattern pattern(Color::White(), Color::Black()); + WHEN("c <- stripe_at_object(pattern, object, point(1.5, 0, 0))") + { + Color c = pattern.stripe_at_object(&object, Tuple::Point(1.5, 0, 0)); + THEN("c = white") + { + REQUIRE(c == Color::White()); + } + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("Stripes with an pattern transformation", "[features/patterns.feature]") +{ + GIVEN("object <- sphere()") + { + Sphere object; + AND_GIVEN("pattern <- stripe_pattern(white, black)") + { + StripePattern pattern(Color::White(), Color::Black()); + AND_GIVEN("set_transform(pattern, scaling(2, 2, 2))") + { + pattern.set_pattern_transform(Matrix::scaling(2, 2, 2)); + WHEN("c <- stripe_at_object(pattern, object, point(1.5, 0, 0))") + { + Color c = pattern.stripe_at_object(&object, Tuple::Point(1.5, 0, 0)); + THEN("c = white") + { + REQUIRE(c == Color::White()); + } + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("Stripes with both an object and pattern transformation", "[features/patterns.feature]") +{ + GIVEN("object <- sphere()") + { + Sphere object; + AND_GIVEN("set_transform(object, scaling(2, 2, 2))") + { + object.set_transform(Matrix::scaling(2, 2, 2)); + AND_GIVEN("pattern <- stripe_pattern(white, black)") + { + StripePattern pattern(Color::White(), Color::Black()); + AND_GIVEN("set_pattern_transform(pattern, translation(0.5, 0, 0)))") + { + pattern.set_pattern_transform(Matrix::translation(0.5, 0, 0)); + WHEN("c <- stripe_at_object(pattern, object, point(2.5, 0, 0))") + { + Color c = pattern.stripe_at_object(&object, Tuple::Point(1.5, 0, 0)); + THEN("c = white") + { + REQUIRE(c == Color::White()); + } + } + } + } + } + } +}