Finally! We have reflections!
This commit is contained in:
@@ -16,8 +16,10 @@ class Shape;
|
||||
|
||||
struct Computation
|
||||
{
|
||||
Computation(Shape *object, double t, Tuple point, Tuple eyev, Tuple normalv, Tuple overHitP, bool inside) :
|
||||
object(object), t(t), hitPoint(point), eyeVector(eyev), normalVector(normalv), inside(inside), overHitPoint(overHitP) { };
|
||||
Computation(Shape *object, double t, Tuple point, Tuple eyev, Tuple normalv, Tuple overHitP,
|
||||
bool inside, Tuple reflectV = Vector(0, 0, 0)) :
|
||||
object(object), t(t), hitPoint(point), eyeVector(eyev), normalVector(normalv), inside(inside),
|
||||
overHitPoint(overHitP), reflectVector(reflectV) { };
|
||||
|
||||
Shape *object;
|
||||
double t;
|
||||
@@ -25,6 +27,7 @@ struct Computation
|
||||
Tuple overHitPoint;
|
||||
Tuple eyeVector;
|
||||
Tuple normalVector;
|
||||
Tuple reflectVector;
|
||||
|
||||
bool inside;
|
||||
};
|
||||
|
||||
@@ -24,11 +24,12 @@ public:
|
||||
double diffuse;
|
||||
double specular;
|
||||
double shininess;
|
||||
double reflective;
|
||||
|
||||
Pattern *pattern;
|
||||
|
||||
public:
|
||||
Material() : colour(Colour(1, 1, 1)), ambient(0.1), diffuse(0.9), specular(0.9), shininess(200), pattern(nullptr) {};
|
||||
Material() : colour(Colour(1, 1, 1)), ambient(0.1), diffuse(0.9), specular(0.9), shininess(200), reflective(0.0), pattern(nullptr) {};
|
||||
|
||||
Colour lighting(Light light, Tuple point, Tuple eyeVector, Tuple normalVector, Shape *hitObject, bool inShadow = false);
|
||||
|
||||
|
||||
@@ -40,11 +40,14 @@ public:
|
||||
bool objectIsIn(Shape &s);
|
||||
|
||||
Shape *getObject(int i) { return this->objectList[i]; };
|
||||
Light *getLight(int i) { return this->lightList[i]; };
|
||||
|
||||
Tuple shadeHit(Computation comps);;
|
||||
Tuple colourAt(Ray r);
|
||||
Tuple shadeHit(Computation comps, uint32_t depthCount = 4);
|
||||
Tuple colourAt(Ray r, uint32_t depthCount = 4);
|
||||
bool isShadowed(Tuple point);
|
||||
|
||||
Colour reflectColour(Computation comps, uint32_t depthCount = 4);
|
||||
|
||||
Intersect intersect(Ray r);
|
||||
|
||||
};
|
||||
|
||||
@@ -23,6 +23,7 @@ Computation Intersection::prepareComputation(Ray r)
|
||||
}
|
||||
|
||||
Tuple overHitP = hitP + normalV * getEpsilon();
|
||||
Tuple reflectV = r.direction.reflect(normalV);
|
||||
|
||||
return Computation(this->object,
|
||||
this->t,
|
||||
@@ -30,5 +31,6 @@ Computation Intersection::prepareComputation(Ray r)
|
||||
eyeV,
|
||||
normalV,
|
||||
overHitP,
|
||||
inside);
|
||||
inside,
|
||||
reflectV);
|
||||
}
|
||||
@@ -92,17 +92,21 @@ Intersect World::intersect(Ray r)
|
||||
return ret;
|
||||
}
|
||||
|
||||
Tuple World::shadeHit(Computation comps)
|
||||
Tuple World::shadeHit(Computation comps, uint32_t depthCount)
|
||||
{
|
||||
/* TODO: Add support for more than one light */
|
||||
|
||||
bool isThereAnObstacle = this->isShadowed(comps.overHitPoint);
|
||||
|
||||
return comps.object->material.lighting(*this->lightList[0], comps.overHitPoint, comps.eyeVector,
|
||||
Tuple surface = comps.object->material.lighting(*this->lightList[0], comps.overHitPoint, comps.eyeVector,
|
||||
comps.normalVector, comps.object, isThereAnObstacle);
|
||||
|
||||
Tuple reflected = this->reflectColour(comps, depthCount);
|
||||
|
||||
return surface + reflected;
|
||||
}
|
||||
|
||||
Tuple World::colourAt(Ray r)
|
||||
Tuple World::colourAt(Ray r, uint32_t depthCount)
|
||||
{
|
||||
Intersection hit = this->intersect(r).hit();
|
||||
|
||||
@@ -112,7 +116,7 @@ Tuple World::colourAt(Ray r)
|
||||
}
|
||||
else
|
||||
{
|
||||
return this->shadeHit(hit.prepareComputation(r));
|
||||
return this->shadeHit(hit.prepareComputation(r), depthCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,3 +138,21 @@ bool World::isShadowed(Tuple point)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Colour World::reflectColour(Computation comps, uint32_t depthCount)
|
||||
{
|
||||
if ((depthCount == 0) || (comps.object->material.reflective == 0))
|
||||
{
|
||||
return Colour(0, 0, 0);
|
||||
}
|
||||
|
||||
/* So it is reflective, even just a bit. Let'sr reflect the ray! */
|
||||
Ray reflectedRay = Ray(comps.overHitPoint, comps.reflectVector);
|
||||
|
||||
Tuple hitColour = this->colourAt(reflectedRay, depthCount - 1);
|
||||
hitColour = hitColour * comps.object->material.reflective;
|
||||
|
||||
return Colour(hitColour.x, hitColour.y, hitColour.z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -44,8 +44,14 @@ target_include_directories(ch10_test PUBLIC ../source/include)
|
||||
target_sources(ch10_test PRIVATE ch10_test.cpp)
|
||||
target_link_libraries(ch10_test rayonnement)
|
||||
|
||||
add_executable(ch11_reflection)
|
||||
target_include_directories(ch11_reflection PUBLIC ../source/include)
|
||||
target_sources(ch11_reflection PRIVATE ch11_reflection.cpp)
|
||||
target_link_libraries(ch11_reflection rayonnement)
|
||||
|
||||
add_test(NAME Chapter05_Test COMMAND $<TARGET_FILE:ch5_test>)
|
||||
add_test(NAME Chapter06_Test COMMAND $<TARGET_FILE:ch6_test>)
|
||||
add_test(NAME Chapter07_Test COMMAND $<TARGET_FILE:ch7_test>)
|
||||
add_test(NAME Chapter09_Test COMMAND $<TARGET_FILE:ch9_test>)
|
||||
add_test(NAME Chapter10_Test COMMAND $<TARGET_FILE:ch10_test>)
|
||||
add_test(NAME Chapter11_Reflection COMMAND $<TARGET_FILE:ch11_reflection>)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <intersect.h>
|
||||
#include <intersection.h>
|
||||
#include <sphere.h>
|
||||
#include <plane.h>
|
||||
#include <transformation.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@@ -189,4 +190,15 @@ TEST(IntersectTest, The_hit_should_offset_the_point)
|
||||
|
||||
ASSERT_LT(comps.overHitPoint.z, -getEpsilon() / 2);
|
||||
ASSERT_GT(comps.hitPoint.z, comps.overHitPoint.z);
|
||||
}
|
||||
|
||||
TEST(IntersectTest, Precomputing_the_reflection_vector)
|
||||
{
|
||||
Plane s = Plane();
|
||||
Ray r = Ray(Point(0, 1, -1), Vector(0, -sqrt(2) / 2, sqrt(2) / 2));
|
||||
Intersection i = Intersection(sqrt(2), &s);
|
||||
|
||||
Computation comps = i.prepareComputation(r);
|
||||
|
||||
ASSERT_EQ(comps.reflectVector, Vector(0, sqrt(2) / 2, sqrt(2) / 2));
|
||||
}
|
||||
@@ -106,4 +106,11 @@ TEST(MaterialTest, Lighting_with_the_surface_in_shadow)
|
||||
Colour result = m.lighting(light, position, eyev, normalv, &t, inShadow);
|
||||
|
||||
ASSERT_EQ(result, Colour(0.1, 0.1, 0.1));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MaterialTest, Reflectivity_for_the_default_material)
|
||||
{
|
||||
Material m = Material();
|
||||
|
||||
ASSERT_EQ(m.reflective, 0);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <worldbuilder.h>
|
||||
#include <math.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <plane.h>
|
||||
|
||||
|
||||
TEST(WorldTest, Creating_a_world)
|
||||
@@ -167,4 +168,109 @@ TEST(WorldTest, Shade_hit_is_given_an_intersection_in_shadow)
|
||||
Tuple c = w.shadeHit(comps);
|
||||
|
||||
ASSERT_EQ(c, Colour(0.1, 0.1, 0.1));
|
||||
};
|
||||
};
|
||||
|
||||
TEST(WorldTest, The_reflected_colour_for_a_non_reflective_material)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Ray r = Ray(Point(0, 0, 0), Vector(0, 0, 1));
|
||||
|
||||
Shape *shape = w.getObject(1); /* The second object */
|
||||
shape->material.ambient = 1; /* We use this to get a predictable colour */
|
||||
|
||||
Intersection i = Intersection(1, shape);
|
||||
|
||||
Computation comps = i.prepareComputation(r);
|
||||
Colour colour = w.reflectColour(comps);
|
||||
|
||||
ASSERT_EQ(colour, Colour(0, 0, 0));
|
||||
}
|
||||
|
||||
TEST(WorldTest, The_reflected_colour_for_a_reflective_material)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Plane shape = Plane();
|
||||
shape.material.reflective = 0.5;
|
||||
shape.setTransform(translation(0, -1, 0));
|
||||
w.addObject(&shape);
|
||||
|
||||
Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2));
|
||||
|
||||
Intersection i = Intersection(sqrt(2), &shape);
|
||||
|
||||
Computation comps = i.prepareComputation(r);
|
||||
Colour colour = w.reflectColour(comps);
|
||||
|
||||
/* Temporary lower the precision */
|
||||
set_equal_precision(0.00002);
|
||||
|
||||
ASSERT_EQ(colour, Colour(0.19032, 0.2379, 0.14274));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
|
||||
TEST(WorldTest, Shade_hit_with_a_reflective_material)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Plane shape = Plane();
|
||||
shape.material.reflective = 0.5;
|
||||
shape.setTransform(translation(0, -1, 0));
|
||||
w.addObject(&shape);
|
||||
|
||||
Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2));
|
||||
|
||||
Intersection i = Intersection(sqrt(2), &shape);
|
||||
|
||||
Computation comps = i.prepareComputation(r);
|
||||
Tuple colour = w.shadeHit(comps);
|
||||
|
||||
/* Temporary lower the precision */
|
||||
set_equal_precision(0.00005);
|
||||
|
||||
ASSERT_EQ(colour, Colour(0.87677, 0.92436, 0.82918));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
|
||||
TEST(WorldTest, Colour_at_with_mutually_reflective_surfaces)
|
||||
{
|
||||
World w = World();
|
||||
|
||||
Light l = Light(POINT_LIGHT, Point(0, 0, 0), Colour(1, 1, 1));
|
||||
|
||||
w.addLight(&l);
|
||||
|
||||
Plane lower = Plane();
|
||||
lower.material.reflective = 1;
|
||||
lower.setTransform(translation(0, -1, 0));
|
||||
|
||||
Plane higher = Plane();
|
||||
higher.material.reflective = 1;
|
||||
higher.setTransform(translation(0, 1, 0));
|
||||
|
||||
w.addObject(&lower);
|
||||
w.addObject(&higher);
|
||||
|
||||
Ray r = Ray(Point(0, 0, 0), Vector(0, 1, 0));
|
||||
|
||||
/* It should just exit, we don't care about the actual colour */
|
||||
w.colourAt(r);
|
||||
}
|
||||
|
||||
TEST(WorldTest, The_reflected_colour_at_the_maximum_recursion_depth)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Plane shape = Plane();
|
||||
shape.material.reflective = 0.5;
|
||||
shape.setTransform(translation(0, -1, 0));
|
||||
w.addObject(&shape);
|
||||
|
||||
Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2));
|
||||
Intersection i = Intersection(sqrt(2), &shape);
|
||||
|
||||
Computation comps = i.prepareComputation(r);
|
||||
Tuple colour = w.reflectColour(comps, 0);
|
||||
|
||||
/* Temporary lower the precision */
|
||||
ASSERT_EQ(colour, Colour(0, 0, 0));
|
||||
}
|
||||
Reference in New Issue
Block a user