Bläddra i källkod

Add function to calculate sphere normal vector on given point on the sphere.

Godzil 4 år sedan
förälder
incheckning
a8194169c5
5 ändrade filer med 92 tillägg och 0 borttagningar
  1. 2 0
      source/include/object.h
  2. 1 0
      source/include/sphere.h
  3. 5 0
      source/objects/object.cpp
  4. 12 0
      source/objects/sphere.cpp
  5. 72 0
      tests/sphere_test.cpp

+ 2 - 0
source/include/object.h

@@ -27,6 +27,8 @@ public:
     Object();
 
     virtual Intersect intersect(Ray r);
+    virtual Tuple normalAt(Tuple point);
+
     void setTransform(Matrix transform);
     Ray transform(Ray r) {  return Ray(this->transformMatrix * r.origin, this->transformMatrix * r.direction); };
     Ray invTransform(Ray r) {  return Ray(this->inverseTransform * r.origin, this->inverseTransform * r.direction); };

+ 1 - 0
source/include/sphere.h

@@ -18,6 +18,7 @@ class Sphere : public Object
 public:
     /* 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

+ 5 - 0
source/objects/object.cpp

@@ -24,6 +24,11 @@ Intersect Object::intersect(Ray r)
     return Intersect();
 };
 
+Tuple Object::normalAt(Tuple point)
+{
+    return Vector(0, 0, 0);
+}
+
 void Object::setTransform(Matrix transform)
 {
     this->transformMatrix = transform;

+ 12 - 0
source/objects/sphere.cpp

@@ -35,4 +35,16 @@ Intersect Sphere::intersect(Ray r)
     }
 
     return ret;
+}
+
+Tuple Sphere::normalAt(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();
 }

+ 72 - 0
tests/sphere_test.cpp

@@ -105,4 +105,76 @@ TEST(SphereTest, Intersecting_a_translated_sphere_with_a_ray)
     Intersect xs = s.intersect(r);
 
     ASSERT_EQ(xs.count(), 0);
+}
+
+TEST(SphereTest, The_normal_on_a_sphere_at_a_point_on_the_X_axis)
+{
+    Sphere s = Sphere();
+    Tuple n = s.normalAt(Point(1, 0, 0));
+
+    ASSERT_EQ(n, Vector(1, 0, 0));
+}
+
+TEST(SphereTest, The_normal_on_a_sphere_at_a_point_on_the_Y_axis)
+{
+    Sphere s = Sphere();
+    Tuple n = s.normalAt(Point(0, 1, 0));
+
+    ASSERT_EQ(n, Vector(0, 1, 0));
+}
+
+TEST(SphereTest, The_normal_on_a_sphere_at_a_point_on_the_Z_axis)
+{
+    Sphere s = Sphere();
+    Tuple n = s.normalAt(Point(0, 0, 1));
+
+    ASSERT_EQ(n, Vector(0, 0, 1));
+}
+
+TEST(SphereTest, The_normal_on_a_sphere_at_a_nonaxial_point)
+{
+    Sphere s = Sphere();
+    Tuple n = s.normalAt(Point(sqrt(3)/3, sqrt(3)/3, sqrt(3)/3));
+
+    ASSERT_EQ(n, Vector(sqrt(3)/3, sqrt(3)/3, sqrt(3)/3));
+}
+
+TEST(SphereTest, The_normal_is_a_normalise_vector)
+{
+    Sphere s = Sphere();
+    Tuple n = s.normalAt(Point(sqrt(3)/3, sqrt(3)/3, sqrt(3)/3));
+
+    ASSERT_EQ(n, n.normalise());
+}
+
+TEST(SphereTest, Computing_the_normal_on_a_translated_sphere)
+{
+    Sphere s = Sphere();
+
+    s.setTransform(translation(0, 1, 0));
+
+    Tuple n = s.normalAt(Point(0, 1.70711, -0.70711));
+
+    set_equal_precision(0.0001);
+
+    ASSERT_EQ(n, Vector(0, 0.70711, -0.70711));
+
+    /* Revert to default */
+    set_equal_precision(FLT_EPSILON);
+}
+
+TEST(SphereTest, Computing_the_normal_on_a_tranformed_sphere)
+{
+    Sphere s = Sphere();
+
+    s.setTransform(scaling(1, 0.5, 1) * rotation_z(M_PI / 5));
+
+    Tuple n = s.normalAt(Point(0, sqrt(2)/2, -sqrt(2)/2));
+
+    set_equal_precision(0.0001);
+
+    ASSERT_EQ(n, Vector(0, 0.97014, -0.24254));
+
+    /* Revert to default */
+    set_equal_precision(FLT_EPSILON);
 }