diff --git a/source/include/shape.h b/source/include/shape.h index a2874ad..9750dde 100644 --- a/source/include/shape.h +++ b/source/include/shape.h @@ -29,6 +29,10 @@ class Shape private: ShapeType type; +private: + virtual Intersect localIntersect(Ray r) = 0; + virtual Tuple localNormalAt(Tuple point) = 0; + public: Matrix transformMatrix; Matrix inverseTransform; @@ -37,8 +41,8 @@ public: public: Shape(ShapeType = SHAPE_NONE); - virtual Intersect intersect(Ray r); - virtual Tuple normalAt(Tuple point); + Intersect intersect(Ray r); + Tuple normalAt(Tuple point); void setTransform(Matrix transform); void setMaterial(Material material) { this->material = material; }; diff --git a/source/include/sphere.h b/source/include/sphere.h index d94026c..7e0ff9d 100644 --- a/source/include/sphere.h +++ b/source/include/sphere.h @@ -15,11 +15,13 @@ class Sphere : public Shape { +private: + Intersect localIntersect(Ray r); + Tuple localNormalAt(Tuple point); + public: Sphere() : Shape(SHAPE_SPHERE) { }; /* All sphere are at (0, 0, 0) and radius 1 in the object space */ - virtual Intersect intersect(Ray r); - virtual Tuple normalAt(Tuple point); }; #endif /* DORAYME_SPHERE_H */ diff --git a/source/include/testshape.h b/source/include/testshape.h new file mode 100644 index 0000000..b441f83 --- /dev/null +++ b/source/include/testshape.h @@ -0,0 +1,28 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * Test shape header + * + * Created by Manoël Trapier + * Copyright (c) 2020 986-Studio. + * + */ +#ifndef DORAYME_TESTSHAPE_H +#define DORAYME_TESTSHAPE_H + +#include +#include +#include + +class TestShape : public Shape +{ +private: + Intersect localIntersect(Ray r); + Tuple localNormalAt(Tuple point); + +public: + Ray localRay; + + TestShape(); +}; + +#endif //DORAYME_TESTSHAPE_H diff --git a/source/shapes/shape.cpp b/source/shapes/shape.cpp index 5182a08..8890a58 100644 --- a/source/shapes/shape.cpp +++ b/source/shapes/shape.cpp @@ -22,12 +22,21 @@ Shape::Shape(ShapeType type) Intersect Shape::intersect(Ray r) { - return Intersect(); + return this->localIntersect(this->invTransform(r)); }; Tuple Shape::normalAt(Tuple point) { - return Vector(0, 0, 0); + Tuple local_point = this->inverseTransform * point; + + Tuple local_normal = this->localNormalAt(local_point); + + Tuple world_normal = this->inverseTransform.transpose() * local_normal; + + /* W may get wrong, so hack it. This is perfectly normal as we are using a 4x4 matrix instead of a 3x3 */ + world_normal.w = 0; + + return world_normal.normalise(); } void Shape::setTransform(Matrix transform) diff --git a/source/shapes/sphere.cpp b/source/shapes/sphere.cpp index 9f2c526..fabede7 100644 --- a/source/shapes/sphere.cpp +++ b/source/shapes/sphere.cpp @@ -13,17 +13,15 @@ #include #include -Intersect Sphere::intersect(Ray r) +Intersect Sphere::localIntersect(Ray r) { Intersect ret; double a, b, c, discriminant; - Ray transRay = this->invTransform(r); + Tuple sphere_to_ray = r.origin - Point(0, 0, 0); - Tuple sphere_to_ray = transRay.origin - Point(0, 0, 0); - - a = transRay.direction.dot(transRay.direction); - b = 2 * transRay.direction.dot(sphere_to_ray); + a = r.direction.dot(r.direction); + b = 2 * r.direction.dot(sphere_to_ray); c = sphere_to_ray.dot(sphere_to_ray) - 1; discriminant = b * b - 4 * a * c; @@ -37,14 +35,7 @@ Intersect Sphere::intersect(Ray r) return ret; } -Tuple Sphere::normalAt(Tuple point) +Tuple Sphere::localNormalAt(Tuple point) { - Tuple object_point = this->inverseTransform * point; - Tuple object_normal = (object_point - Point(0, 0, 0)).normalise(); - Tuple world_normal = this->inverseTransform.transpose() * object_normal; - - /* W may get wrong, so hack it. This is perfectly normal as we are using a 4x4 matrix instead of a 3x3 */ - world_normal.w = 0; - - return world_normal.normalise(); + return (point - Point(0, 0, 0)).normalise(); } \ No newline at end of file diff --git a/source/shapes/testshape.cpp b/source/shapes/testshape.cpp new file mode 100644 index 0000000..6334818 --- /dev/null +++ b/source/shapes/testshape.cpp @@ -0,0 +1,25 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * Test shape implementation + * + * Created by Manoël Trapier + * Copyright (c) 2020 986-Studio. + * + */ +#include +#include + +TestShape::TestShape() : localRay(Point(0, 0, 0), Vector(0, 0, 0)) +{ +} + +Intersect TestShape::localIntersect(Ray r) +{ + this->localRay = r; + return Intersect(); +} + +Tuple TestShape::localNormalAt(Tuple point) +{ + return Vector(point.x, point.y, point.z); +} \ No newline at end of file diff --git a/tests/ray_test.cpp b/tests/ray_test.cpp index 2b94f9b..71f567e 100644 --- a/tests/ray_test.cpp +++ b/tests/ray_test.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -38,7 +39,7 @@ TEST(RayTest, Translating_a_ray) Ray r = Ray(Point(1, 2, 3), Vector(0, 1, 0)); Matrix m = translation(3, 4, 5); - Shape o = Shape(); + TestShape o = TestShape(); o.setTransform(m); @@ -53,7 +54,7 @@ TEST(RayTest, Scaling_a_ray) Ray r = Ray(Point(1, 2, 3), Vector(0, 1, 0)); Matrix m = scaling(2, 3, 4); - Shape o = Shape(); + TestShape o = TestShape(); o.setTransform(m); diff --git a/tests/shape_test.cpp b/tests/shape_test.cpp index 2f7a40a..7a516ad 100644 --- a/tests/shape_test.cpp +++ b/tests/shape_test.cpp @@ -7,19 +7,20 @@ * */ #include +#include #include #include #include TEST(ShapeTest, The_default_transformation) { - Shape s = Shape(); + TestShape s = TestShape(); ASSERT_EQ(s.transformMatrix, Matrix4().identity()); } TEST(ShapeTest, Assigning_a_transformation) { - Shape s = Shape(); + TestShape s = TestShape(); s.setTransform(translation(2, 3, 4)); @@ -28,18 +29,70 @@ TEST(ShapeTest, Assigning_a_transformation) TEST(ShapeTest, The_default_material) { - Shape s = Shape(); + TestShape s = TestShape(); ASSERT_EQ(s.material, Material()); } TEST(ShapeTest, Assigning_a_material) { - Shape s = Shape(); + TestShape s = TestShape(); Material m = Material(); m.ambient = 1; s.material = m; ASSERT_EQ(s.material, m); +} + +TEST(ShapeTest, Intersecting_a_scaled_shape_with_a_ray) +{ + Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1)); + TestShape s = TestShape(); + + s.setTransform(scaling(2, 2, 2)); + Intersect xs = s.intersect(r); + + ASSERT_EQ(s.localRay.origin, Point(0, 0, -2.5)); + ASSERT_EQ(s.localRay.direction, Vector(0, 0, 0.5)); +} + +TEST(ShapeTest, Intersecting_a_translated_shape_with_a_ray) +{ + Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1)); + TestShape s = TestShape(); + + s.setTransform(translation(5, 0, 0)); + Intersect xs = s.intersect(r); + + ASSERT_EQ(s.localRay.origin, Point(-5, 0, -5)); + ASSERT_EQ(s.localRay.direction, Vector(0, 0, 1)); +} + +TEST(ShapeTest, Computing_the_normal_on_a_translated_shape) +{ + TestShape s = TestShape(); + s.setTransform(translation(0, 1, 0)); + Tuple n = s.normalAt(Point(0, 1.70711, -0.70711)); + + /* Temporary lower the precision */ + set_equal_precision(0.00001); + + ASSERT_EQ(n, Vector(0, 0.70711, -0.70711)); + + set_equal_precision(FLT_EPSILON); +} + +TEST(ShapeTest, Computing_the_normal_on_a_tranformed_shape) +{ + TestShape s = TestShape(); + s.setTransform(scaling(1, 0.5, 1) * rotationZ(M_PI / 5)); + Tuple n = s.normalAt(Point(0, sqrt(2)/2, -sqrt(2)/2)); + + /* Temporary lower the precision */ + set_equal_precision(0.00001); + + ASSERT_EQ(n, Vector(0, 0.97014, -0.24254)); + + set_equal_precision(FLT_EPSILON); } \ No newline at end of file