Browse Source

Add jitter to area light and example render of it.

Godzil 4 years ago
parent
commit
21749695b6

+ 24 - 15
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)
+![Area light without jitter](output/arealight_test_nojitter.png)
+
+With jitter:
+![Area light witht jitter](output/arealight_test.png)

BIN
output/arealight_test.png


BIN
output/arealight_test_nojitter.png


BIN
output/ch11_reflection_on_ball.png


BIN
output/ch11_zooming_on_reflective_ball.png


+ 4 - 1
source/include/light.h

@@ -12,6 +12,7 @@
 #include <tuple.h>
 #include <colour.h>
 #include <renderstat.h>
+#include <sequence.h>
 #include <stdio.h>
 
 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);

+ 50 - 0
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 <stdlib.h>
+#include <stdint.h>
+#include <time.h>
+
+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 */

+ 10 - 1
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);
 }

+ 1 - 1
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})

+ 2 - 6
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);
 
 

+ 74 - 0
tests/light_test.cpp

@@ -8,6 +8,7 @@
  */
 #include <light.h>
 #include <math.h>
+#include <math_helper.h>
 #include <colour.h>
 #include <tuple.h>
 #include <gtest/gtest.h>
@@ -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]));
+    }
 }

+ 21 - 0
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 <sequence.h>
+#include <gtest/gtest.h>
+
+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);
+}