diff --git a/source/include/intersection.h b/source/include/intersection.h index bb98a3a..8491efc 100644 --- a/source/include/intersection.h +++ b/source/include/intersection.h @@ -13,13 +13,14 @@ #include 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)); }; }; diff --git a/source/include/list.h b/source/include/list.h new file mode 100644 index 0000000..b0a0e00 --- /dev/null +++ b/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 + +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 diff --git a/source/include/material.h b/source/include/material.h index 6dfb65f..2faad50 100644 --- a/source/include/material.h +++ b/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 */ diff --git a/source/include/sphere.h b/source/include/sphere.h index 7e0ff9d..3d0d5eb 100644 --- a/source/include/sphere.h +++ b/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 */ diff --git a/source/intersection.cpp b/source/intersection.cpp index e8b8aa6..0bbff19 100644 --- a/source/intersection.cpp +++ b/source/intersection.cpp @@ -8,9 +8,13 @@ */ #include #include +#include -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); } \ No newline at end of file diff --git a/source/world.cpp b/source/world.cpp index 38c9b1f..7e28720 100644 --- a/source/world.cpp +++ b/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); } } diff --git a/tests/intersect_test.cpp b/tests/intersect_test.cpp index f8a20a4..9f1228c 100644 --- a/tests/intersect_test.cpp +++ b/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)); -} \ No newline at end of file +} + +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 +} diff --git a/tests/material_test.cpp b/tests/material_test.cpp index 9841991..a2b6ca0 100644 --- a/tests/material_test.cpp +++ b/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); +} \ No newline at end of file diff --git a/tests/sphere_test.cpp b/tests/sphere_test.cpp index 5328f41..b610209 100644 --- a/tests/sphere_test.cpp +++ b/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); } \ No newline at end of file