Browse Source

Add support for point light and materials.
Add material to objects.

Godzil 4 years ago
parent
commit
5ebed12f4f

+ 2 - 0
source/CMakeLists.txt

@@ -5,8 +5,10 @@ add_library(rayonnement STATIC)
 
 set(RAY_HEADERS include/tuple.h include/math_helper.h include/colour.h include/canvas.h
         include/matrix.h include/transformation.h include/intersect.h include/intersection.h
+        include/light.h include/material.h
         include/object.h include/ray.h include/sphere.h)
 set(RAY_SOURCES tuple.cpp math_helper.cpp colour.cpp canvas.cpp matrix.cpp transformation.cpp intersect.cpp
+        objects/light.cpp objects/material.cpp
         objects/object.cpp objects/ray.cpp objects/sphere.cpp)
 
 target_include_directories(rayonnement PUBLIC include)

+ 32 - 0
source/include/light.h

@@ -0,0 +1,32 @@
+/*
+ *  DoRayMe - a quick and dirty Raytracer
+ *  Light header
+ *
+ *  Created by Manoël Trapier
+ *  Copyright (c) 2020 986-Studio.
+ *
+ */
+#ifndef DORAYME_LIGHT_H
+#define DORAYME_LIGHT_H
+
+#include <tuple.h>
+#include <colour.h>
+
+enum LightType
+{
+    POINT_LIGHT = 0,
+};
+
+class Light
+{
+public:
+    Colour intensity;
+    Tuple position;
+    LightType type;
+
+public:
+    Light(LightType type = POINT_LIGHT, Tuple position=Point(0, 0, 0),
+          Colour intensity=Colour(1, 1, 1)) : type(type), position(position), intensity(intensity) { };
+};
+
+#endif //DORAYME_LIGHT_H

+ 37 - 0
source/include/material.h

@@ -0,0 +1,37 @@
+/*
+ *  DoRayMe - a quick and dirty Raytracer
+ *  Material header
+ *
+ *  Created by Manoël Trapier
+ *  Copyright (c) 2020 986-Studio.
+ *
+ */
+#ifndef DORAYME_MATERIAL_H
+#define DORAYME_MATERIAL_H
+
+#include <tuple.h>
+#include <colour.h>
+#include <light.h>
+
+class Material
+{
+public:
+    Colour colour;
+    double ambient;
+    double diffuse;
+    double specular;
+    double shininess;
+
+public:
+    Material() : colour(Colour(1, 1, 1)), ambient(0.1), diffuse(0.9), specular(0.9), shininess(200) {};
+
+    Colour lighting(Light light, Tuple point, Tuple eyeVector, Tuple normalVector);
+
+    bool operator==(const Material &b) const { return double_equal(this->ambient,   b.ambient) &&
+                                                      double_equal(this->diffuse,   b.diffuse) &&
+                                                      double_equal(this->specular,  b.specular) &&
+                                                      double_equal(this->shininess, b.shininess) &&
+                                                      (this->colour == b.colour); };
+};
+
+#endif //DORAYME_MATERIAL_H

+ 3 - 0
source/include/object.h

@@ -15,6 +15,7 @@ class Object;
 #include <tuple.h>
 #include <matrix.h>
 #include <intersect.h>
+#include <material.h>
 
 /* Base class for all object that can be presented in the world */
 class Object
@@ -22,6 +23,7 @@ class Object
 public:
     Matrix transformMatrix;
     Matrix inverseTransform;
+    Material material;
 
 public:
     Object();
@@ -30,6 +32,7 @@ public:
     virtual Tuple normalAt(Tuple point);
 
     void setTransform(Matrix transform);
+    void setMaterial(Material material) { this->material = material; };
     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); };
 };

+ 8 - 0
source/objects/light.cpp

@@ -0,0 +1,8 @@
+/*
+ *  DoRayMe - a quick and dirty Raytracer
+ *  Light implementation
+ *
+ *  Created by Manoël Trapier
+ *  Copyright (c) 2020 986-Studio.
+ *
+ */

+ 56 - 0
source/objects/material.cpp

@@ -0,0 +1,56 @@
+/*
+ *  DoRayMe - a quick and dirty Raytracer
+ *  Material implementation
+ *
+ *  Created by Manoël Trapier
+ *  Copyright (c) 2020 986-Studio.
+ *
+ */
+#include <tuple.h>
+#include <material.h>
+#include <colour.h>
+
+Colour Material::lighting(Light light, Tuple point, Tuple eyeVector, Tuple normalVector)
+{
+    Tuple lightVector = (light.position - point).normalise();
+    Tuple reflectVector = Tuple(0, 0, 0, 0);
+
+    Tuple effectiveColour = this->colour * light.intensity;
+    Tuple ambientColour = Colour(0, 0, 0);
+    Tuple diffuseColour = Colour(0, 0, 0);
+    Tuple specularColour = Colour(0, 0, 0);
+    Tuple finalColour = Colour(0, 0, 0);
+
+    double lightDotNormal, reflectDotEye;
+
+    ambientColour = effectiveColour * this->ambient;
+
+    lightDotNormal = lightVector.dot(normalVector);
+
+    if (lightDotNormal < 0)
+    {
+        diffuseColour = Colour(0, 0, 0);
+        specularColour = Colour(0, 0, 0);
+    }
+    else
+    {
+        diffuseColour = effectiveColour * this->diffuse * lightDotNormal;
+        reflectVector = -lightVector.reflect(normalVector);
+
+        reflectDotEye = reflectVector.dot(eyeVector);
+
+        if (reflectDotEye < 0)
+        {
+            specularColour = Colour(0, 0, 0);
+        }
+        else
+        {
+            double factor = pow(reflectDotEye, this->shininess);
+            specularColour = light.intensity * this->specular * factor;
+        }
+    }
+
+    finalColour = ambientColour + diffuseColour + specularColour;
+
+    return Colour(finalColour.x, finalColour.y, finalColour.z);
+}

+ 1 - 1
tests/CMakeLists.txt

@@ -4,7 +4,7 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
 find_package(Threads REQUIRED)
 
 set(TESTS_SRC tuple_test.cpp colour_test.cpp canvas_test.cpp matrix_test.cpp transformation_test.cpp ray_test.cpp
-        intersect_test.cpp sphere_test.cpp)
+        intersect_test.cpp sphere_test.cpp light_test.cpp material_test.cpp)
 
 add_executable(testMyRays)
 target_include_directories(testMyRays PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})

+ 24 - 0
tests/light_test.cpp

@@ -0,0 +1,24 @@
+/*
+ *  DoRayMe - a quick and dirty Raytracer
+ *  Light unit tests
+ *
+ *  Created by Manoël Trapier
+ *  Copyright (c) 2020 986-Studio.
+ *
+ */
+#include <light.h>
+#include <math.h>
+#include <colour.h>
+#include <tuple.h>
+#include <gtest/gtest.h>
+
+TEST(LightTest, A_point_lighthas_a_position_and_intensity)
+{
+    Colour intensity = Colour(1, 1, 1);
+    Point position = Point(0, 0, 0);
+
+    Light light = Light(POINT_LIGHT, position, intensity);
+
+    ASSERT_EQ(light.position, position);
+    ASSERT_EQ(light.intensity, intensity);
+}

+ 90 - 0
tests/material_test.cpp

@@ -0,0 +1,90 @@
+/*
+ *  DoRayMe - a quick and dirty Raytracer
+ *  Material unit tests
+ *
+ *  Created by Manoël Trapier
+ *  Copyright (c) 2020 986-Studio.
+ *
+ */
+#include <material.h>
+#include <math.h>
+#include <colour.h>
+#include <gtest/gtest.h>
+
+TEST(MaterialTest, The_default_material)
+{
+    Material m = Material();
+
+    ASSERT_EQ(m.colour, Colour(1, 1, 1));
+    ASSERT_EQ(m.ambient, 0.1);
+    ASSERT_EQ(m.diffuse, 0.9);
+    ASSERT_EQ(m.specular, 0.9);
+    ASSERT_EQ(m.shininess, 200.0);
+}
+
+// This is used by the next tests
+static Material m = Material();
+static Point position = Point(0, 0, 0);
+
+TEST(MaterialTest, Lighting_with_the_eye_between_the_light_and_the_surface)
+{
+    Vector eyev = Vector(0, 0, -1);
+    Vector normalv = Vector(0, 0, -1);
+    Light light = Light(POINT_LIGHT, Point(0, 0, -10), Colour(1, 1, 1));
+
+    Colour result = m.lighting(light, position, eyev, normalv);
+
+    ASSERT_EQ(result, Colour(1.9, 1.9, 1.9));
+}
+
+TEST(MaterialTest, Lighting_with_the_eye_between_the_light_and_the_surface_eye_offset_by_45)
+{
+    Vector eyev = Vector(0, -sqrt(2)/2, -sqrt(2)/2);
+    Vector normalv = Vector(0, 0, -1);
+    Light light = Light(POINT_LIGHT, Point(0, 0, -10), Colour(1, 1, 1));
+
+    Colour result = m.lighting(light, position, eyev, normalv);
+
+    ASSERT_EQ(result, Colour(1.0, 1.0, 1.0));
+}
+
+TEST(MaterialTest, Lighting_with_the_eye_opposite_surface_light_offset_45)
+{
+    Vector eyev = Vector(0, 0, -1);
+    Vector normalv = Vector(0, 0, -1);
+    Light light = Light(POINT_LIGHT, Point(0, 10, -10), Colour(1, 1, 1));
+
+    Colour result = m.lighting(light, position, eyev, normalv);
+
+    set_equal_precision(0.0001);
+
+    ASSERT_EQ(result, Colour(0.7364, 0.7364, 0.7364));
+
+    set_equal_precision(FLT_EPSILON);
+}
+
+TEST(MaterialTest, Lighting_with_the_eye_in_the_path_of_the_reflection_vector)
+{
+    Vector eyev = Vector(0, -sqrt(2)/2, -sqrt(2)/2);
+    Vector normalv = Vector(0, 0, -1);
+    Light light = Light(POINT_LIGHT, Point(0, 10, -10), Colour(1, 1, 1));
+
+    Colour result = m.lighting(light, position, eyev, normalv);
+
+    set_equal_precision(0.0001);
+
+    ASSERT_EQ(result, Colour(1.6364, 1.6364, 1.6364));
+
+    set_equal_precision(FLT_EPSILON);
+}
+
+TEST(MaterialTest, Lighting_with_the_light_behind_the_surface)
+{
+    Vector eyev = Vector(0, 0, -1);
+    Vector normalv = Vector(0, 0, -1);
+    Light light = Light(POINT_LIGHT, Point(0, 0, 10), Colour(1, 1, 1));
+
+    Colour result = m.lighting(light, position, eyev, normalv);
+
+    ASSERT_EQ(result, Colour(0.1, 0.1, 0.1));
+}

+ 21 - 0
tests/sphere_test.cpp

@@ -8,6 +8,7 @@
  */
 #include <ray.h>
 #include <sphere.h>
+#include <material.h>
 #include <transformation.h>
 #include <gtest/gtest.h>
 
@@ -177,4 +178,24 @@ TEST(SphereTest, Computing_the_normal_on_a_tranformed_sphere)
 
     /* Revert to default */
     set_equal_precision(FLT_EPSILON);
+}
+
+TEST(SphereTest, A_sphere_have_a_default_material)
+{
+    Sphere s = Sphere();
+    Material m = Material();
+
+    ASSERT_EQ(s.material, m);
+}
+
+TEST(SphereTest, A_sphere_may_be_assigned_a_material)
+{
+    Sphere s = Sphere();
+
+    Material m = Material();
+    m.ambient = 1;
+
+    s.setMaterial(m);
+
+    ASSERT_EQ(s.material, m);
 }