Browse Source

Refraction is fully there, with magic fresnel!

Godzil 4 years ago
parent
commit
51a6bbebb9

+ 5 - 1
README.md

@@ -40,4 +40,8 @@ From Chapter 10:
 
 From Chapter 11:
 
-![Chapter 11 reflections rendering test](output/ch11_reflection.png)
+![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)

BIN
output/ch11_refraction.png


BIN
output/ch11_test.png


+ 26 - 0
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;

+ 8 - 0
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;
 }
 

+ 3 - 1
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),

+ 1 - 1
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)));

+ 49 - 0
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);
 }

+ 34 - 0
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);
 }