diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index b1da617..f508c79 100644 --- a/source/CMakeLists.txt +++ b/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) diff --git a/source/include/light.h b/source/include/light.h new file mode 100644 index 0000000..2309e93 --- /dev/null +++ b/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 +#include + +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 diff --git a/source/include/material.h b/source/include/material.h new file mode 100644 index 0000000..c8fc6f3 --- /dev/null +++ b/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 +#include +#include + +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 diff --git a/source/include/object.h b/source/include/object.h index 64f1da6..fc94507 100644 --- a/source/include/object.h +++ b/source/include/object.h @@ -15,6 +15,7 @@ class Object; #include #include #include +#include /* 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); }; }; diff --git a/source/objects/light.cpp b/source/objects/light.cpp new file mode 100644 index 0000000..c873a40 --- /dev/null +++ b/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. + * + */ diff --git a/source/objects/material.cpp b/source/objects/material.cpp new file mode 100644 index 0000000..2a62b25 --- /dev/null +++ b/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 +#include +#include + +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); +} \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0d0bc6e..2a16547 100644 --- a/tests/CMakeLists.txt +++ b/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}) diff --git a/tests/light_test.cpp b/tests/light_test.cpp new file mode 100644 index 0000000..c374bbd --- /dev/null +++ b/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 +#include +#include +#include +#include + +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); +} \ No newline at end of file diff --git a/tests/material_test.cpp b/tests/material_test.cpp new file mode 100644 index 0000000..08ba89e --- /dev/null +++ b/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 +#include +#include +#include + +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)); +} \ No newline at end of file diff --git a/tests/sphere_test.cpp b/tests/sphere_test.cpp index 689c4c3..8b85f25 100644 --- a/tests/sphere_test.cpp +++ b/tests/sphere_test.cpp @@ -8,6 +8,7 @@ */ #include #include +#include #include #include @@ -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); } \ No newline at end of file