diff --git a/README.md b/README.md index c16f3d7..2b210fe 100644 --- a/README.md +++ b/README.md @@ -40,4 +40,8 @@ From Chapter 10: From Chapter 11: -![Chapter 11 reflections rendering test](output/ch11_reflection.png) \ No newline at end of file +![Chapter 11 reflections rendering test](output/ch11_reflection.png) + +![Chapter 11 refraction rendering test](output/ch11_refraction.png) + +![Chapter 11 rendering test](output/ch11_test.png) \ No newline at end of file diff --git a/output/ch11_refraction.png b/output/ch11_refraction.png new file mode 100644 index 0000000..06decb4 Binary files /dev/null and b/output/ch11_refraction.png differ diff --git a/output/ch11_test.png b/output/ch11_test.png new file mode 100644 index 0000000..e28b305 Binary files /dev/null and b/output/ch11_test.png differ diff --git a/source/include/intersection.h b/source/include/intersection.h index d4ebc6a..aaa0897 100644 --- a/source/include/intersection.h +++ b/source/include/intersection.h @@ -23,6 +23,32 @@ struct Computation object(object), t(t), hitPoint(point), eyeVector(eyev), normalVector(normalv), inside(inside), overHitPoint(overHitP), underHitPoint(underHitP), reflectVector(reflectV), n1(n1), n2(n2) { }; + double schlick() + { + /* Find the cos of the angle betzeen the eye and normal vector */ + double cos = this->eyeVector.dot(this->normalVector); + double r0; + /* Total internal reflection can only occur when n1 > n2 */ + if (this->n1 > this->n2) + { + double n, sin2_t; + n = this->n1 / this->n2; + sin2_t = (n * n) * (1.0 - (cos * cos)); + if (sin2_t > 1.0) + { + return 1.0; + } + /* Compute the cos of theta */ + cos = sqrt(1.0 - sin2_t); + } + + + r0 = ((this->n1 - this->n2) / (this->n1 + this->n2)); + r0 = r0 * r0; + + return r0 + (1 - r0) * ((1 - cos)*(1 - cos)*(1 - cos)*(1 - cos)*(1 - cos)); + }; + Shape *object; double t; Tuple hitPoint; diff --git a/source/world.cpp b/source/world.cpp index d4c2c8c..a75315c 100644 --- a/source/world.cpp +++ b/source/world.cpp @@ -104,6 +104,14 @@ Tuple World::shadeHit(Computation comps, uint32_t depthCount) Tuple reflected = this->reflectColour(comps, depthCount); Tuple refracted = this->refractedColour(comps, depthCount); + if ((comps.object->material.reflective > 0) && (comps.object->material.transparency > 0)) + { + double reflectance = comps.schlick(); + + return surface + reflected * reflectance + refracted * (1 - reflectance); + + } + return surface + reflected + refracted; } diff --git a/tests/ch11_refraction.cpp b/tests/ch11_refraction.cpp index 47fe47f..34ac730 100644 --- a/tests/ch11_refraction.cpp +++ b/tests/ch11_refraction.cpp @@ -37,6 +37,7 @@ int main() Sphere glassBall = Sphere(); glassBall.material.shininess = 300; glassBall.material.transparency = 1; + glassBall.material.reflective = 1; glassBall.material.refractiveIndex = 1.52; glassBall.material.diffuse = 0.1; w.addObject(&glassBall); @@ -45,6 +46,7 @@ int main() airBall.setTransform(scaling(0.5, 0.5, 0.5)); airBall.material.shininess = 300; airBall.material.transparency = 1; + airBall.material.reflective = 1; airBall.material.refractiveIndex = 1.0009; airBall.material.diffuse = 0.1; w.addObject(&airBall); @@ -54,7 +56,7 @@ int main() w.addLight(&light); /* Set the camera */ - Camera camera = Camera(1000, 1000, M_PI / 3); + Camera camera = Camera(100, 100, M_PI / 3); camera.setTransform(viewTransform(Point(0, 2.5, 0), Point(0, 0, 0), diff --git a/tests/ch11_test.cpp b/tests/ch11_test.cpp index 955386e..30e27bc 100644 --- a/tests/ch11_test.cpp +++ b/tests/ch11_test.cpp @@ -145,7 +145,7 @@ int main() w.addLight(&light); /* Set the camera */ - Camera camera = Camera(800, 400, 1.152); + Camera camera = Camera(400, 100, 1.152); camera.setTransform(viewTransform(Point(-2.6, 1.5, -3.9), Point(-0.6, 1, -0.8), Vector(0, 1, 0))); diff --git a/tests/intersect_test.cpp b/tests/intersect_test.cpp index 2beed0c..0998b36 100644 --- a/tests/intersect_test.cpp +++ b/tests/intersect_test.cpp @@ -253,4 +253,53 @@ TEST(IntersectTest, The_under_point_is_offset_below_the_surface) ASSERT_TRUE(double_equal(comps.underHitPoint.z, getEpsilon() / 2)); ASSERT_LT(comps.hitPoint.z, comps.underHitPoint.z); +} + +TEST(IntersectTest, The_Schlick_approximation_under_total_internal_reflection) +{ + GlassSphere shape = GlassSphere(); + + Ray r = Ray(Point(0, 0, sqrt(2)/2), Vector(0, 1, 0)); + Intersect xs = Intersect(); + xs.add(Intersection(-sqrt(2)/2, &shape)); + xs.add(Intersection(sqrt(2)/2, &shape)); + + Computation comps = xs[1].prepareComputation(r, &xs); + double reflectance = comps.schlick(); + + ASSERT_EQ(reflectance, 1.0); +} + +TEST(IntersectTest, The_Schlick_approximation_with_a_perpendicular_viewing_angle) +{ + GlassSphere shape = GlassSphere(); + + Ray r = Ray(Point(0, 0, 0), Vector(0, 1, 0)); + Intersect xs = Intersect(); + xs.add(Intersection(-1, &shape)); + xs.add(Intersection(1, &shape)); + + Computation comps = xs[1].prepareComputation(r, &xs); + double reflectance = comps.schlick(); + + ASSERT_TRUE(double_equal(reflectance, 0.04)); +} + +TEST(IntersectTest, The_Schlick_approximation_with_small_angle_and_n2_gt_n1) +{ + GlassSphere shape = GlassSphere(); + + Ray r = Ray(Point(0, 0.99, -2), Vector(0, 0, 1)); + Intersect xs = Intersect(); + xs.add(Intersection(1.8589, &shape)); + + Computation comps = xs[0].prepareComputation(r, &xs); + double reflectance = comps.schlick(); + + /* Temporary lower the precision */ + set_equal_precision(0.00001); + + ASSERT_TRUE(double_equal(reflectance, 0.48873)); + + set_equal_precision(FLT_EPSILON); } \ No newline at end of file diff --git a/tests/world_test.cpp b/tests/world_test.cpp index 2752709..b1b635b 100644 --- a/tests/world_test.cpp +++ b/tests/world_test.cpp @@ -391,5 +391,39 @@ TEST(WorldTest, Shade_hit_with_a_transparent_material) ASSERT_EQ(c, Colour(0.93642, 0.68642, 0.68642)); + set_equal_precision(FLT_EPSILON); +} + +TEST(WorldTest, Shade_hit_with_a_reflective_transparent_material) +{ + World w = DefaultWorld(); + + Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2)); + + Plane floor = Plane(); + floor.setTransform(translation(0, -1, 0)); + floor.material.transparency = 0.5; + floor.material.reflective = 0.5; + floor.material.refractiveIndex = 1.5; + w.addObject(&floor); + + Sphere ball = Sphere(); + ball.material.colour = Colour(1, 0, 0); + ball.material.ambient = 0.5; + ball.setTransform(translation(0, -3.5, -0.5)); + w.addObject(&ball); + + Intersect xs = Intersect(); + xs.add(Intersection(sqrt(2), &floor)); + + Computation comps = xs[0].prepareComputation(r, &xs); + + Tuple c = w.shadeHit(comps, 5); + + /* Temporary lower the precision */ + set_equal_precision(0.00001); + + ASSERT_EQ(c, Colour(0.93391, 0.69643, 0.69243)); + set_equal_precision(FLT_EPSILON); } \ No newline at end of file