From 6e17dd94c1211e8d4f30fe61acef5e27e87a0e9d Mon Sep 17 00:00:00 2001 From: NADAL Jean-Baptiste Date: Tue, 12 Mar 2024 18:42:19 +0100 Subject: [PATCH] [FEAT] until test3 of schlick implementation --- .vscode/settings.json | 1 + raytracing/src/core/intersection-data.cpp | 31 +++++ raytracing/src/core/intersection-data.h | 2 + raytracing/src/renderer/world.cpp | 11 +- tests/11_reflection_refraction.cpp | 150 ++++++++++++++++++++++ 5 files changed, 190 insertions(+), 5 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index b5f2f52..905b136 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,7 @@ "NADAL", "noninvertible", "Raytracer", + "Schlick", "submatrix" ], "files.associations": { diff --git a/raytracing/src/core/intersection-data.cpp b/raytracing/src/core/intersection-data.cpp index 8ffcc01..e7954a5 100644 --- a/raytracing/src/core/intersection-data.cpp +++ b/raytracing/src/core/intersection-data.cpp @@ -28,6 +28,7 @@ /* ------------------------------------------------------------------------- */ +#include #include #include "intersection-data.h" @@ -213,3 +214,33 @@ const double IntersectionData::n2(void) const { return m_n2; } + +/* ------------------------------------------------------------------------- */ + +double IntersectionData::schlick(void) +{ + double the_cos, the_r0; + + // Find the cosine of the angle between the eye and the normal vectors + the_cos = m_eyev.dot(m_normalv); + + // Total internal reflection can only occur if n1 > n2 + if (m_n1 > m_n2) + { + double the_n, the_sin2_t, the_cos_t; + the_n = m_n1 / m_n2; + the_sin2_t = std::pow(the_n, 2) * (1.0 - std::pow(the_cos, 2)); + if (the_sin2_t > 1.0) + { + return 1.0; + } + + // Compute cosine of the theta_t using trig identity + the_cos_t = std::sqrt(1.0 - the_sin2_t); + the_cos = the_cos_t; + } + + the_r0 = std::pow(((m_n1 - m_n2) / (m_n1 + m_n2)), 2); + + return the_r0 + (1 - the_r0) * std::pow((1 - the_cos), 5); +} diff --git a/raytracing/src/core/intersection-data.h b/raytracing/src/core/intersection-data.h index f8c8d62..576defd 100644 --- a/raytracing/src/core/intersection-data.h +++ b/raytracing/src/core/intersection-data.h @@ -75,6 +75,8 @@ namespace Raytracer void set_n2(double an_n2); const double n2(void) const; + double schlick(void); + private: bool m_is_inside; double m_distance; diff --git a/raytracing/src/renderer/world.cpp b/raytracing/src/renderer/world.cpp index 401edff..625328a 100644 --- a/raytracing/src/renderer/world.cpp +++ b/raytracing/src/renderer/world.cpp @@ -168,7 +168,8 @@ Color World::shade_hit(const IntersectionData &an_intersection_data, uint32_t a_ Color the_surface = the_object->material().lighting(the_object, m_light, an_intersection_data.over_point(), an_intersection_data.eyev(), an_intersection_data.normalv(), the_shadowed); Color the_reflected = reflected_color(an_intersection_data, a_remaining); - return the_surface + the_reflected; + Color the_refracted = refracted_color(an_intersection_data, a_remaining); + return the_surface + the_reflected + the_refracted; } /* ------------------------------------------------------------------------- */ @@ -194,7 +195,7 @@ Color World::reflected_color(const IntersectionData &a_data, uint32_t a_remainin Color World::refracted_color(const IntersectionData &an_intersection_data, uint32_t a_remaining) const { - double the_n_ratio, the_cos_i, the_cos_t, the_sin_t; + double the_n_ratio, the_cos_i, the_cos_t, the_sin2_t; Tuple the_direction; Color the_color; double the_transparency; @@ -218,14 +219,14 @@ Color World::refracted_color(const IntersectionData &an_intersection_data, uint3 the_cos_i = an_intersection_data.eyev().dot(an_intersection_data.normalv()); // Find sin(theta_t)^2 via trigonometric identity - the_sin_t = the_n_ratio * the_n_ratio * (1.0 - the_cos_i * the_cos_i); - if (the_sin_t > 1) + the_sin2_t = the_n_ratio * the_n_ratio * (1.0 - the_cos_i * the_cos_i); + if (the_sin2_t > 1) { return Color(0, 0, 0); } // Find cos(theta_t) via trigonometric identity - the_cos_t = std::sqrt(1.0 - the_sin_t); + the_cos_t = std::sqrt(1.0 - the_sin2_t); // Compute the direction of the refracted ray the_direction = an_intersection_data.normalv() * (the_n_ratio * the_cos_i - the_cos_t) - diff --git a/tests/11_reflection_refraction.cpp b/tests/11_reflection_refraction.cpp index fb5ebae..485fac2 100644 --- a/tests/11_reflection_refraction.cpp +++ b/tests/11_reflection_refraction.cpp @@ -633,3 +633,153 @@ SCENARIO("The refracted color with a refracted ray", "[features/world.feature]") } } } + +/* ------------------------------------------------------------------------- */ + +SCENARIO("shade_hit() with a transparent material", "[features/world.feature]") +{ + GIVEN("w <- default_world()") + { + World w = World::default_world(); + AND_GIVEN("floor <- plane() with:") + { + // | transform | translation(0, -1, 0) | + // | material.transparency | 0.5 | + // | material.refractive_index | 1.5 | + Plane floor; + floor.set_transform(Matrix::translation(0, -1, 0)); + floor.material().set_transparency(0.5); + floor.material().set_refractive_index(1.5); + AND_GIVEN("floor is added to w") + { + w.add_object(&floor); + AND_GIVEN("ball <- sphere() with:") + { + // | material.color | (1, 0, 0) | + // | material.ambient | 0.5 | + // | transform | translation(0, -3.5, -0.5) | + Sphere ball; + ball.material().set_color(Color(1, 0, 0)); + ball.material().set_ambient(0.5); + ball.set_transform(Matrix::translation(0, -3.5, -0.5)); + AND_GIVEN("ball is added to w") + { + w.add_object(&ball); + AND_GIVEN("r <- ray(point(0, 0, -3), vector(0, -sqrt(2) / 2, sqrt(2) / 2))") + { + Ray r(Tuple::Point(0, 0, -3), Tuple::Vector(0, -sqrt(2) / 2, sqrt(2) / 2)); + AND_GIVEN("xs <- intersections(sqrt(2):floor)") + { + Intersections xs = Intersections({Intersection(sqrt(2), &floor)}); + WHEN("comps <- prepare_computations(xs[0], r, xs)") + { + IntersectionData comps = xs[0].prepare_computations(r, &xs); + AND_WHEN("color <- shade_hit(w, comps, 5)") + { + Color color = w.shade_hit(comps, 5); + THEN("color = color(0.93642, 0.68642, 0.68642)") + { + REQUIRE(color == Color(0.93642, 0.68642, 0.68642)); + } + } + } + } + } + } + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("The Schlick approximation under total internal reflection", "[features/intersections.feature]") +{ + GIVEN("shape <- glass_sphere()") + { + Sphere shape = Sphere::Glass(); + AND_GIVEN("r <- ray(point(0, 0, sqrt(2) / 2)), vector(0, 1, 0)") + { + Ray r(Tuple::Point(0, 0, sqrt(2) / 2), Tuple::Vector(0, 1, 0)); + AND_GIVEN("xs <- intersections(-sqrt(2) / 2):shape, sqrt(2) / 2):shape)") + { + Intersections xs = Intersections({Intersection(-sqrt(2) / 2, &shape), + Intersection(sqrt(2) / 2, &shape)}); + WHEN("comps <- prepare_computations(xs[1], r, xs)") + { + IntersectionData comps = xs[1].prepare_computations(r, &xs); + AND_WHEN("reflectance <- schlick(comps)") + { + double reflectance = comps.schlick(); + THEN("reflectance = 1.0") + { + REQUIRE(reflectance == 1.0); + } + } + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("The Schlick approximation with a perpendicular viewing angle", "[features/intersections.feature]") +{ + GIVEN("shape <- glass_sphere()") + { + Sphere shape = Sphere::Glass(); + AND_GIVEN("r <- ray(point(0, 0, 0), vector(0, 1, 0)") + { + Ray r(Tuple::Point(0, 0, 0), Tuple::Vector(0, 1, 0)); + AND_GIVEN("xs <- intersections(-1:shape, 1:shape)") + { + Intersections xs = Intersections({Intersection(-1, &shape), + Intersection(1, &shape)}); + WHEN("comps <- prepare_computations(xs[1], r, xs)") + { + IntersectionData comps = xs[1].prepare_computations(r, &xs); + AND_WHEN("reflectance <- schlick(comps)") + { + double reflectance = comps.schlick(); + THEN("reflectance = 0.04") + { + REQUIRE(double_equal(reflectance, 0.04)); + } + } + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("The Schlick approximation with a small angle and n2 > n1", "[features/intersections.feature]") +{ + GIVEN("shape <- glass_sphere()") + { + Sphere shape = Sphere::Glass(); + AND_GIVEN("r <- ray(point(0, 0.99, -2), vector(0, 0, 1)") + { + Ray r(Tuple::Point(0, 0.99, -2), Tuple::Vector(0, 0, 1)); + AND_GIVEN("xs <- intersections(1.8589:shape)") + { + Intersections xs = Intersections({Intersection(1.8589, &shape)}); + WHEN("comps <- prepare_computations(xs[0], r, xs)") + { + IntersectionData comps = xs[0].prepare_computations(r, &xs); + AND_WHEN("reflectance <- schlick(comps)") + { + double reflectance = comps.schlick(); + THEN("reflectance = 0.48873") + { + REQUIRE(double_equal(reflectance, 0.48873)); + } + } + } + } + } + } +}