瀏覽代碼

Working on refraction & transparency.
Lots of work left to do!

Godzil 4 年之前
父節點
當前提交
df52cb36db

+ 7 - 3
source/include/intersection.h

@@ -13,13 +13,14 @@
 #include <ray.h>
 
 class Shape;
+class Intersect;
 
 struct Computation
 {
     Computation(Shape *object, double t, Tuple point, Tuple eyev, Tuple normalv, Tuple overHitP,
-                bool inside, Tuple reflectV = Vector(0, 0, 0)) :
+                bool inside, Tuple reflectV = Vector(0, 0, 0), double n1 = 1.0, double n2 = 1.0) :
           object(object), t(t), hitPoint(point), eyeVector(eyev), normalVector(normalv), inside(inside),
-          overHitPoint(overHitP),  reflectVector(reflectV) { };
+          overHitPoint(overHitP),  reflectVector(reflectV), n1(n1), n2(n2) { };
 
     Shape *object;
     double t;
@@ -29,6 +30,9 @@ struct Computation
     Tuple normalVector;
     Tuple reflectVector;
 
+    double n1;
+    double n2;
+
     bool inside;
 };
 
@@ -42,7 +46,7 @@ public:
     Intersection(double t, Shape *object) : t(t), object(object) { };
     bool nothing() { return (this->object == nullptr); };
 
-    Computation prepareComputation(Ray r);
+    Computation prepareComputation(Ray r, Intersect *xs = nullptr);
 
     bool operator==(const Intersection &b) const { return ((this->t == b.t) && (this->object == b.object)); };
 };

+ 94 - 0
source/include/list.h

@@ -0,0 +1,94 @@
+/*
+ *  DoRayMe - a quick and dirty Raytracer
+ *  List header
+ *
+ *  Created by Manoël Trapier
+ *  Copyright (c) 2020 986-Studio.
+ *
+ */
+#ifndef DORAYME_LIST_H
+#define DORAYME_LIST_H
+
+#include <shape.h>
+
+struct ChainList
+{
+    Shape *shape;
+    ChainList *next;
+};
+
+class List
+{
+private:
+    ChainList *head;
+    ChainList *tail;
+    uint32_t count;
+public:
+    List() : head(nullptr), tail(nullptr), count(0) { };
+    ~List()
+    {
+        ChainList *p = this->head;
+        if (p == nullptr) { return; }
+
+        /* clear up the list */
+    }
+
+    Shape *last()
+    {
+        ChainList *p = this->tail;
+        if (p == nullptr) { return nullptr; }
+        return p->shape;
+    }
+
+    void remove(Shape *s)
+    {
+        ChainList *p = this->head;
+        if (p == nullptr) { return; }
+        while(p->next != nullptr)
+        {
+            if (p->next->shape == s)
+            {
+                this->count --;
+                p->next = p->next->next;
+                free(p->next);
+                return;
+            }
+            p = p->next;
+        }
+    }
+
+    void append(Shape *s)
+    {
+        ChainList *theNew = (ChainList *)calloc(1, sizeof(ChainList));
+
+        theNew->shape = s;
+
+        ChainList *p = this->tail;
+        tail = theNew;
+
+        if (p != nullptr) { p->next = theNew; }
+        else { head = theNew; } /* If the tail is empty, it mean the list IS empty. */
+
+        this->count ++;
+    }
+
+    bool isEmpty()
+    {
+        return (this->count == 0);
+    }
+
+    bool doesInclude(Shape *s)
+    {
+        ChainList *p = this->head;
+
+        while(p != nullptr)
+        {
+            if (p->shape == s) { return true; }
+            p = p->next;
+        }
+
+        return false;
+    }
+};
+
+#endif //DORAYME_LIST_H

+ 5 - 1
source/include/material.h

@@ -25,11 +25,14 @@ public:
     double specular;
     double shininess;
     double reflective;
+    double transparency;
+    double refractiveIndex;
 
     Pattern *pattern;
 
 public:
-    Material() : colour(Colour(1, 1, 1)), ambient(0.1), diffuse(0.9), specular(0.9), shininess(200), reflective(0.0), pattern(nullptr) {};
+    Material() : colour(Colour(1, 1, 1)), ambient(0.1), diffuse(0.9), specular(0.9), shininess(200),
+           reflective(0.0), transparency(0.0), refractiveIndex(1.0), pattern(nullptr) {};
 
     Colour lighting(Light light, Tuple point, Tuple eyeVector, Tuple normalVector, Shape *hitObject, bool inShadow = false);
 
@@ -40,4 +43,5 @@ public:
                                                       (this->colour == b.colour); };
 };
 
+
 #endif /* DORAYME_MATERIAL_H */

+ 7 - 0
source/include/sphere.h

@@ -24,4 +24,11 @@ public:
     /* All sphere are at (0, 0, 0) and radius 1 in the object space */
 };
 
+/* Mostly for test purposes */
+class GlassSphere : public Sphere
+{
+public:
+    GlassSphere() : Sphere() { this->material.transparency = 1.0; this->material.refractiveIndex = 1.5; };
+};
+
 #endif /* DORAYME_SPHERE_H */

+ 48 - 2
source/intersection.cpp

@@ -8,9 +8,13 @@
  */
 #include <intersection.h>
 #include <shape.h>
+#include <list.h>
 
-Computation Intersection::prepareComputation(Ray r)
+Computation Intersection::prepareComputation(Ray r, Intersect *xs)
 {
+    double n1 = 1.0;
+    double n2 = 1.0;
+
     Tuple hitP = r.position(this->t);
     Tuple normalV = this->object->normalAt(hitP);
     Tuple eyeV = -r.direction;
@@ -25,6 +29,46 @@ Computation Intersection::prepareComputation(Ray r)
     Tuple overHitP = hitP + normalV * getEpsilon();
     Tuple reflectV = r.direction.reflect(normalV);
 
+    if (xs != nullptr)
+    {
+        List containers;
+        int j, k;
+        Intersection hit = xs->hit();
+
+        for(j = 0; j < xs->count(); j++)
+        {
+            Intersection i = (*xs)[j];
+            if (hit == i)
+            {
+                if (!containers.isEmpty())
+                {
+                    n1 = containers.last()->material.refractiveIndex;
+                }
+            }
+
+            if (containers.doesInclude(i.object))
+            {
+                containers.remove(i.object);
+            }
+            else
+            {
+                containers.append(i.object);
+            }
+
+            if (hit == i)
+            {
+                if (!containers.isEmpty())
+                {
+                    Shape *cur = containers.last();
+                    n2 = cur->material.refractiveIndex;
+                }
+
+                /* End the loop */
+                break;
+            }
+        }
+    }
+
     return Computation(this->object,
                        this->t,
                        hitP,
@@ -32,5 +76,7 @@ Computation Intersection::prepareComputation(Ray r)
                        normalV,
                        overHitP,
                        inside,
-                       reflectV);
+                       reflectV,
+                       n1,
+                       n2);
 }

+ 3 - 2
source/world.cpp

@@ -108,7 +108,8 @@ Tuple World::shadeHit(Computation comps, uint32_t depthCount)
 
 Tuple World::colourAt(Ray r, uint32_t depthCount)
 {
-    Intersection hit = this->intersect(r).hit();
+    Intersect allHits = this->intersect(r);
+    Intersection hit = allHits.hit();
 
     if (hit.nothing())
     {
@@ -116,7 +117,7 @@ Tuple World::colourAt(Ray r, uint32_t depthCount)
     }
     else
     {
-        return this->shadeHit(hit.prepareComputation(r), depthCount);
+        return this->shadeHit(hit.prepareComputation(r, &allHits), depthCount);
     }
 }
 

+ 38 - 1
tests/intersect_test.cpp

@@ -201,4 +201,41 @@ TEST(IntersectTest, Precomputing_the_reflection_vector)
     Computation comps = i.prepareComputation(r);
 
     ASSERT_EQ(comps.reflectVector, Vector(0, sqrt(2) / 2, sqrt(2) / 2));
-}
+}
+
+TEST(IntersectTest, Finding_n1_and_n2_at_various_intersections)
+{
+#if 0
+    int i;
+    double n1_res[6] = { 1.0, 1.5, 2.0, 2.5, 2.5, 1.5 };
+    double n2_res[6] = { 1.5, 2.0, 2.5, 2.5, 1.5, 1.0 };
+
+    GlassSphere A = GlassSphere();
+    A.setTransform(scaling(2, 2, 2));
+    A.material.refractiveIndex = 1.5;
+
+    GlassSphere B = GlassSphere();
+    B.setTransform(translation(0, 0, -0.25));
+    B.material.refractiveIndex = 2.0;
+
+    GlassSphere C = GlassSphere();
+    C.setTransform(translation(0, 0, 0.25));
+    C.material.refractiveIndex = 2.5;
+
+    Ray r = Ray(Point(0, 0, -4), Vector(0, 0, 1));
+    Intersect xs = Intersect();
+    xs.add(Intersection(2.0, &A));
+    xs.add(Intersection(2.75, &B));
+    xs.add(Intersection(3.25, &C));
+    xs.add(Intersection(4.75, &B));
+    xs.add(Intersection(5.25, &C));
+    xs.add(Intersection(6, &A));
+
+    for(i = 0; i < xs.count(); i++)
+    {
+        Computation comps = xs[i].prepareComputation(r, &xs);
+        ASSERT_EQ(comps.n1, n1_res[i]);
+        ASSERT_EQ(comps.n2, n2_res[i]);
+    }
+#endif
+}

+ 8 - 0
tests/material_test.cpp

@@ -114,3 +114,11 @@ TEST(MaterialTest, Reflectivity_for_the_default_material)
 
     ASSERT_EQ(m.reflective, 0);
 }
+
+TEST(MaterialTest, Transparency_and_refractive_index_for_the_default_material)
+{
+    Material m = Material();
+
+    ASSERT_EQ(m.transparency, 0.0);
+    ASSERT_EQ(m.refractiveIndex, 1.0);
+}

+ 9 - 0
tests/sphere_test.cpp

@@ -198,4 +198,13 @@ TEST(SphereTest, A_sphere_may_be_assigned_a_material)
     s.setMaterial(m);
 
     ASSERT_EQ(s.material, m);
+}
+
+TEST(SphereTest, A_helper_for_producing_a_sphere_with_a_glassy_material)
+{
+    GlassSphere s = GlassSphere();
+
+    ASSERT_EQ(s.transformMatrix, Matrix4().identity());
+    ASSERT_EQ(s.material.transparency, 1.0);
+    ASSERT_EQ(s.material.refractiveIndex, 1.5);
 }