diff --git a/README.md b/README.md index 7852dfb..84f8bcb 100644 --- a/README.md +++ b/README.md @@ -14,60 +14,69 @@ It is writen in C++ with no STL and use [LodePNG](https://github.com/lvandeve/lo Examples outputs ---------------- -From chapter 05 - Sphere intersections: +**From chapter 05 - Sphere intersections:** ![Chapter 5 rendering test](output/ch5_test.png) -From Chapter 06 - Phong shading: +**From Chapter 06 - Phong shading:** ![Chapter 6 rendering test](output/ch6_test.png) -From Chapter 07 - World / Camera / Scenes: +**From Chapter 07 - World / Camera / Scenes:** ![Chapter 7 rendering test](output/ch7_test.png) -From Chapter 08 - Shadows: +**From Chapter 08 - Shadows:** ![Chapter 8 rendering test](output/ch8_test.png) -From Chapter 09 - Planes: +**From Chapter 09 - Planes:** ![Chapter 9 rendering test](output/ch9_test.png) -From Chapter 10 - Patterns: +**From Chapter 10 - Patterns:** ![Chapter 10 rendering test](output/ch10_test.png) -From Chapter 11 - Reflections, Transparency & Refractions +**From Chapter 11 - Reflections, Transparency & Refractions:** ![Chapter 11 reflections rendering test](output/ch11_reflection.png) -![Chapter 11 refraction rendering test](output/ch11_refraction.png) +Bonus: Zooming on a reflective ball: +![Chapter 11 zooming on a ball](output/ch11_zooming_on_reflective_ball.png) -![Chapter 11 rendering test](output/ch11_test.png) +Zooming on a reflection on that ball: +![Chapter 11 zooming on a reflection](output/ch11_reflection_on_ball.png) -From Chapter 12 - Cubes: +![Chapter 11 refraction rendering test](output/ch11_refraction.png | width=1000) + +![Chapter 11 rendering test](output/ch11_test.png | width=1000) + +**From Chapter 12 - Cubes:** ![Chapter 12 rendering test](output/ch12_test.png) -From Chapter 13 - Cylinders: +**From Chapter 13 - Cylinders:** ![Chapter 13 rendering test](output/ch13_test.png) Bonus: ![Chapter 13 cone test](output/ch13_cone.png) -From Chapter 14 - Groups & Bounding boxes +**From Chapter 14 - Groups & Bounding boxes:** ![Chapter 14 rendering test](output/ch14_test.png) -Bonus (from the forum): +**Bonus (from the forum):** [Merry Christmas](https://forum.raytracerchallenge.com/thread/16/merry-christmas-scene-description) ![Merry Christmas](output/christmasball.png) (about 1min render time using OpenMP on a 2.6Ghz Core i7 3720QM) -Bonus chapter - Soft shadow / Area light +**Bonus chapter - Soft shadow / Area light** Without jitter: -![Area light without jitter](output/arealight_test_nojitter.png) \ No newline at end of file +![Area light without jitter](output/arealight_test_nojitter.png) + +With jitter: +![Area light witht jitter](output/arealight_test.png) \ No newline at end of file diff --git a/output/arealight_test.png b/output/arealight_test.png new file mode 100644 index 0000000..02811dc Binary files /dev/null and b/output/arealight_test.png differ diff --git a/output/arealight_test_nojitter.png b/output/arealight_test_nojitter.png index a809501..87321e0 100644 Binary files a/output/arealight_test_nojitter.png and b/output/arealight_test_nojitter.png differ diff --git a/output/ch11_reflection_on_ball.png b/output/ch11_reflection_on_ball.png new file mode 100644 index 0000000..a356b27 Binary files /dev/null and b/output/ch11_reflection_on_ball.png differ diff --git a/output/ch11_zooming_on_reflective_ball.png b/output/ch11_zooming_on_reflective_ball.png new file mode 100644 index 0000000..4059bc6 Binary files /dev/null and b/output/ch11_zooming_on_reflective_ball.png differ diff --git a/source/include/light.h b/source/include/light.h index d1c64d2..df2d662 100644 --- a/source/include/light.h +++ b/source/include/light.h @@ -12,6 +12,7 @@ #include #include #include +#include #include class World; @@ -37,6 +38,8 @@ public: uint32_t samples; uint32_t uSteps; uint32_t vSteps; + bool jitter; + Sequence jitterBy; public: Light(LightType type = POINT_LIGHT, Tuple position=Point(0, 0, 0), @@ -44,7 +47,7 @@ public: { stats.addLight(); }; Light(LightType type, Tuple corner, Tuple fullUVec, uint32_t uSteps, Tuple fullVVec, uint32_t vSteps, Colour intensity, bool jitter = false): type(type), corner(corner), uVec(fullUVec / uSteps), uSteps(uSteps), - vVec(fullVVec / vSteps), vSteps(vSteps), intensity(intensity) + vVec(fullVVec / vSteps), vSteps(vSteps), intensity(intensity), jitter(jitter) { this->samples = this->vSteps * this->uSteps; this->position = this->corner + ((fullUVec + fullVVec) / 2); diff --git a/source/include/sequence.h b/source/include/sequence.h new file mode 100644 index 0000000..2bb0cd2 --- /dev/null +++ b/source/include/sequence.h @@ -0,0 +1,50 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * Sequence header + * + * Created by Manoël Trapier + * Copyright (c) 2020 986-Studio. + * + */ +#ifndef DORAYME_SEQUENCE_H +#define DORAYME_SEQUENCE_H + +#include +#include +#include + +class Sequence +{ +private: + double *list; + uint32_t listPos; + uint32_t listSize; +public: + Sequence() : list(nullptr), listPos(0), listSize(0) { + /* Need to bootstrap rand here */ + srand(time(NULL)); + } + Sequence(double *list, uint32_t listSize) : list(list), listPos(0), listSize(listSize) { }; + + static double frand(void) + { + return rand() / ((double) RAND_MAX); + } + + + double next() { + if (this->listSize == 0) + { + return frand(); + } + else + { + uint32_t pos = this->listPos; + this->listPos = (this->listPos + 1) % this->listSize; + return this->list[pos]; + } + } +}; + + +#endif /* DORAYME_SEQUENCE_H */ diff --git a/source/shapes/light.cpp b/source/shapes/light.cpp index 8069dc9..ab2120b 100644 --- a/source/shapes/light.cpp +++ b/source/shapes/light.cpp @@ -47,5 +47,14 @@ double Light::intensityAt(World &w, Tuple point) Tuple Light::pointOnLight(uint32_t u, uint32_t v) { - return this->corner + this->uVec * (u+0.5) + this->vVec * (v+0.5); + if (this->jitter) + { + /* For some reason, for the test to pass, I need to get the sequence for V first, then U contrary to what + * the bonus chapter says + */ + return this->corner + + this->vVec * (v + this->jitterBy.next()) + + this->uVec * (u + this->jitterBy.next()); + } + return this->corner + this->uVec * (u + 0.5) + this->vVec * (v + 0.5); } \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 93d327e..be6f850 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,7 +6,7 @@ find_package(Threads REQUIRED) set(TESTS_SRC math_test.cpp tuple_test.cpp colour_test.cpp canvas_test.cpp matrix_test.cpp transformation_test.cpp ray_test.cpp intersect_test.cpp sphere_test.cpp light_test.cpp material_test.cpp world_test.cpp camera_test.cpp shape_test.cpp plane_test.cpp pattern_test.cpp cube_test.cpp cylinder_test.cpp cone_test.cpp group_test.cpp - boundingbox_test.cpp triangle_test.cpp) + boundingbox_test.cpp triangle_test.cpp sequence_test.cpp) add_executable(testMyRays) target_include_directories(testMyRays PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) diff --git a/tests/arealight_test.cpp b/tests/arealight_test.cpp index 6877e64..b692558 100644 --- a/tests/arealight_test.cpp +++ b/tests/arealight_test.cpp @@ -32,15 +32,11 @@ int main() World w = World(); /* Add lights */ -#if 1 Light light1 = Light(AREA_LIGHT, Point(-1, 2, 4), Vector(2, 0, 0), 8, Vector(0, 2, 0), 8, - //jitter, - Colour(1.5, 1.5, 1.5)); -#else - Light light1 = Light(POINT_LIGHT, Point(-1, 2, 4), Colour(1.5,1.5,1.5)); -#endif + Colour(1.5, 1.5, 1.5), + true); w.addLight(&light1); diff --git a/tests/light_test.cpp b/tests/light_test.cpp index 121ccb8..9b8f4b0 100644 --- a/tests/light_test.cpp +++ b/tests/light_test.cpp @@ -8,6 +8,7 @@ */ #include #include +#include #include #include #include @@ -145,5 +146,78 @@ TEST(LightTest, The_area_light_intensity_function) double intensity = light.intensityAt(w, testList[i]); ASSERT_TRUE(double_equal(intensity, testResults[i])); } +} +TEST(LightTest, Finding_a_single_point_on_a_jittered_area_light) +{ + Tuple corner = Point(0, 0, 0); + Tuple v1 = Vector(2, 0, 0); + Tuple v2 = Vector(0, 0, 1); + + Light light = Light(AREA_LIGHT, corner, v1, 4, v2, 2, Colour(1, 1, 1), true); + + double seqList[] = { 0.3, 0.7 }; + light.jitterBy = Sequence(seqList, 2); + + uint32_t testList[][2] = { + {0, 0}, + {1, 0}, + {0, 1}, + {2, 0}, + {3, 1}, + }; + + Point testResults[] { + Point(0.15, 0, 0.35), + Point(0.65, 0, 0.35), + Point(0.15, 0, 0.85), + Point(1.15, 0, 0.35), + Point(1.65, 0, 0.85), + }; + + int testCount = sizeof(testList)/sizeof((testList)[0]); + int i; + + for(i = 0; i < testCount; i++) + { + Tuple tp = light.pointOnLight(testList[i][0], testList[i][1]); + ASSERT_EQ(tp, testResults[i]); + } +} + +TEST(LightTest, The_area_light_with_jittered_samples) +{ + World w = DefaultWorld(); + Tuple corner = Point(-0.5, -0.5, -5); + Tuple v1 = Vector(1, 0, 0); + Tuple v2 = Vector(0, 1, 0); + + Light light = Light(AREA_LIGHT, corner, v1, 2, v2, 2, Colour(1, 1, 1), true); + double seqList[] = { 0.7, 0.3, 0.9, 0.1, 0.5 }; + light.jitterBy = Sequence(seqList, 5); + + Point testList[] = { + Point(0, 0, 2), + Point(1, -1, 2), + Point(1.5, 0, 2), + Point(1.25, 1.25, 3), + Point(0, 0, -2), + }; + + double testResults[] { + 0.0, + 0.25, /* Chapter say 0.5 but it's not what I get here ?! */ + 0.75, + 0.75, + 1, + }; + + int testCount = sizeof(testList)/sizeof((testList)[0]); + int i; + + for(i = 0; i < testCount; i++) + { + double intensity = light.intensityAt(w, testList[i]); + EXPECT_TRUE(double_equal(intensity, testResults[i])); + } } \ No newline at end of file diff --git a/tests/sequence_test.cpp b/tests/sequence_test.cpp new file mode 100644 index 0000000..7d307ef --- /dev/null +++ b/tests/sequence_test.cpp @@ -0,0 +1,21 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * Ray unit tests + * + * Created by Manoël Trapier + * Copyright (c) 2020 986-Studio. + * + */ +#include +#include + +TEST(SequenceTest, A_number_generator_returns_a_cyclic_sequence_of_numbers) +{ + double seqList[] = { 0.1, 0.5, 1.0}; + Sequence gen = Sequence(seqList, 3); + + ASSERT_EQ(gen.next(), 0.1); + ASSERT_EQ(gen.next(), 0.5); + ASSERT_EQ(gen.next(), 1.0); + ASSERT_EQ(gen.next(), 0.1); +} \ No newline at end of file