Working on refraction & transparency.
Lots of work left to do!
This commit is contained in:
@@ -13,13 +13,14 @@
|
|||||||
#include <ray.h>
|
#include <ray.h>
|
||||||
|
|
||||||
class Shape;
|
class Shape;
|
||||||
|
class Intersect;
|
||||||
|
|
||||||
struct Computation
|
struct Computation
|
||||||
{
|
{
|
||||||
Computation(Shape *object, double t, Tuple point, Tuple eyev, Tuple normalv, Tuple overHitP,
|
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),
|
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;
|
Shape *object;
|
||||||
double t;
|
double t;
|
||||||
@@ -29,6 +30,9 @@ struct Computation
|
|||||||
Tuple normalVector;
|
Tuple normalVector;
|
||||||
Tuple reflectVector;
|
Tuple reflectVector;
|
||||||
|
|
||||||
|
double n1;
|
||||||
|
double n2;
|
||||||
|
|
||||||
bool inside;
|
bool inside;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -42,7 +46,7 @@ public:
|
|||||||
Intersection(double t, Shape *object) : t(t), object(object) { };
|
Intersection(double t, Shape *object) : t(t), object(object) { };
|
||||||
bool nothing() { return (this->object == nullptr); };
|
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)); };
|
bool operator==(const Intersection &b) const { return ((this->t == b.t) && (this->object == b.object)); };
|
||||||
};
|
};
|
||||||
|
|||||||
94
source/include/list.h
Normal file
94
source/include/list.h
Normal file
@@ -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
|
||||||
@@ -25,11 +25,14 @@ public:
|
|||||||
double specular;
|
double specular;
|
||||||
double shininess;
|
double shininess;
|
||||||
double reflective;
|
double reflective;
|
||||||
|
double transparency;
|
||||||
|
double refractiveIndex;
|
||||||
|
|
||||||
Pattern *pattern;
|
Pattern *pattern;
|
||||||
|
|
||||||
public:
|
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);
|
Colour lighting(Light light, Tuple point, Tuple eyeVector, Tuple normalVector, Shape *hitObject, bool inShadow = false);
|
||||||
|
|
||||||
@@ -40,4 +43,5 @@ public:
|
|||||||
(this->colour == b.colour); };
|
(this->colour == b.colour); };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif /* DORAYME_MATERIAL_H */
|
#endif /* DORAYME_MATERIAL_H */
|
||||||
|
|||||||
@@ -24,4 +24,11 @@ public:
|
|||||||
/* All sphere are at (0, 0, 0) and radius 1 in the object space */
|
/* 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 */
|
#endif /* DORAYME_SPHERE_H */
|
||||||
|
|||||||
@@ -8,9 +8,13 @@
|
|||||||
*/
|
*/
|
||||||
#include <intersection.h>
|
#include <intersection.h>
|
||||||
#include <shape.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 hitP = r.position(this->t);
|
||||||
Tuple normalV = this->object->normalAt(hitP);
|
Tuple normalV = this->object->normalAt(hitP);
|
||||||
Tuple eyeV = -r.direction;
|
Tuple eyeV = -r.direction;
|
||||||
@@ -25,6 +29,46 @@ Computation Intersection::prepareComputation(Ray r)
|
|||||||
Tuple overHitP = hitP + normalV * getEpsilon();
|
Tuple overHitP = hitP + normalV * getEpsilon();
|
||||||
Tuple reflectV = r.direction.reflect(normalV);
|
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,
|
return Computation(this->object,
|
||||||
this->t,
|
this->t,
|
||||||
hitP,
|
hitP,
|
||||||
@@ -32,5 +76,7 @@ Computation Intersection::prepareComputation(Ray r)
|
|||||||
normalV,
|
normalV,
|
||||||
overHitP,
|
overHitP,
|
||||||
inside,
|
inside,
|
||||||
reflectV);
|
reflectV,
|
||||||
|
n1,
|
||||||
|
n2);
|
||||||
}
|
}
|
||||||
@@ -108,7 +108,8 @@ Tuple World::shadeHit(Computation comps, uint32_t depthCount)
|
|||||||
|
|
||||||
Tuple World::colourAt(Ray r, 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())
|
if (hit.nothing())
|
||||||
{
|
{
|
||||||
@@ -116,7 +117,7 @@ Tuple World::colourAt(Ray r, uint32_t depthCount)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return this->shadeHit(hit.prepareComputation(r), depthCount);
|
return this->shadeHit(hit.prepareComputation(r, &allHits), depthCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -201,4 +201,41 @@ TEST(IntersectTest, Precomputing_the_reflection_vector)
|
|||||||
Computation comps = i.prepareComputation(r);
|
Computation comps = i.prepareComputation(r);
|
||||||
|
|
||||||
ASSERT_EQ(comps.reflectVector, Vector(0, sqrt(2) / 2, sqrt(2) / 2));
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -114,3 +114,11 @@ TEST(MaterialTest, Reflectivity_for_the_default_material)
|
|||||||
|
|
||||||
ASSERT_EQ(m.reflective, 0);
|
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);
|
||||||
|
}
|
||||||
@@ -198,4 +198,13 @@ TEST(SphereTest, A_sphere_may_be_assigned_a_material)
|
|||||||
s.setMaterial(m);
|
s.setMaterial(m);
|
||||||
|
|
||||||
ASSERT_EQ(s.material, 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);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user