From 4b53fcc7adc8f460252bc13491762b13c8f9674e Mon Sep 17 00:00:00 2001 From: Philip Abernethy Date: Wed, 13 Apr 2016 12:17:59 +0200 Subject: [PATCH] Implemented areal light sources and path tracing Common base class renderer introduced. Meshes and spheres can now be used a s light sources. --- CMakeLists.txt | 27 +- README.md | 10 +- classes.dot | 28 +- examples/box.obj | 37 ++- examples/example5.xml | 2 +- examples/example6.xml | 34 ++- examples/example7.xml | 39 ++- lib/camera/camera.cpp | 28 +- lib/camera/camera.h | 18 +- lib/camera/perspective_camera.cpp | 21 +- lib/camera/perspective_camera.h | 9 +- lib/camera/realistic_camera.cpp | 23 +- lib/camera/realistic_camera.h | 13 +- lib/geometry/color.cpp | 20 +- lib/geometry/intersection.h | 8 +- lib/geometry/material/lambertian_material.cpp | 49 ++-- lib/geometry/material/lambertian_material.h | 24 +- lib/geometry/material/material.cpp | 13 + lib/geometry/material/material.h | 32 ++- lib/geometry/material/phong_material.cpp | 54 ++-- lib/geometry/material/phong_material.h | 25 +- lib/geometry/material/solid_material.cpp | 28 +- lib/geometry/material/solid_material.h | 20 +- lib/geometry/material/specular_material.cpp | 56 ++-- lib/geometry/material/specular_material.h | 21 +- lib/geometry/material/textured_material.cpp | 35 +-- lib/geometry/material/textured_material.h | 9 +- .../material/transparent_material.cpp | 48 ++-- lib/geometry/material/transparent_material.h | 18 +- lib/geometry/point.cpp | 30 -- lib/geometry/point.h | 94 ------- lib/geometry/position.cpp | 30 ++ lib/geometry/position.h | 94 +++++++ lib/geometry/ray.cpp | 2 +- lib/geometry/ray.h | 10 +- lib/geometry/shapes/mesh.cpp | 49 ++-- lib/geometry/shapes/mesh.h | 18 +- lib/geometry/shapes/point.cpp | 15 + lib/geometry/shapes/point.h | 18 ++ lib/geometry/shapes/shape.cpp | 49 ++-- lib/geometry/shapes/shape.h | 37 ++- lib/geometry/shapes/sphere.cpp | 34 +-- lib/geometry/shapes/sphere.h | 14 +- lib/geometry/shapes/triangle.cpp | 99 ++++--- lib/geometry/shapes/triangle.h | 56 ++-- lib/geometry/transform.cpp | 4 +- lib/geometry/transform.h | 12 +- lib/light/ambient_light.cpp | 18 +- lib/light/ambient_light.h | 13 +- lib/light/lambertian_light.cpp | 29 ++ lib/light/lambertian_light.h | 25 ++ lib/light/light.cpp | 5 +- lib/light/light.h | 30 +- lib/light/mesh_light.cpp | 94 +++++++ lib/light/mesh_light.h | 28 ++ lib/light/parallel_light.cpp | 27 +- lib/light/parallel_light.h | 16 +- lib/light/point_light.cpp | 25 +- lib/light/point_light.h | 20 +- lib/light/sphere_light.cpp | 56 ++++ lib/light/sphere_light.h | 26 ++ lib/parser.cpp | 263 ++++++++++-------- lib/parser.h | 29 +- lib/renderer/pathtracer.cpp | 72 +++++ lib/renderer/pathtracer.h | 21 ++ lib/renderer/renderer.cpp | 65 +++++ lib/renderer/renderer.h | 73 +++++ lib/renderer/whitted_rt.cpp | 50 ++++ lib/renderer/whitted_rt.h | 24 ++ lib/sampler/random_sampler.cpp | 53 +++- lib/sampler/random_sampler.h | 14 +- lib/sampler/sampler.cpp | 36 ++- lib/sampler/sampler.h | 21 +- lib/tiny_obj_loader.cpp | 2 +- lib/whitted_rt.cpp | 100 ------- lib/whitted_rt.h | 90 ------ main.cpp | 24 +- 77 files changed, 1663 insertions(+), 1100 deletions(-) create mode 100644 lib/geometry/material/material.cpp delete mode 100644 lib/geometry/point.cpp delete mode 100644 lib/geometry/point.h create mode 100644 lib/geometry/position.cpp create mode 100644 lib/geometry/position.h create mode 100644 lib/geometry/shapes/point.cpp create mode 100644 lib/geometry/shapes/point.h create mode 100644 lib/light/lambertian_light.cpp create mode 100644 lib/light/lambertian_light.h create mode 100644 lib/light/mesh_light.cpp create mode 100644 lib/light/mesh_light.h create mode 100644 lib/light/sphere_light.cpp create mode 100644 lib/light/sphere_light.h create mode 100644 lib/renderer/pathtracer.cpp create mode 100644 lib/renderer/pathtracer.h create mode 100644 lib/renderer/renderer.cpp create mode 100644 lib/renderer/renderer.h create mode 100644 lib/renderer/whitted_rt.cpp create mode 100644 lib/renderer/whitted_rt.h delete mode 100644 lib/whitted_rt.cpp delete mode 100644 lib/whitted_rt.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 62ce343..497a370 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,11 +22,6 @@ endif () list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/extern) -find_package(OpenMP) -if (OPENMP_FOUND) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") -endif () - find_package(CImg REQUIRED) list(APPEND PROJ_INCLUDE_DIRS ${CImg_INCLUDE_DIRS}) list(APPEND PROJ_LIBRARY_DIRS ${CImg_SYSTEM_LIBS_DIR}) @@ -39,29 +34,37 @@ include_directories(${CImg_INCLUDE_DIRS}) find_package(pugixml REQUIRED) +find_package(OpenMP) +if (OPENMP_FOUND) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") +endif () + add_library(math STATIC lib/math/vec4.cpp lib/math/vec2.cpp lib/math/mat4.cpp lib/math/helper.cpp) add_library(sampler STATIC lib/sampler/random_sampler.cpp lib/sampler/sampler.cpp) target_link_libraries(sampler math) add_library(geometry STATIC lib/geometry/color.cpp lib/geometry/direction.cpp lib/geometry/normal.cpp - lib/geometry/point.cpp lib/geometry/ray.cpp lib/geometry/transform.cpp lib/geometry/intersection.h + lib/geometry/position.cpp lib/geometry/ray.cpp lib/geometry/transform.cpp lib/geometry/intersection.h lib/geometry/shapes/shape.cpp lib/geometry/shapes/sphere.cpp lib/geometry/shapes/triangle.cpp - lib/geometry/shapes/mesh.cpp lib/geometry/material/material.h lib/geometry/material/solid_material.cpp + lib/geometry/shapes/mesh.cpp lib/geometry/material/material.cpp lib/geometry/material/solid_material.cpp lib/geometry/material/phong_material.cpp lib/geometry/material/lambertian_material.cpp lib/geometry/material/specular_material.cpp lib/geometry/material/textured_material.cpp - lib/geometry/material/transparent_material.cpp) + lib/geometry/material/transparent_material.cpp lib/geometry/shapes/point.cpp) target_link_libraries(geometry math) add_library(light STATIC lib/light/light.cpp lib/light/ambient_light.cpp lib/light/parallel_light.cpp - lib/light/point_light.cpp) + lib/light/point_light.cpp lib/light/lambertian_light.cpp lib/light/mesh_light.cpp lib/light/sphere_light.cpp) target_link_libraries(light geometry) add_library(camera STATIC lib/camera/camera.cpp lib/camera/perspective_camera.cpp lib/camera/realistic_camera.cpp) target_link_libraries(camera sampler geometry) -add_library(whitted_rt STATIC lib/whitted_rt.cpp lib/parser.cpp lib/tiny_obj_loader.cpp) -target_link_libraries(whitted_rt camera light pugixml ${CImg_SYSTEM_LIBS}) +add_library(renderer STATIC lib/renderer/renderer.cpp lib/renderer/whitted_rt.cpp lib/renderer/pathtracer.cpp) +target_link_libraries(renderer camera light) + +add_library(parser STATIC lib/parser.cpp lib/tiny_obj_loader.cpp) +target_link_libraries(parser renderer pugixml ${CImg_SYSTEM_LIBS}) add_executable(Ray_Tracer main.cpp) -target_link_libraries(Ray_Tracer whitted_rt) \ No newline at end of file +target_link_libraries(Ray_Tracer parser) \ No newline at end of file diff --git a/README.md b/README.md index a6c9655..d49263b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ # RayTracer A whitted ray tracer written in C++ -This project mainly serves the purposes of making me more familiar with C++ as well as getting me into computer graphics. I hope though, that it is helpful and/or educational for others too and I try to keep the code understandable, rather than highly performant. +This project mainly serves the purposes of making me more familiar with C++ as well as getting me into computer +graphics. I hope though, that it is helpful and/or educational for others too and I try to keep the code understandable, +rather than highly performant. -It is written in C++11 and thus requires a compiler capable of that standard. It uses CImg for image import and export, which at least depends on X11 and pthread libraries. CImg dependencies are handled in its own `.cmake` file. It further depends on [PugiXML](https://github.com/zeux/pugixml) for XML parsing and uses [TinyOBJLoader](https://github.com/syoyo/tinyobjloader) as in-tree library. +It is written in C++11 and thus requires a compiler capable of that standard. It uses CImg for image import and export, +which at least depends on X11 and pthread libraries. CImg dependencies are handled in its own `.cmake` file. For CImg to +be able to handle the `.png` files of this project it requires ImageMagick to be installed. CMAKE expects to find +[PugiXML](https://github.com/zeux/pugixml) in the library path, so either install it system-wide or compile to `/usr/local/lib`. +[TinyOBJLoader](https://github.com/syoyo/tinyobjloader) is used as in-tree library. diff --git a/classes.dot b/classes.dot index 955eed7..60ea092 100644 --- a/classes.dot +++ b/classes.dot @@ -33,7 +33,7 @@ digraph Classes { label = "{intersection| + object : shape *\l + dir : normal *\l - + pos : point *\l + + pos : position *\l + local_pos : vec2 *\l }" ] @@ -42,8 +42,8 @@ digraph Classes { # transforms : transform\l # resolution : std::array\\l # data : std::vector\\>\>\l| - # camera(position : point &, - look_at : point &, + # camera(position : position &, + look_at : position &, up : direction &, resolution : std::array &, samples : unsigned long &)\l @@ -57,8 +57,8 @@ digraph Classes { label = "{perspective_camera| # fov : float\l| + perspective_camera()\l - + perspective_camera(position : point &, - look_at : point &, + + perspective_camera(position : position &, + look_at : position &, up : direction &, fov : float &, resolution : std::array &, @@ -67,9 +67,9 @@ digraph Classes { ] ray [ label = "{ray| - + o : point\l + + o : position\l + d : direction\l| - + ray(o : point &, d : direction &)\l + + ray(o : position &, d : direction &)\l }" ] shape_ [ @@ -146,7 +146,7 @@ digraph Classes { parallel_light| - transforms : transform\l| + parallel_light(col : color, dir : direction &)\l - + get_direction() : direction &\l + + get_directions() : direction &\l }" ] transform [ @@ -158,7 +158,7 @@ digraph Classes { + transform(trans : mat4 &, inv_trans : mat4 &)\l + transform(in : transform &)\l + operator()(t : transform &) : transform\l - + operator()(p : point &) : point\l + + operator()(p : position &) : position\l + operator()(v : direction &) : direction\l + operator()(n : normal &) : normal\l + translate(t : direction &) : void\l @@ -181,11 +181,11 @@ digraph Classes { ] point_ [ label = "{ - point|| - + point()\l - + point(x : float &, y : float &, z : float &)\l - + point(v : vec4 &)\l - + point(in : point)\l + position|| + + position()\l + + position(x : float &, y : float &, z : float &)\l + + position(v : vec4 &)\l + + position(in : position)\l + operator[](i : int) : float\l }" ] diff --git a/examples/box.obj b/examples/box.obj index 202261e..36a7d52 100644 --- a/examples/box.obj +++ b/examples/box.obj @@ -1,24 +1,23 @@ # Blender3D v249 OBJ File: # www.blender3d.org -v 1.000000 1.000000 -1.000000 -v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 -1.000000 v -1.000000 -1.000000 -1.000000 -v -1.000000 1.000000 -1.000000 -v 1.000000 1.000000 1.000000 -v 1.000000 -1.000000 1.000000 -v -1.000000 -1.000000 1.000000 -v -1.000000 1.000000 1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 1.000000 +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 vt 0.000000 0.000000 vt 1.000000 0.000000 vt 1.000000 1.000000 vt 0.000000 1.000000 -vn 0.000000 1.000000 0.000000 -vn -1.000000 0.000000 -0.000000 -vn -0.000000 -1.000000 -0.000000 -vn 1.000000 0.000000 -0.000000 -vn 1.000000 -0.000000 0.000000 -vn 0.000000 0.000000 1.000000 -vn 0.000000 0.000000 -1.000000 +vn 0.000000 1.000000 0.000000 +vn -1.000000 0.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 0.000000 0.000000 1.000000 +vn 0.000000 0.000000 -1.000000 usemtl Material s off f 5/1/1 1/2/1 4/3/1 @@ -28,8 +27,8 @@ f 3/1/2 8/3/2 4/4/2 f 2/1/3 6/2/3 3/4/3 f 6/2/3 7/3/3 3/4/3 f 1/1/4 5/2/4 2/4/4 -f 5/2/5 6/3/5 2/4/5 -f 5/1/6 8/2/6 6/4/6 -f 8/2/6 7/3/6 6/4/6 -f 1/1/7 2/2/7 3/3/7 -f 1/1/7 3/3/7 4/4/7 +f 5/2/4 6/3/4 2/4/4 +f 5/1/5 8/2/5 6/4/5 +f 8/2/5 7/3/5 6/4/5 +f 1/1/6 2/2/6 3/3/6 +f 1/1/6 3/3/6 4/4/6 diff --git a/examples/example5.xml b/examples/example5.xml index c6700a8..e9891e5 100644 --- a/examples/example5.xml +++ b/examples/example5.xml @@ -10,7 +10,7 @@ - + diff --git a/examples/example6.xml b/examples/example6.xml index 8769ec2..a753ee6 100644 --- a/examples/example6.xml +++ b/examples/example6.xml @@ -11,7 +11,9 @@ - + + + @@ -24,9 +26,9 @@ - + - + @@ -34,9 +36,9 @@ - + - + @@ -45,9 +47,9 @@ - + - + @@ -56,9 +58,19 @@ - + + + + + + + + + + + - + @@ -67,9 +79,9 @@ - + - + diff --git a/examples/example7.xml b/examples/example7.xml index 91b4a4b..dc200d2 100644 --- a/examples/example7.xml +++ b/examples/example7.xml @@ -1,7 +1,7 @@ - + @@ -10,24 +10,32 @@ - - - + + + + - - - - + - - + + + + + + + + + + + + @@ -38,6 +46,7 @@ + @@ -49,6 +58,7 @@ + @@ -60,6 +70,7 @@ + @@ -70,6 +81,7 @@ + @@ -81,6 +93,7 @@ + @@ -92,6 +105,7 @@ + @@ -102,9 +116,10 @@ + - - + + diff --git a/lib/camera/camera.cpp b/lib/camera/camera.cpp index e4cef99..f7f1142 100644 --- a/lib/camera/camera.cpp +++ b/lib/camera/camera.cpp @@ -4,18 +4,18 @@ #include "camera.h" -camera::camera(const point &position, const point &look_at, const direction &up, +camera::camera(const position &pos, const position &look_at, const direction &up, const std::array &resolution, const unsigned long max_bounces, - const unsigned long &samples, const std::shared_ptr s) - : resolution(resolution), max_bounces(max_bounces), samples(samples), s(s) { + const unsigned long &samples, const unsigned long &sr, const std::shared_ptr s) + : resolution(resolution), max_bounces(max_bounces), samples(samples), shadow_rays(sr), s(s) { assert(resolution[0] > 0 && resolution[1] > 0); - direction dir = normalise(look_at - position); + direction dir = normalise(look_at - pos); direction left = cross(normalise(up), dir); direction new_up = cross(dir, left); std::array, 4> d = {{ - {left[0], new_up[0], dir[0], position[0]}, - {left[1], new_up[1], dir[1], position[1]}, - {left[2], new_up[2], dir[2], position[2]}, + {left[0], new_up[0], dir[0], pos[0]}, + {left[1], new_up[1], dir[1], pos[1]}, + {left[2], new_up[2], dir[2], pos[2]}, {0, 0, 0, 1} }}; std::shared_ptr m(new mat4(d)); @@ -34,16 +34,8 @@ void camera::set_data(const unsigned long &x, const unsigned long &y, const std: std::shared_ptr camera::get_pixel(const unsigned long &x, const unsigned long &y) const { std::shared_ptr out(new color()); - for (auto &i : this->data[x][y]) + for (auto &i : data[x][y]) *out += i; - *out = *out * (float(1) / this->data[x][y].capacity()); + *out = *out * (1.0f / data[x][y].size()); return out; -} - -const std::array &camera::get_resolution() const { - return this->resolution; -} - -const unsigned long camera::get_max_bounces() const { - return this->max_bounces; -} +} \ No newline at end of file diff --git a/lib/camera/camera.h b/lib/camera/camera.h index 5df8170..65b0e22 100644 --- a/lib/camera/camera.h +++ b/lib/camera/camera.h @@ -9,15 +9,12 @@ #include "../geometry/ray.h" #include "../geometry/color.h" #include "../sampler/sampler.h" +#include "../sampler/random_sampler.h" class camera { protected: transform transforms; - std::array resolution; std::vector>> data; - const unsigned long max_bounces = 0; - unsigned long samples; - std::shared_ptr s; camera() { }; @@ -37,11 +34,16 @@ class camera { * * @param &samples the number of samples per pixel */ - camera(const point &position, const point &look_at, const direction &up, + camera(const position &pos, const position &look_at, const direction &up, const std::array &resolution, const unsigned long max_bounces, - const unsigned long &samples, const std::shared_ptr s); + const unsigned long &samples, const unsigned long &sr, const std::shared_ptr s); public: + const std::array resolution = {{1024, 768}}; + const unsigned long max_bounces = 0; + const unsigned long samples = 1; + const unsigned long shadow_rays = 1; + const std::shared_ptr s; /** * @brief Grants access to the initial rays of the camera * @@ -78,10 +80,6 @@ class camera { * @return the final color of the pixel with the given coordinates */ std::shared_ptr get_pixel(const unsigned long &x, const unsigned long &y) const; - - const std::array &get_resolution() const; - - const unsigned long get_max_bounces() const; }; #endif //RAY_TRACER_CAMERA_H \ No newline at end of file diff --git a/lib/camera/perspective_camera.cpp b/lib/camera/perspective_camera.cpp index f98c62a..62a1470 100644 --- a/lib/camera/perspective_camera.cpp +++ b/lib/camera/perspective_camera.cpp @@ -1,15 +1,17 @@ #include "perspective_camera.h" -perspective_camera::perspective_camera() : perspective_camera(point(0, 0, 0), point(0, 0, -1), direction(0, 1, 0), - {1024, 768}, 10, 45, 1, +perspective_camera::perspective_camera() : perspective_camera(position(0, 0, 0), position(0, 0, -1), direction(0, 1, 0), + {1024, 768}, 10, 45, 1, 1, (std::shared_ptr(new sampler())), 0) { } -perspective_camera::perspective_camera(const point &position, const point &look_at, const direction &up, - const std::array &resolution, const unsigned long max_bounces, - const float &fov, const unsigned long &samples, - const std::shared_ptr sampler, const float &defocus) : - camera(position, look_at, up, resolution, max_bounces, samples, sampler), fov(fov), defocus(defocus) { +perspective_camera::perspective_camera(const position &pos, const position &look_at, const direction &up, + const std::array &resolution, + const unsigned long max_bounces, const float &fov, + const unsigned long &samples, const unsigned long &sr, + const std::shared_ptr s, const float &defocus) : + camera(pos, look_at, up, resolution, max_bounces, samples, sr, + s), fov(fov), defocus(defocus) { assert(0 < fov && fov < 90); assert(defocus >= 0); stepwidth = std::tan(helper::to_radians(fov)) / (resolution[0] / 2); @@ -18,10 +20,11 @@ perspective_camera::perspective_camera(const point &position, const point &look_ } std::shared_ptr> perspective_camera::get_rays(const unsigned long &x, const unsigned long &y) { - std::shared_ptr> offsets = s->get_samples(defocus, defocus, samples); + std::shared_ptr> offsets = s->get_2d_samples(-defocus / 2, defocus / 2, -defocus / 2, defocus / 2, + samples); std::shared_ptr> out(new std::vector()); direction d = start + direction(-1, 0, 0) * (stepwidth * x) + direction(0, -1, 0) * (stepwidth * y); for (vec2 o : *offsets) - out->push_back(transforms(ray(point(), d + direction(o[0], o[1], 0)))); + out->push_back(transforms(ray(position(), d + direction(o[0], o[1], 0)))); return out; } \ No newline at end of file diff --git a/lib/camera/perspective_camera.h b/lib/camera/perspective_camera.h index 175932d..f558ed5 100644 --- a/lib/camera/perspective_camera.h +++ b/lib/camera/perspective_camera.h @@ -44,10 +44,11 @@ class perspective_camera: public camera { * * @param &fov the half horizontal field-of-view angle of the camera */ - perspective_camera(const point &position, const point &look_at, const direction &up, - const std::array &resolution, const unsigned long max_bounces, - const float &fov, const unsigned long &samples, const std::shared_ptr s, - const float &defocus); + perspective_camera(const position &pos, const position &look_at, const direction &up, + const std::array &resolution, + const unsigned long max_bounces, const float &fov, + const unsigned long &samples, const unsigned long &sr, + const std::shared_ptr s, const float &defocus); virtual std::shared_ptr> get_rays(const unsigned long &x, const unsigned long &y); }; diff --git a/lib/camera/realistic_camera.cpp b/lib/camera/realistic_camera.cpp index 2a97057..e5e45ac 100644 --- a/lib/camera/realistic_camera.cpp +++ b/lib/camera/realistic_camera.cpp @@ -4,28 +4,27 @@ #include "realistic_camera.h" - -realistic_camera::realistic_camera(const point &position, const point &look_at, const direction &up, +realistic_camera::realistic_camera(const position &pos, const position &look_at, const direction &up, const std::array &resolution, const unsigned long max_bounces, - const float &fov, - const unsigned long &samples, const std::shared_ptr s, const float &defocus, - const float &focus, const float &aperture) - : perspective_camera(position, look_at, up, resolution, max_bounces, fov, - samples, s, defocus), focus(focus), aperture(aperture) { + const float &fov, const unsigned long &samples, const unsigned long &sr, + const std::shared_ptr s, const float &defocus, const float &focus, + const float &aperture) + : perspective_camera(pos, look_at, up, resolution, max_bounces, fov, samples, sr, s, defocus), focus(focus), + aperture(aperture) { assert(focus > 0); assert(aperture >= 0); } std::shared_ptr> realistic_camera::get_rays(const unsigned long &x, const unsigned long &y) { - std::shared_ptr> offsets = s->get_samples(aperture, aperture, samples); - std::shared_ptr> focus_offsets = s->get_samples(defocus, defocus, samples); + std::shared_ptr> offsets = s->get_2d_samples(-aperture / 2, aperture / 2, -aperture / 2, + aperture / 2, samples); + std::shared_ptr> focus_offsets = s->get_1d_samples(-defocus / 2, defocus / 2, samples); std::shared_ptr> out(new std::vector()); direction d = start + direction(-1, 0, 0) * (stepwidth * x) + direction(0, -1, 0) * (stepwidth * y); - point fp = point(focus * d); for (unsigned long i = 0; i < offsets->size(); i++) { - point o = point((*offsets)[i][0], (*offsets)[i][1], 0); - out->push_back(transforms(ray(o, fp-o+direction((*focus_offsets)[i][0], (*focus_offsets)[i][1], 0)))); + position o = position((*offsets)[i][0], (*offsets)[i][1], 0); + out->push_back(transforms(ray(o, position((focus + focus_offsets->at(i)) * d) - o))); } return out; } diff --git a/lib/camera/realistic_camera.h b/lib/camera/realistic_camera.h index e1420e5..ed98dce 100644 --- a/lib/camera/realistic_camera.h +++ b/lib/camera/realistic_camera.h @@ -16,13 +16,12 @@ class realistic_camera : public perspective_camera { public: realistic_camera() : perspective_camera(), focus(1), aperture(0) { } - realistic_camera(const point &position, const point &look_at, const direction &up, - const std::array &resolution, const unsigned long max_bounces, const float &fov, - const unsigned long &samples, const std::shared_ptr s, const float &defocus, - const float &focus, const float &aperture); + realistic_camera(const position &pos, const position &look_at, const direction &up, + const std::array &resolution, const unsigned long max_bounces, + const float &fov, const unsigned long &samples, const unsigned long &sr, + const std::shared_ptr s, const float &defocus, const float &focus, + const float &aperture); virtual std::shared_ptr> get_rays(const unsigned long &x, const unsigned long &y) override; }; - - -#endif //RAY_TRACER_REALISTIC_CAMERA_H +#endif //RAY_TRACER_REALISTIC_CAMERA_H \ No newline at end of file diff --git a/lib/geometry/color.cpp b/lib/geometry/color.cpp index d6c6eba..424814a 100644 --- a/lib/geometry/color.cpp +++ b/lib/geometry/color.cpp @@ -18,12 +18,12 @@ color::color(const color &in) { float &color::operator[](const unsigned long i) { assert(0 <= i && i <= 2); - return this->v[i]; + return v[i]; } const float &color::operator[](const unsigned long i) const { assert(0 <= i && i <= 2); - return this->v[i]; + return v[i]; } std::ostream &operator<<(std::ostream &out, const color &a) { @@ -43,26 +43,26 @@ color &operator+=(color &lhs, const color &rhs) { } color operator*(const color &lhs, const float &rhs) { - return color(lhs[0]*rhs, lhs[1]*rhs, lhs[2]*rhs); + return color(lhs[0] * rhs, lhs[1] * rhs, lhs[2] * rhs); } color operator*(const float &lhs, const color &rhs) { - return color(lhs*rhs[0], lhs*rhs[1], lhs*rhs[2]); + return color(lhs * rhs[0], lhs * rhs[1], lhs * rhs[2]); } color scale(const color &col, const std::array &sf) { - return color(col[0]*sf[0], col[1]*sf[1], col[2]*sf[2]); + return color(col[0] * sf[0], col[1] * sf[1], col[2] * sf[2]); } color scale(const color &col, const color &sf) { - return color(col[0]*sf[0], col[1]*sf[1], col[2]*sf[2]); + return color(col[0] * sf[0], col[1] * sf[1], col[2] * sf[2]); } std::array rgb(const color &col) { std::array out = {{ - static_cast(col[0] > 1 ? 255 : std::round(col[0]*255)), - static_cast(col[1] > 1 ? 255 : std::round(col[1]*255)), - static_cast(col[2] > 1 ? 255 : std::round(col[2]*255)) - }}; + static_cast(col[0] > 1 ? 255 : std::round(col[0] * 255)), + static_cast(col[1] > 1 ? 255 : std::round(col[1] * 255)), + static_cast(col[2] > 1 ? 255 : std::round(col[2] * 255)) + }}; return out; } diff --git a/lib/geometry/intersection.h b/lib/geometry/intersection.h index b25fec1..e5d3148 100644 --- a/lib/geometry/intersection.h +++ b/lib/geometry/intersection.h @@ -6,7 +6,7 @@ #define RAY_TRACER_INTERSECTION_H #include "normal.h" -#include "point.h" +#include "position.h" #include "../math/vec2.h" #include @@ -19,15 +19,15 @@ struct intersection { /** * The intersected \ref shape */ - std::shared_ptr object; + std::shared_ptr object; /** - * The normal at the intersection point + * The normal at the intersection position */ std::shared_ptr norm; /** * The \ref point "position" of the intersection in world coordinates */ - std::shared_ptr pos; + std::shared_ptr pos; /** * The local (UV) coordinates at the intersection point. */ diff --git a/lib/geometry/material/lambertian_material.cpp b/lib/geometry/material/lambertian_material.cpp index 84f4c93..97914a4 100644 --- a/lib/geometry/material/lambertian_material.cpp +++ b/lib/geometry/material/lambertian_material.cpp @@ -4,50 +4,39 @@ #include "lambertian_material.h" -lambertian_material::lambertian_material(color col, float ambient, float diffuse) : solid_material(col), - ambient(ambient), - diffuse(diffuse) { } +lambertian_material::lambertian_material(const std::shared_ptr emit_col, const std::shared_ptr col, + const float &ambient, const float &diffuse) : solid_material(emit_col, col), + ambient(ambient), + diffuse(diffuse) { } std::ostream &operator<<(std::ostream &out, const lambertian_material &a) { out << "Phong material: Color: " << a.col << " Ambient intensity: " << a.ambient << ", Diffuse intensity: " << - a.diffuse; + a.diffuse; return out; } -std::shared_ptr lambertian_material::shade(const color &lcol, - const direction &l, - const normal &n, - const direction &v, - const vec2 &pos, - const bool internal) const { +const std::shared_ptr lambertian_material::shade(const color &lcol, const direction &l, const normal &n, + const direction &v, const vec2 &pos, + const bool &internal) const { + std::shared_ptr out(new color()); if (l != direction()) { // Directional light if (!internal) { float phi = std::max(0.0, dot(n, l)); - if (phi > 0) { - return std::make_shared(scale(this->col, lcol*(this->diffuse*phi))); - } + *out = phi > 0 ? scale(*col, lcol * (diffuse * phi)) : *out; } } else // Ambient light - return std::make_shared(scale(this->col, lcol*this->ambient)); - return std::make_shared(color()); -} - -std::shared_ptr> lambertian_material::reflect(const direction &i, const normal &n, const point &x, - const unsigned int &s) const { - return std::shared_ptr>(); -} - -std::shared_ptr> lambertian_material::refract(const direction &i, const normal &n, const point &x, - const bool internal) const { - return std::shared_ptr>(); + *out = scale(*col, lcol * ambient); + *out += *emit_col; + return out; } -const float lambertian_material::get_reflectance() const { - return 0; +std::shared_ptr lambertian_material::reflect(const direction &i, const normal &n, const position &x) const { + return std::shared_ptr(); } -const float lambertian_material::get_transmittance() const { - return 0; -} +std::shared_ptr lambertian_material::refract(const direction &i, const normal &n, const position &x, + const bool &internal) const { + return std::shared_ptr(); +} \ No newline at end of file diff --git a/lib/geometry/material/lambertian_material.h b/lib/geometry/material/lambertian_material.h index 32ea437..25689e2 100644 --- a/lib/geometry/material/lambertian_material.h +++ b/lib/geometry/material/lambertian_material.h @@ -7,27 +7,23 @@ #include "solid_material.h" -class lambertian_material: public solid_material { +class lambertian_material : public solid_material { private: float ambient, diffuse; public: - lambertian_material(color col, float ambient, float diffuse); + lambertian_material(const std::shared_ptr emit_col, const std::shared_ptr col, const float &ambient, + const float &diffuse); friend std::ostream &operator<<(std::ostream &out, const lambertian_material &a); - virtual std::shared_ptr - shade(const color &lcol, const direction &l, const normal &n, const direction &v, const vec2 &pos, - const bool internal) const; + virtual const std::shared_ptr shade(const color &lcol, const direction &l, const normal &n, + const direction &v, + const vec2 &pos, const bool &internal) const override; - virtual std::shared_ptr> - reflect(const direction &i, const normal &n, const point &x, const unsigned int &s) const; + virtual std::shared_ptr reflect(const direction &i, const normal &n, const position &x) const; - virtual std::shared_ptr> - refract(const direction &i, const normal &n, const point &x, const bool internal) const; - - virtual const float get_reflectance() const; - - virtual const float get_transmittance() const; + virtual std::shared_ptr refract(const direction &i, const normal &n, const position &x, + const bool &internal) const; }; -#endif //RAY_TRACER_LAMBERTIAN_MATERIAL_H +#endif //RAY_TRACER_LAMBERTIAN_MATERIAL_H \ No newline at end of file diff --git a/lib/geometry/material/material.cpp b/lib/geometry/material/material.cpp new file mode 100644 index 0000000..8896d2e --- /dev/null +++ b/lib/geometry/material/material.cpp @@ -0,0 +1,13 @@ +// +// Created by chais on 29/03/16. +// + +#include "material.h" + + +material::material(const std::shared_ptr emit_col, const std::shared_ptr col) : emit_col(emit_col), + col(col) { } + +const std::shared_ptr material::get_emit_col() const { + return emit_col; +} \ No newline at end of file diff --git a/lib/geometry/material/material.h b/lib/geometry/material/material.h index da271b0..6b7aab1 100644 --- a/lib/geometry/material/material.h +++ b/lib/geometry/material/material.h @@ -20,7 +20,11 @@ */ class material { protected: - material() { }; + const std::shared_ptr emit_col; + const std::shared_ptr col; + + material(const std::shared_ptr emit_col, const std::shared_ptr col); + public: /** * @brief Shades the material @@ -29,15 +33,14 @@ class material { * and viewing direction as well as the normal and whether the material is viewed from the inside or the outside. * @param lcol The color of the light * @param l The direction the light is in. Expected to be a 0 vector for ambient light. - * @param n The normal at the point that is being shaded + * @param n The normal at the position that is being shaded * @param v The direction the ray is coming from * @param pos The texture coordinates as calculated by shape::intersect_full * @param internal A flag denoting whether the ray originated from inside (true) or outside (false) the shape this material belongs to. * @return The base color multiplied by a factor according to the viewing situation */ - virtual std::shared_ptr shade - (const color &lcol, const direction &l, const normal &n, const direction &v, const vec2 &pos, - const bool internal) const = 0; + virtual const std::shared_ptr shade(const color &lcol, const direction &l, const normal &n, + const direction &v, const vec2 &pos, const bool &internal) const = 0; /** * @brief Reflects a ray @@ -47,8 +50,7 @@ class material { * @param s The number of samples * @return The reflected ray(s) */ - virtual std::shared_ptr> reflect(const direction &i, const normal &n, const point &x, - const unsigned int &s) const = 0; + virtual std::shared_ptr reflect(const direction &i, const normal &n, const position &x) const = 0; /** * @brief Refracts a ray according to Snell's law @@ -60,13 +62,13 @@ class material { * @param internal A flag denoting whether the ray originated from inside (true) or outside (false) the shape this material belongs * @return The refracted ray(s) */ - virtual std::shared_ptr> refract(const direction &i, const normal &n, const point &x, - const bool internal) const = 0; + virtual std::shared_ptr refract(const direction &i, const normal &n, const position &x, + const bool &internal) const = 0; - /** - * @brief Returns the reflectance - * @return The reflectance - */ + /** + * @brief Returns the reflectance + * @return The reflectance + */ virtual const float get_reflectance() const = 0; /** @@ -74,6 +76,8 @@ class material { * @return The transmittance */ virtual const float get_transmittance() const = 0; + + const std::shared_ptr get_emit_col() const; }; -#endif //RAY_TRACER_MATERIAL_H +#endif //RAY_TRACER_MATERIAL_H \ No newline at end of file diff --git a/lib/geometry/material/phong_material.cpp b/lib/geometry/material/phong_material.cpp index 74db44c..69ce6e0 100644 --- a/lib/geometry/material/phong_material.cpp +++ b/lib/geometry/material/phong_material.cpp @@ -4,55 +4,43 @@ #include "phong_material.h" -phong_material::phong_material(const color &col, const float &ambient, const float &diffuse, const float &specular, - const float &exponent) : solid_material(col), ambient(ambient), diffuse(diffuse), - specular(specular), exponent(exponent) { } +phong_material::phong_material(const std::shared_ptr emit_col, const std::shared_ptr col, + const float &ambient, const float &diffuse, const float &specular, const float &exponent) + : solid_material(emit_col, col), ambient(ambient), diffuse(diffuse), specular(specular), exponent(exponent) { } std::ostream &operator<<(std::ostream &out, const phong_material &a) { out << "Phong material: Color: " << a.col << " Ambient intensity: " << a.ambient << ", Diffuse intensity: " << - a.diffuse << ", Specular intensity: " << - a.specular << ", Specular exponent: " << a.exponent; + a.diffuse << ", Specular intensity: " << + a.specular << ", Specular exponent: " << a.exponent; return out; } -std::shared_ptr phong_material::shade(const color &lcol, - const direction &l, - const normal &n, - const direction &v, - const vec2 &pos, - const bool internal) const { +const std::shared_ptr phong_material::shade(const color &lcol, const direction &l, const normal &n, + const direction &v, + const vec2 &pos, const bool &internal) const { + std::shared_ptr out(new color()); if (l != direction()) { // Directional light if (!internal) { float phi = std::max(0.0, dot(n, l)); if (phi > 0) { - direction r = n*(2*phi) - l; - return std::make_shared(scale(this->col, lcol*(this->diffuse*phi)) + - lcol*(std::pow(std::max(0.0, dot(v, r)), this->exponent)*this->specular)); + direction r = n * (2 * phi) - l; + *out = scale(*col, lcol * (diffuse * phi)) + + lcol * (std::pow(std::max(0.0, dot(v, r)), exponent) * specular); } } } else // Ambient light - return std::make_shared(scale(this->col, lcol*this->ambient)); - return std::make_shared(color()); -} - -std::shared_ptr> phong_material::reflect(const direction &i, - const normal &n, - const point &x, - const unsigned int &s) const { - return std::shared_ptr>(); -} - -std::shared_ptr> phong_material::refract(const direction &i, const normal &n, const point &x, - const bool internal) const { - return std::shared_ptr>(); + *out = scale(*col, lcol * ambient); + *out += *emit_col; + return out; } -const float phong_material::get_reflectance() const { - return 0; +std::shared_ptr phong_material::reflect(const direction &i, const normal &n, const position &x) const { + return std::shared_ptr(); } -const float phong_material::get_transmittance() const { - return 0; -} +std::shared_ptr phong_material::refract(const direction &i, const normal &n, const position &x, + const bool &internal) const { + return std::shared_ptr(); +} \ No newline at end of file diff --git a/lib/geometry/material/phong_material.h b/lib/geometry/material/phong_material.h index bfa9cef..e4e3e4e 100644 --- a/lib/geometry/material/phong_material.h +++ b/lib/geometry/material/phong_material.h @@ -8,29 +8,24 @@ #include #include "solid_material.h" -struct phong_material: public solid_material { +struct phong_material : public solid_material { protected: float ambient, diffuse, specular, exponent; public: - phong_material(const color &col, const float &ambient, const float &diffuse, const float &specular, - const float &exponent); + phong_material(const std::shared_ptr emit_col, const std::shared_ptr col, const float &ambient, + const float &diffuse, + const float &specular, const float &exponent); friend std::ostream &operator<<(std::ostream &out, const phong_material &a); - virtual std::shared_ptr - shade(const color &lcol, const direction &l, const normal &n, const direction &v, const vec2 &pos, - const bool internal) const; + virtual const std::shared_ptr shade(const color &lcol, const direction &l, const normal &n, + const direction &v, + const vec2 &pos, const bool &internal) const override; - virtual std::shared_ptr> - reflect(const direction &i, const normal &n, const point &x, const unsigned int &s) const; + virtual std::shared_ptr reflect(const direction &i, const normal &n, const position &x) const; - virtual std::shared_ptr> - refract(const direction &i, const normal &n, const point &x, - const bool internal) const; - - virtual const float get_reflectance() const; - - virtual const float get_transmittance() const; + virtual std::shared_ptr refract(const direction &i, const normal &n, const position &x, + const bool &internal) const; }; #endif //RAY_TRACER_PHONG_H diff --git a/lib/geometry/material/solid_material.cpp b/lib/geometry/material/solid_material.cpp index c30f321..e6e9d7d 100644 --- a/lib/geometry/material/solid_material.cpp +++ b/lib/geometry/material/solid_material.cpp @@ -4,4 +4,30 @@ #include "solid_material.h" -solid_material::solid_material(const color &col) : col(col) { } \ No newline at end of file +solid_material::solid_material(const std::shared_ptr emit_col, const std::shared_ptr col) : + material(emit_col, col) { } + +const std::shared_ptr solid_material::shade(const color &lcol, const direction &l, const normal &n, + const direction &v, + const vec2 &pos, const bool &internal) const { + std::shared_ptr out = std::make_shared(*col); + *out += *emit_col; + return out; +} + +std::shared_ptr solid_material::reflect(const direction &i, const normal &n, const position &x) const { + return std::shared_ptr(); +} + +std::shared_ptr solid_material::refract(const direction &i, const normal &n, const position &x, + const bool &internal) const { + return std::shared_ptr(); +} + +const float solid_material::get_reflectance() const { + return 0; +} + +const float solid_material::get_transmittance() const { + return 0; +} \ No newline at end of file diff --git a/lib/geometry/material/solid_material.h b/lib/geometry/material/solid_material.h index fee0776..ac878d6 100644 --- a/lib/geometry/material/solid_material.h +++ b/lib/geometry/material/solid_material.h @@ -14,10 +14,22 @@ * * All solid materials should have a color */ -class solid_material: public material { -protected: - color col; - solid_material(const color &col); +class solid_material : public material { +public: + solid_material(const std::shared_ptr emit_col, const std::shared_ptr col); + + virtual const std::shared_ptr shade(const color &lcol, const direction &l, const normal &n, + const direction &v, const vec2 &pos, + const bool &internal) const override; + + virtual std::shared_ptr reflect(const direction &i, const normal &n, const position &x) const override; + + virtual std::shared_ptr refract(const direction &i, const normal &n, const position &x, + const bool &internal) const override; + + virtual const float get_reflectance() const override; + + virtual const float get_transmittance() const override; }; #endif //RAY_TRACER_SOLID_MATERIAL_H diff --git a/lib/geometry/material/specular_material.cpp b/lib/geometry/material/specular_material.cpp index c082a0c..98d8cff 100644 --- a/lib/geometry/material/specular_material.cpp +++ b/lib/geometry/material/specular_material.cpp @@ -4,56 +4,54 @@ #include "specular_material.h" -specular_material::specular_material(const color &col, const float &ambient, const float &diffuse, - const float &specular, const float &exponent, const float reflectivity) - : phong_material(col, ambient, diffuse, specular, - exponent), - reflectance(reflectivity) { +specular_material::specular_material(const std::shared_ptr emit_col, const std::shared_ptr col, + const float &ambient, const float &diffuse, const float &specular, + const float &exponent, const float &reflectivity) : phong_material(emit_col, col, + ambient, + diffuse, + specular, + exponent), + reflectance(reflectivity) { assert(reflectivity >= 0 && reflectivity <= 1); } -std::shared_ptr specular_material::shade(const color &lcol, - const direction &l, - const normal &n, - const direction &v, - const vec2 &pos, - const bool internal) const { - if (this->reflectance < 1) { +const std::shared_ptr specular_material::shade(const color &lcol, const direction &l, const normal &n, + const direction &v, + const vec2 &pos, const bool &internal) const { + std::shared_ptr out(new color()); + if (reflectance < 1) { if (l != direction()) { // Directional light if (!internal) { float phi = std::max(0.0, dot(n, l)); if (phi > 0) { - direction r = n*(2*phi) - l; - return std::make_shared((scale(this->col, lcol*(this->diffuse*phi)) + - lcol*(std::pow(std::max(0.0, dot(v, r)), this->exponent)*this->specular))* - (1 - this->reflectance)); + direction r = n * (2 * phi) - l; + *out = (scale(*col, lcol * (diffuse * phi)) + + lcol * (std::pow(std::max(0.0, dot(v, r)), exponent) * specular)) * + (1 - reflectance); } } } else // Ambient light - return std::make_shared(scale(this->col, lcol*this->ambient)*(1 - this->reflectance)); + *out = scale(*col, lcol * ambient) * (1 - reflectance); } - return std::shared_ptr(new color()); + *out += *emit_col; + return out; } -std::shared_ptr> specular_material::reflect(const direction &i, const normal &n, const point &x, - const unsigned int &s) const { - std::shared_ptr> out(new std::vector()); - // TODO adapt for multisampling - out->push_back(ray(x, (2*dot(n, i))*n - i)); - return out; +std::shared_ptr specular_material::reflect(const direction &i, const normal &n, const position &x) const { + return std::shared_ptr(new ray(x, (2 * dot(n, i)) * n - i)); } -std::shared_ptr> specular_material::refract(const direction &i, const normal &n, const point &x, - const bool internal) const { - return std::shared_ptr>(); +std::shared_ptr specular_material::refract(const direction &i, const normal &n, const position &x, + const bool &internal) const { + return std::shared_ptr(); } const float specular_material::get_reflectance() const { - return this->reflectance; + return reflectance; } const float specular_material::get_transmittance() const { return 0; -} +} \ No newline at end of file diff --git a/lib/geometry/material/specular_material.h b/lib/geometry/material/specular_material.h index 227920e..079d23f 100644 --- a/lib/geometry/material/specular_material.h +++ b/lib/geometry/material/specular_material.h @@ -7,24 +7,23 @@ #include "phong_material.h" -class specular_material: public phong_material { +class specular_material : public phong_material { protected: const float reflectance; public: - specular_material(const color &col, const float &ambient, const float &diffuse, const float &specular, - const float &exponent, const float reflectance); + specular_material(const std::shared_ptr emit_col, const std::shared_ptr col, const float &ambient, + const float &diffuse, + const float &specular, const float &exponent, const float &reflectance); - virtual std::shared_ptr - shade(const color &lcol, const direction &l, const normal &n, const direction &v, const vec2 &pos, - const bool internal) const; + virtual const std::shared_ptr shade(const color &lcol, const direction &l, const normal &n, + const direction &v, + const vec2 &pos, const bool &internal) const override; - virtual std::shared_ptr> - reflect(const direction &i, const normal &n, const point &x, const unsigned int &s) const; + virtual std::shared_ptr reflect(const direction &i, const normal &n, const position &x) const; - virtual std::shared_ptr> - refract(const direction &i, const normal &n, const point &x, - const bool internal) const; + virtual std::shared_ptr refract(const direction &i, const normal &n, const position &x, + const bool &internal) const; virtual const float get_reflectance() const; diff --git a/lib/geometry/material/textured_material.cpp b/lib/geometry/material/textured_material.cpp index 327d398..4e23170 100644 --- a/lib/geometry/material/textured_material.cpp +++ b/lib/geometry/material/textured_material.cpp @@ -4,21 +4,22 @@ #include "textured_material.h" -textured_material::textured_material(const float &ambient, const float &diffuse, const float &specular, - const float &exponent, std::shared_ptr>> texture) - : phong_material(color(), ambient, diffuse, specular, exponent), texture(texture) { +textured_material::textured_material(const std::shared_ptr emit_col, const float &ambient, const float &diffuse, + const float &specular, const float &exponent, + std::shared_ptr>> texture) : phong_material( + emit_col, std::shared_ptr(new color()), ambient, diffuse, specular, exponent), texture(texture) { } -std::shared_ptr textured_material::shade(const color &lcol, const direction &l, const normal &n, - const direction &v, const vec2 &pos, const bool internal) const { +const std::shared_ptr textured_material::shade(const color &lcol, const direction &l, const normal &n, + const direction &v, const vec2 &pos, const bool &internal) const { float U = pos[0] < 0 ? -pos[0] : pos[0]; float V = pos[1] < 0 ? -pos[1] : pos[1]; if (U > 1) U -= static_cast(U); if (V > 1) V -= static_cast(V); - V *= (this->texture->size() - 1); - U *= (this->texture->at(0).size() - 1); + V *= (texture->size() - 1); + U *= (texture->at(0).size() - 1); int lv = static_cast(std::floor(V)); int lu = static_cast(std::floor(U)); int hv = static_cast(std::ceil(V)); @@ -27,24 +28,24 @@ std::shared_ptr textured_material::shade(const color &lcol, const directi color lh = (*texture)[lv][hu]; color hl = (*texture)[hv][lu]; color hh = (*texture)[hv][hu]; - ll=(hu-U)*ll; - lh=(U-lu)*lh; - hl=(hu-U)*hl; - hh=(U-lu)*hh; - color out = (hv-V)*(ll+lh)+(V-lv)*(hl+hh); + ll = (hu - U) * ll; + lh = (U - lu) * lh; + hl = (hu - U) * hl; + hh = (U - lu) * hh; + std::shared_ptr out = std::make_shared((hv - V) * (ll + lh) + (V - lv) * (hl + hh)); if (l != direction()) { // Directional light if (!internal) { float phi = std::max(0.0, dot(n, l)); if (phi > 0) { direction r = n * (2 * phi) - l; - return std::make_shared( - scale(out, lcol * (this->diffuse * phi)) - + lcol * (std::pow(std::max(0.0, dot(v, r)), this->exponent) * this->specular)); + *out = scale(*out, lcol * (diffuse * phi)) + + lcol * (std::pow(std::max(0.0, dot(v, r)), exponent) * specular); } } } else // Ambient light - return std::make_shared(scale(out, lcol * this->ambient)); - return std::make_shared(color()); + *out = scale(*out, lcol * ambient); + *out += *emit_col; + return out; } diff --git a/lib/geometry/material/textured_material.h b/lib/geometry/material/textured_material.h index 2d1f048..28801fb 100644 --- a/lib/geometry/material/textured_material.h +++ b/lib/geometry/material/textured_material.h @@ -22,12 +22,13 @@ class textured_material: public phong_material { * @param exponent The specular exponent * @param texture The texture data */ - textured_material(const float &ambient, const float &diffuse, const float &specular, + textured_material(const std::shared_ptr emit_col, const float &ambient, const float &diffuse, + const float &specular, const float &exponent, std::shared_ptr>> texture); - virtual std::shared_ptr - shade(const color &lcol, const direction &l, const normal &n, const direction &v, const vec2 &pos, - const bool internal) const override; + virtual const std::shared_ptr shade(const color &lcol, const direction &l, const normal &n, + const direction &v, + const vec2 &pos, const bool &internal) const override; }; #endif //RAY_TRACER_TEXTURED_MATERIAL_H diff --git a/lib/geometry/material/transparent_material.cpp b/lib/geometry/material/transparent_material.cpp index 4df2b3d..410dfd7 100644 --- a/lib/geometry/material/transparent_material.cpp +++ b/lib/geometry/material/transparent_material.cpp @@ -4,26 +4,20 @@ #include "transparent_material.h" -transparent_material::transparent_material(const color &col, const float &ambient, const float &diffuse, - const float &specular, const float &exponent, const float reflectivity, - const float transmittance, const float ior) : specular_material(col, - ambient, - diffuse, - specular, - exponent, - reflectivity), - transmittance(transmittance), - ior(ior) { +transparent_material::transparent_material(const std::shared_ptr emit_col, const std::shared_ptr col, + const float &ambient, const float &diffuse, const float &specular, + const float &exponent, const float &reflectivity, const float &transmittance, + const float &ior) : specular_material(emit_col, col, ambient, diffuse, + specular, exponent, reflectivity), + transmittance(transmittance), ior(ior) { assert(transmittance >= 0); assert(ior > 0); } -std::shared_ptr transparent_material::shade(const color &lcol, - const direction &l, - const normal &n, - const direction &v, - const vec2 &pos, - const bool internal) const { +const std::shared_ptr transparent_material::shade(const color &lcol, const direction &l, const normal &n, + const direction &v, const vec2 &pos, + const bool &internal) const { + std::shared_ptr out(new color()); if (reflectance + transmittance < 1) { if (l != direction()) { // Directional light @@ -31,30 +25,28 @@ std::shared_ptr transparent_material::shade(const color &lcol, float phi = std::max(0.0, dot(n, l)); if (phi > 0) { direction r = n * (2 * phi) - l; - return std::make_shared((scale(col, lcol * (diffuse * phi)) + lcol * (std::pow( - std::max(0.0, dot(v, r)), exponent) * specular)) * - (1 - reflectance - transmittance)); + *out = (scale(*col, lcol * (diffuse * phi)) + + lcol * (std::pow(std::max(0.0, dot(v, r)), exponent) * specular)) * + (1 - reflectance - transmittance); } } } else // Ambient light - return std::make_shared( - scale(col, lcol * ambient) * (1 - reflectance - transmittance)); + *out = scale(*col, lcol * ambient) * (1 - reflectance - transmittance); } - return std::make_shared(color()); + *out += *emit_col; + return out; } -std::shared_ptr> transparent_material::refract(const direction &i, const normal &n, const point &x, - const bool internal) const { - std::shared_ptr> out(new std::vector()); +std::shared_ptr transparent_material::refract(const direction &i, const normal &n, const position &x, + const bool &internal) const { const float cosi = dot(n, i); const float eta = internal ? ior : 1 / ior; const float sint2 = std::pow(eta, 2) * (1 - std::pow(cosi, 2)); if (sint2 <= 1) - out->push_back(ray(x, -i * eta + n * (eta * cosi - std::sqrt(1 - sint2)))); + return std::shared_ptr(new ray(x, -i * eta + n * (eta * cosi - std::sqrt(1 - sint2)))); else - out->push_back(ray(x, n * (2 * cosi) - i)); - return out; + return std::shared_ptr(new ray(x, n * (2 * cosi) - i)); } const float transparent_material::get_transmittance() const { diff --git a/lib/geometry/material/transparent_material.h b/lib/geometry/material/transparent_material.h index 9e8e566..8f2b1de 100644 --- a/lib/geometry/material/transparent_material.h +++ b/lib/geometry/material/transparent_material.h @@ -10,7 +10,7 @@ /** * Models a transparent material with a smooth surface. It reflects light and refracts it according to its index of refraction. It is assumed that outside the geometry there is vacuum, with an index of refraction of 1. Inclusion of geometry (e.g. a sphere inside a sphere) is not currently supported and will give incorrect results. Beer's law is not currently implemented. */ -class transparent_material: public specular_material { +class transparent_material : public specular_material { protected: /** * The factor by which the colour resulting from transmitted (refracted) rays is scaled. \p transmittance + \p reflectance must be <= 1 @@ -32,15 +32,17 @@ class transparent_material: public specular_material { * @param transmittance The scaling factor for refracted rays * @param ior The index of refraction */ - transparent_material(const color &col, const float &ambient, const float &diffuse, const float &specular, - const float &exponent, const float reflectance, const float transmittance, const float ior); + transparent_material(const std::shared_ptr emit_col, const std::shared_ptr col, const float &ambient, + const float &diffuse, + const float &specular, const float &exponent, const float &reflectance, + const float &transmittance, const float &ior); - virtual std::shared_ptr - shade(const color &lcol, const direction &l, const normal &n, const direction &v, const vec2 &pos, - const bool internal) const override; + virtual const std::shared_ptr shade(const color &lcol, const direction &l, const normal &n, + const direction &v, const vec2 &pos, + const bool &internal) const override; - virtual std::shared_ptr> refract(const direction &i, const normal &n, const point &x, - const bool internal) const override; + virtual std::shared_ptr refract(const direction &i, const normal &n, const position &x, + const bool &internal) const override; virtual const float get_transmittance() const override; }; diff --git a/lib/geometry/point.cpp b/lib/geometry/point.cpp deleted file mode 100644 index 2d9f43a..0000000 --- a/lib/geometry/point.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// -// Created by chais on 25.08.15. -// - -#include "point.h" - -point::point() : vec4(0, 0, 0, 1) { } - -point::point(const float &x, const float &y, const float &z) : vec4(x, y, z, 1) { } - -point::point(const vec4 &in) : vec4(in[0], in[1], in[2], 1) { } - -point::point(const point &in) : vec4(in) { } - -point operator+(const point &lhs, const direction &rhs) { - return point(dynamic_cast(lhs) + dynamic_cast(rhs)); -} - -point &operator+=(point &lhs, const direction &rhs) { - dynamic_cast(lhs) += dynamic_cast(rhs); - return lhs; -} - -direction operator-(const point &lhs, const point &rhs) { - return direction(dynamic_cast(lhs) - dynamic_cast(rhs)); -} - -point operator-(const point &lhs, const direction &rhs) { - return point(dynamic_cast(lhs) - dynamic_cast(rhs)); -} diff --git a/lib/geometry/point.h b/lib/geometry/point.h deleted file mode 100644 index 064cdf2..0000000 --- a/lib/geometry/point.h +++ /dev/null @@ -1,94 +0,0 @@ -// -// Created by chais on 25.08.15. -// - -#ifndef RAY_TRACER_POINT_H -#define RAY_TRACER_POINT_H - -#include "../math/vec4.h" -#include "direction.h" - -/** - * @brief Description of point vectors - * - * A vec4 specialisation that has the w component set to 1 by default. - */ -class point: public vec4 { -public: - /** - * @brief Default constructor - * - * Creates a 0 point vector. - */ - point(); - - /** - * @brief Explicit constructor - * - * Sets the x, y and z components. The w component defaults to 1. - * @param x the x component - * @param y the y component - * @param z the z component - */ - point(const float &x, const float &y, const float &z); - - /** - * @brief vec4 copy constructor - * - * Sets the x, y and z components. The values are taken from the vec4 parameter. - * @param in the vec4 of component values. Note that in[3] is ignored. - */ - point(const vec4 &in); - - /** - * @brief Copy constructor - * - * Creates a copy of the given point vector. - * @param in the original vector - */ - point(const point &in); -}; - -/** - * @brief Addition operator - * - * Adds a point vector and a direction vector. The result is a point vector again. - * @param lhs the point vector - * @param rhs the direction vector - * @return the resulting point vector - */ -point operator+(const point &lhs, const direction &rhs); - -/** - * @brief Addition assignment - * - * Adds a point vector and a direction vector. The resulting point vector is assigned to the input point. - * @param lhs the point vector - * @param rhs the direction vector - * @return the updated lhs point vector - */ -point &operator+=(point &lhs, const direction &rhs); - -/** - * @brief Subtraction operator - * - * Subtracts to point vectors, resulting in a direction vector pointing from the right hand side to the left hand side - * point. - * @param lhs the 'target' point - * @param rhs the 'source' point - * @return the resulting direction vector - */ -direction operator-(const point &lhs, const point &rhs); - -/** - * @brief Addition operator - * - * Subtracts a direction vector from a point vector. The result is a point vector again. This is equivalent to - * lhs+(-rhs). - * @param lhs the point vector - * @param rhs the direction vector - * @return the resulting point vector - */ -point operator-(const point &lhs, const direction &rhs); - -#endif //RAY_TRACER_POINT_H diff --git a/lib/geometry/position.cpp b/lib/geometry/position.cpp new file mode 100644 index 0000000..a99691d --- /dev/null +++ b/lib/geometry/position.cpp @@ -0,0 +1,30 @@ +// +// Created by chais on 25.08.15. +// + +#include "position.h" + +position::position() : vec4(0, 0, 0, 1) { } + +position::position(const float &x, const float &y, const float &z) : vec4(x, y, z, 1) { } + +position::position(const vec4 &in) : vec4(in[0], in[1], in[2], 1) { } + +position::position(const position &in) : vec4(in) { } + +position operator+(const position &lhs, const direction &rhs) { + return position(dynamic_cast(lhs) + dynamic_cast(rhs)); +} + +position &operator+=(position &lhs, const direction &rhs) { + dynamic_cast(lhs) += dynamic_cast(rhs); + return lhs; +} + +direction operator-(const position &lhs, const position &rhs) { + return direction(dynamic_cast(lhs) - dynamic_cast(rhs)); +} + +position operator-(const position &lhs, const direction &rhs) { + return position(dynamic_cast(lhs) - dynamic_cast(rhs)); +} diff --git a/lib/geometry/position.h b/lib/geometry/position.h new file mode 100644 index 0000000..19f8e4c --- /dev/null +++ b/lib/geometry/position.h @@ -0,0 +1,94 @@ +// +// Created by chais on 25.08.15. +// + +#ifndef RAY_TRACER_POSITION_H +#define RAY_TRACER_POSITION_H + +#include "../math/vec4.h" +#include "direction.h" + +/** + * @brief Description of position vectors + * + * A vec4 specialisation that has the w component set to 1 by default. + */ +class position: public vec4 { +public: + /** + * @brief Default constructor + * + * Creates a 0 position vector. + */ + position(); + + /** + * @brief Explicit constructor + * + * Sets the x, y and z components. The w component defaults to 1. + * @param x the x component + * @param y the y component + * @param z the z component + */ + position(const float &x, const float &y, const float &z); + + /** + * @brief vec4 copy constructor + * + * Sets the x, y and z components. The values are taken from the vec4 parameter. + * @param in the vec4 of component values. Note that in[3] is ignored. + */ + position(const vec4 &in); + + /** + * @brief Copy constructor + * + * Creates a copy of the given position vector. + * @param in the original vector + */ + position(const position &in); +}; + +/** + * @brief Addition operator + * + * Adds a position vector and a direction vector. The result is a position vector again. + * @param lhs the position vector + * @param rhs the direction vector + * @return the resulting position vector + */ +position operator+(const position &lhs, const direction &rhs); + +/** + * @brief Addition assignment + * + * Adds a position vector and a direction vector. The resulting position vector is assigned to the input position. + * @param lhs the position vector + * @param rhs the direction vector + * @return the updated lhs position vector + */ +position &operator+=(position &lhs, const direction &rhs); + +/** + * @brief Subtraction operator + * + * Subtracts to position vectors, resulting in a direction vector pointing from the right hand side to the left hand side + * position. + * @param lhs the 'target' position + * @param rhs the 'source' position + * @return the resulting direction vector + */ +direction operator-(const position &lhs, const position &rhs); + +/** + * @brief Addition operator + * + * Subtracts a direction vector from a position vector. The result is a position vector again. This is equivalent to + * lhs+(-rhs). + * @param lhs the position vector + * @param rhs the direction vector + * @return the resulting position vector + */ +position operator-(const position &lhs, const direction &rhs); + +#endif //RAY_TRACER_POSITION_H diff --git a/lib/geometry/ray.cpp b/lib/geometry/ray.cpp index 3f2bb86..d11144e 100644 --- a/lib/geometry/ray.cpp +++ b/lib/geometry/ray.cpp @@ -4,4 +4,4 @@ #include "ray.h" -ray::ray(const point &o, const direction &d) : o(o), d(normalise(d)) { } +ray::ray(const position &o, const direction &d) : o(o), d(normalise(d)) { } diff --git a/lib/geometry/ray.h b/lib/geometry/ray.h index ef69185..016baff 100644 --- a/lib/geometry/ray.h +++ b/lib/geometry/ray.h @@ -5,23 +5,23 @@ #ifndef RAY_TRACER_RAY_H #define RAY_TRACER_RAY_H -#include "point.h" +#include "position.h" /** - * Models a ray with a \ref point of origin and a \ref direction. The direction is automatically normalised. + * Models a ray with a \ref position of origin and a \ref direction. The direction is automatically normalised. */ class ray { public: - point o; + position o; direction d; /** * Explicit constructor * - * @param o The point of origin of the ray + * @param o The position of origin of the ray * @param d The direction of the ray. This will always be normalised. */ - ray(const point &o, const direction &d); + ray(const position &o, const direction &d); }; #endif //RAY_TRACER_RAY_H diff --git a/lib/geometry/shapes/mesh.cpp b/lib/geometry/shapes/mesh.cpp index f18e820..2fc7057 100644 --- a/lib/geometry/shapes/mesh.cpp +++ b/lib/geometry/shapes/mesh.cpp @@ -4,18 +4,17 @@ #include "mesh.h" -mesh::mesh(std::shared_ptr offset, - std::shared_ptr matrl, +mesh::mesh(const direction &offset, const std::shared_ptr &matrl, std::shared_ptr>> faces) : shape(offset, matrl), faces(faces) { assert(faces->size() > 0); - aabb[0] = point(std::numeric_limits::infinity(), + aabb[0] = position(std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()); - aabb[1] = point(-std::numeric_limits::infinity(), + aabb[1] = position(-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::infinity()); for (std::shared_ptr t : *faces) { - std::array box = t->get_aabb(); + std::array box = t->get_aabb(); aabb[0][0] = std::min(aabb[0][0], box[0][0]); aabb[0][1] = std::min(aabb[0][1], box[0][1]); aabb[0][2] = std::min(aabb[0][2], box[0][2]); @@ -25,38 +24,38 @@ mesh::mesh(std::shared_ptr offset, } } -bool mesh::intersect_quick(const point &o, const direction &inv_d) const { +bool mesh::intersect_quick(const position &o, const direction &inv_d) const { float tmin = -std::numeric_limits::infinity(), tmax = std::numeric_limits::infinity(); if (inv_d[0] != 0) { - float tx1 = (this->aabb[0][0] - o[0])*inv_d[0]; - float tx2 = (this->aabb[1][0] - o[0])*inv_d[0]; + float tx1 = (aabb[0][0] - o[0])*inv_d[0]; + float tx2 = (aabb[1][0] - o[0])*inv_d[0]; tmin = std::max(tmin, std::min(tx1, tx2)); tmax = std::min(tmax, std::max(tx1, tx2)); } if (inv_d[1] != 0) { - float tx1 = (this->aabb[0][1] - o[1])*inv_d[1]; - float tx2 = (this->aabb[1][1] - o[1])*inv_d[1]; + float tx1 = (aabb[0][1] - o[1])*inv_d[1]; + float tx2 = (aabb[1][1] - o[1])*inv_d[1]; tmin = std::max(tmin, std::min(tx1, tx2)); tmax = std::min(tmax, std::max(tx1, tx2)); } if (inv_d[2] != 0) { - float tx1 = (this->aabb[0][2] - o[2])*inv_d[2]; - float tx2 = (this->aabb[1][2] - o[2])*inv_d[2]; + float tx1 = (aabb[0][2] - o[2])*inv_d[2]; + float tx2 = (aabb[1][2] - o[2])*inv_d[2]; tmin = std::max(tmin, std::min(tx1, tx2)); tmax = std::min(tmax, std::max(tx1, tx2)); } return tmax >= tmin; } -intersection mesh::intersect_full(const ray &r) { +intersection mesh::intersect_full(const ray &r) const { intersection out = intersection(); - ray tr = this->world_to_object(r); - tr.o = tr.o - *this->offset; + ray tr = world_to_object(r); + tr.o = tr.o - offset; const direction inv_d = direction(1/tr.d[0], 1/tr.d[1], 1/tr.d[2]); - if (!this->intersect_quick(tr.o, inv_d)) + if (!intersect_quick(tr.o, inv_d)) return out; float dist = std::numeric_limits::max(); - for (std::shared_ptr t : *this->faces) { + for (std::shared_ptr t : *faces) { if (!t->intersect_quick(tr.o, inv_d)) continue; std::shared_ptr n = t->get_avg_normal(); @@ -70,23 +69,23 @@ intersection mesh::intersect_full(const ray &r) { dist = new_dist; out = cur; out.object = shared_from_this(); - *out.pos = this->object_to_world(*out.pos + *this->offset); - *out.norm = normalise(this->object_to_world(*out.norm)); + *out.pos = object_to_world(*out.pos + offset); + *out.norm = normalise(object_to_world(*out.norm)); } } return out; } -bool mesh::intersect_shadow(const point &o, const direction &d) const { +bool mesh::intersect_shadow(const position &o, const direction &d) const { if (d == direction()) return false; - point to = this->world_to_object(o); - direction td = this->world_to_object(d); - to -= *this->offset; + position to = world_to_object(o); + direction td = world_to_object(d); + to -= offset; const direction inv_d = direction(1/td[0], 1/td[1], 1/td[2]); - if (!this->intersect_quick(to, inv_d)) + if (!intersect_quick(to, inv_d)) return false; - for (std::shared_ptr t : *this->faces) + for (std::shared_ptr t : *faces) if (t->intersect_shadow(to, td)) return true; return false; diff --git a/lib/geometry/shapes/mesh.h b/lib/geometry/shapes/mesh.h index d1b75d2..65e8bb7 100644 --- a/lib/geometry/shapes/mesh.h +++ b/lib/geometry/shapes/mesh.h @@ -21,17 +21,17 @@ class mesh: public shape { /** * The minimal and maximal corners of the Axis Aligned Bounding Box of the triangle. */ - std::array aabb; + std::array aabb; /** * Checks whether the bounding box is hit. For faster calculation \p d is expected to be 1/r.d of the original intersection ray. - * @param o The \ref point "origin" (in world coordinates) of the ray + * @param o The \ref position "origin" (in world coordinates) of the ray * @param d The inverted direction of the ray * @return true if bound box is hit, false otherwise */ - virtual bool intersect_quick(const point &o, const direction &inv_d) const; + virtual bool intersect_quick(const position &o, const direction &inv_d) const; public: - mesh(std::shared_ptr offset, std::shared_ptr matrl, + mesh(const direction &offset, const std::shared_ptr &matrl, std::shared_ptr>> faces); /** @@ -41,17 +41,17 @@ class mesh: public shape { * @param r The ray * @return An intersction containing the necessary information to continue calculations if the ray intersects. An intersection with null pointers if it doesn't */ - virtual intersection intersect_full(const ray &r); + virtual intersection intersect_full(const ray &r) const; /** - * @brief Checks whether the given \ref point is shadows by the shape for light coming from \p o+d . + * @brief Checks whether the given \ref position is shadows by the shape for light coming from \p o+d . * * First does an intersection with its bounding box. If it succeeds it iterates over all triangles. For each triangle a bounding box intersection is made. If it succeeds a shadow intersection is made. If it succeeds it returns true, otherwise it moves on to the next triangle. If all triangles were checked and none intersected false is returned. - * @param o The \ref point (in world coordinates) to check + * @param o The \ref position (in world coordinates) to check * @param d The vector pointing to the light source - * @return true if the point is shadowed by the shape, false otherwise + * @return true if the position is shadowed by the shape, false otherwise */ - virtual bool intersect_shadow(const point &o, const direction &d) const; + virtual bool intersect_shadow(const position &o, const direction &d) const; }; #endif //RAY_TRACER_MESH_H diff --git a/lib/geometry/shapes/point.cpp b/lib/geometry/shapes/point.cpp new file mode 100644 index 0000000..7cf187e --- /dev/null +++ b/lib/geometry/shapes/point.cpp @@ -0,0 +1,15 @@ +// +// Created by chais on 29/03/16. +// + +#include "point.h" + +point::point(const direction &offset, const std::shared_ptr &matrl) : shape(offset, matrl) { } + +intersection point::intersect_full(const ray &r) const { + return intersection(); +} + +bool point::intersect_shadow(const position &o, const direction &d) const { + return false; +} \ No newline at end of file diff --git a/lib/geometry/shapes/point.h b/lib/geometry/shapes/point.h new file mode 100644 index 0000000..d8e317b --- /dev/null +++ b/lib/geometry/shapes/point.h @@ -0,0 +1,18 @@ +// +// Created by chais on 29/03/16. +// + +#ifndef RAY_TRACER_POINT_H +#define RAY_TRACER_POINT_H + +#include "shape.h" + +class point : public shape { +public: + point(const direction &offset, const std::shared_ptr &matrl); + + virtual intersection intersect_full(const ray &r) const override; + + virtual bool intersect_shadow(const position &o, const direction &d) const override; +}; +#endif //RAY_TRACER_POINT_H \ No newline at end of file diff --git a/lib/geometry/shapes/shape.cpp b/lib/geometry/shapes/shape.cpp index 0983585..3eacc55 100644 --- a/lib/geometry/shapes/shape.cpp +++ b/lib/geometry/shapes/shape.cpp @@ -4,57 +4,50 @@ #include "shape.h" -shape::shape(const std::shared_ptr offset, std::shared_ptr matrl) : object_to_world(transform()), - world_to_object(object_to_world.inv_trans, - object_to_world.trans), - offset(offset), - matrl(matrl) { } +shape::shape(const direction &offset, const std::shared_ptr &matrl) : object_to_world(transform()), + world_to_object( + object_to_world.inv_trans, + object_to_world.trans), + offset(offset), matrl(matrl) { } -std::shared_ptr shape::shade(const color &lcol, - const direction &l, - const normal &n, - const direction &v, - const vec2 &pos, - const bool internal) { - return this->matrl->shade(lcol, l, n, v, pos, internal); +const std::shared_ptr shape::shade(const color &lcol, const direction &l, const normal &n, const direction &v, + const vec2 &pos, + const bool &internal) const { + return matrl->shade(lcol, l, n, v, pos, internal); } void shape::translate(const direction &t) { - this->object_to_world.translate(t); + object_to_world.translate(t); } -void shape::scale(const std::array sf) { - this->object_to_world.scale(sf); +void shape::scale(const std::array &sf) { + object_to_world.scale(sf); } void shape::rotateX(const float &angle) { - this->object_to_world.rotateX(angle); + object_to_world.rotateX(angle); } void shape::rotateY(const float &angle) { - this->object_to_world.rotateY(angle); + object_to_world.rotateY(angle); } void shape::rotateZ(const float &angle) { - this->object_to_world.rotateZ(angle); + object_to_world.rotateZ(angle); } -std::shared_ptr> shape::reflect(const direction &i, - const normal &n, - const point &x, - const unsigned int &s) const { - return this->matrl->reflect(i, n, x, s); +std::shared_ptr shape::reflect(const direction &i, const normal &n, const position &x) const { + return matrl->reflect(i, n, x); } -std::shared_ptr> shape::refract(const direction &i, const normal &n, const point &x, - const bool internal) const { - return this->matrl->refract(i, n, x, internal); +std::shared_ptr shape::refract(const direction &i, const normal &n, const position &x, const bool internal) const { + return matrl->refract(i, n, x, internal); } const float shape::get_reflectance() const { - return this->matrl->get_reflectance(); + return matrl->get_reflectance(); } const float shape::get_transmittance() const { - return this->matrl->get_transmittance(); + return matrl->get_transmittance(); } diff --git a/lib/geometry/shapes/shape.h b/lib/geometry/shapes/shape.h index eb8eb44..63373a4 100644 --- a/lib/geometry/shapes/shape.h +++ b/lib/geometry/shapes/shape.h @@ -14,10 +14,9 @@ /** * Models properties common to all geometry objects */ -class shape: public std::enable_shared_from_this { -private: - shape() { }; +class shape : public std::enable_shared_from_this { protected: + shape() { }; /** * Stores the transformations necessary to convert object space into world space. Since this is a transform it also stores its inverse. However for ease of use and lower cognitive load both matrices are also accessible through \p world_to_object. */ @@ -31,50 +30,48 @@ class shape: public std::enable_shared_from_this { /** * This is an offset from the object coordinate origin. It is untracked in the \p object_to_world (and therefor also \p world_to_object). This effectively moves the center of rotation for the shape by \p -offset */ - const std::shared_ptr offset; + const direction offset; /** * The shape's material */ std::shared_ptr matrl; - shape(std::shared_ptr offset, std::shared_ptr matrl); + shape(const direction &offset, const std::shared_ptr &matrl); public: /** * Checks whether the given ray intersects the shape. * @param r The ray - * @return An intersction containing the necessary information to continue calculations if the ray intersects. An intersection with null pointers if it doesn't + * @return An intersection containing the necessary information to continue calculations if the ray intersects. An intersection with null pointers if it doesn't */ - virtual intersection intersect_full(const ray &r) = 0; + virtual intersection intersect_full(const ray &r) const = 0; /** - * Checks whether the given \ref point is shadows by the shape for light coming from \p o+d . - * @param o The \ref point (in world coordinates) to check + * Checks whether the given \ref position is shadows by the shape for light coming from \p o+d . + * @param o The \ref position (in world coordinates) to check * @param d The vector pointing to the light source - * @return true if the point is shadowed by the shape, false otherwise + * @return true if the position is shadowed by the shape, false otherwise */ - virtual bool intersect_shadow(const point &o, const direction &d) const = 0; + virtual bool intersect_shadow(const position &o, const direction &d) const = 0; /** * @copydoc material::shade() */ - virtual std::shared_ptr - shade(const color &lcol, const direction &l, const normal &n, const direction &v, const vec2 &pos, - const bool internal); + virtual const std::shared_ptr shade(const color &lcol, const direction &l, const normal &n, + const direction &v, + const vec2 &pos, + const bool &internal) const; /** * @copydoc material::reflect() */ - std::shared_ptr> - reflect(const direction &i, const normal &n, const point &x, const unsigned int &s) const; + std::shared_ptr reflect(const direction &i, const normal &n, const position &x) const; /** * @copydoc material::refract() */ - std::shared_ptr> - refract(const direction &i, const normal &n, const point &x, - const bool internal) const; + std::shared_ptr refract(const direction &i, const normal &n, const position &x, const bool internal) const; /** * Translates the shape according to the given \ref direction vector @@ -88,7 +85,7 @@ class shape: public std::enable_shared_from_this { * Scales the shape according to the given factors * @param sf The array of scaling factors */ - void scale(const std::array sf); + void scale(const std::array &sf); /** * @brief X rotation diff --git a/lib/geometry/shapes/sphere.cpp b/lib/geometry/shapes/sphere.cpp index 715696e..00e7523 100644 --- a/lib/geometry/shapes/sphere.cpp +++ b/lib/geometry/shapes/sphere.cpp @@ -4,7 +4,7 @@ #include "sphere.h" -sphere::sphere(const float &radius, std::shared_ptr position, std::shared_ptr matrl) : radius( +sphere::sphere(const direction &position, const std::shared_ptr &matrl, const float &radius) : radius( radius), shape(position, matrl) { assert(radius > 0); } @@ -14,24 +14,24 @@ std::ostream &operator<<(std::ostream &out, const sphere &a) { return out; } -intersection sphere::intersect_full(const ray &r) { - ray tr = this->world_to_object(r); - direction c = point(-tr.o) + *this->offset; +intersection sphere::intersect_full(const ray &r) const { + ray tr = world_to_object(r); + direction c = position(-tr.o) + offset; float a = std::max(float(0.0), dot(tr.d, c)); float b = std::sqrt(std::pow(length(c), 2) - std::pow(a, 2)); intersection out = intersection(); - if (b < this->radius - 1E-4) { - float d = std::sqrt(std::pow(this->radius, 2) - std::pow(b, 2)); - std::shared_ptr x; + if (b < radius - 1E-4) { + float d = std::sqrt(std::pow(radius, 2) - std::pow(b, 2)); + std::shared_ptr x; if (d < a - 1E-4) - x = std::make_shared(point(tr.o + direction(tr.d*(a - d)))); + x = std::make_shared(position(tr.o + direction(tr.d*(a - d)))); else - x = std::make_shared(point(tr.o + direction(tr.d*(a + d)))); - std::shared_ptr norm = std::make_shared(normal(*x - *this->offset)); + x = std::make_shared(position(tr.o + direction(tr.d*(a + d)))); + std::shared_ptr norm = std::make_shared(normal(*x - offset)); float local_len = length(*norm); std::shared_ptr local_pos = std::make_shared(vec2((*norm)[0]/local_len, (*norm)[1]/local_len)); - *x = this->object_to_world(*x); - *norm = normalise(this->object_to_world(*norm)); + *x = object_to_world(*x); + *norm = normalise(object_to_world(*norm)); out.object = shared_from_this(); out.norm = norm; out.pos = x; @@ -40,13 +40,13 @@ intersection sphere::intersect_full(const ray &r) { return out; } -bool sphere::intersect_shadow(const point &o, const direction &d) const { +bool sphere::intersect_shadow(const position &o, const direction &d) const { if (d == direction()) return false; - point to = this->world_to_object(o); - direction td = this->world_to_object(d); - direction c = point(-to) + *this->offset; + position to = world_to_object(o); + direction td = world_to_object(d); + direction c = position(-to) + offset; float a = std::max(float(0.0), dot(normalise(td), c)); float b = std::sqrt(std::pow(length(c), 2) - std::pow(a, 2)); - return (length(td) > a && a > 0 && b < this->radius); + return (length(td) > a && a > 0 && b < radius); } \ No newline at end of file diff --git a/lib/geometry/shapes/sphere.h b/lib/geometry/shapes/sphere.h index 85be20c..64788de 100644 --- a/lib/geometry/shapes/sphere.h +++ b/lib/geometry/shapes/sphere.h @@ -13,30 +13,30 @@ * Models a sphere */ class sphere: public shape { -private: +protected: /** * The un-transformed radius of the sphere */ float radius; public: - sphere(const float &radius, std::shared_ptr offset, std::shared_ptr matrl); + sphere(const direction &offset, const std::shared_ptr &matrl, const float &radius); friend std::ostream &operator<<(std::ostream &out, const sphere &a); /** * Checks whether the \ref ray \p r intersects the sphere. This is done by constructing a right triangle between \p r.o, the centre of the sphere and the projection of the vector centre-p.o onto r.d. If the leg perpendicular to r.d is longer than the \p radius there's no intersection. If it's shorter further calculationsare necessary to determine which of the two possible intersection points is valid. * @param r The ray - * @return An intersction containing the necessary information to continue calculations if the ray intersects. An intersection with null pointers if it doesn't + * @return An intersection containing the necessary information to continue calculations if the ray intersects. An intersection with null pointers if it doesn't */ - virtual intersection intersect_full(const ray &r); + virtual intersection intersect_full(const ray &r) const; /** * Checks whether the \ref ray \p r intersects the sphere. This is done by constructing a right triangle between \p r.o, the centre of the sphere and the projection of the vector centre-p.o onto r.d. If the leg perpendicular to r.d is longer than the \p radius there's no intersection. - * @param o The \ref point (in world coordinates) to check + * @param o The \ref position (in world coordinates) to check * @param d The vector pointing to the light source - * @return true if the point is shadowed by the shape, false otherwise + * @return true if the position is shadowed by the shape, false otherwise */ - virtual bool intersect_shadow(const point &o, const direction &d) const; + virtual bool intersect_shadow(const position &o, const direction &d) const; }; #endif //RAY_TRACER_SPHERE_H diff --git a/lib/geometry/shapes/triangle.cpp b/lib/geometry/shapes/triangle.cpp index e4153db..4adcea0 100644 --- a/lib/geometry/shapes/triangle.cpp +++ b/lib/geometry/shapes/triangle.cpp @@ -4,70 +4,72 @@ #include "triangle.h" -triangle::triangle(std::shared_ptr> vertices, - std::shared_ptr> normals, - std::shared_ptr> texture_coords) - : vertices(vertices), normals(normals), texture_coords(texture_coords) { - aabb[0] = point(std::min((*vertices)[0][0], std::min((*vertices)[1][0], (*vertices)[2][0])), - std::min((*vertices)[0][1], std::min((*vertices)[1][1], (*vertices)[2][1])), - std::min((*vertices)[0][2], std::min((*vertices)[1][2], (*vertices)[2][2]))); - aabb[1] = point(std::max((*vertices)[0][0], std::max((*vertices)[1][0], (*vertices)[2][0])), - std::max((*vertices)[0][1], std::max((*vertices)[1][1], (*vertices)[2][1])), - std::max((*vertices)[0][2], std::max((*vertices)[1][2], (*vertices)[2][2]))); +triangle::triangle(const direction &offset, const std::shared_ptr &matrl, + std::shared_ptr> vertices, std::shared_ptr> normals, + std::shared_ptr> texture_coords) : shape(offset, matrl), vertices(vertices), + normals(normals), + texture_coords(texture_coords) { + aabb[0] = position(std::min((*vertices)[0][0], std::min((*vertices)[1][0], (*vertices)[2][0])), + std::min((*vertices)[0][1], std::min((*vertices)[1][1], (*vertices)[2][1])), + std::min((*vertices)[0][2], std::min((*vertices)[1][2], (*vertices)[2][2]))); + aabb[1] = position(std::max((*vertices)[0][0], std::max((*vertices)[1][0], (*vertices)[2][0])), + std::max((*vertices)[0][1], std::max((*vertices)[1][1], (*vertices)[2][1])), + std::max((*vertices)[0][2], std::max((*vertices)[1][2], (*vertices)[2][2]))); } intersection triangle::intersect_full(const ray &r) const { - direction E1 = (*this->vertices)[1] - (*this->vertices)[0]; - direction E2 = (*this->vertices)[2] - (*this->vertices)[0]; - direction T = r.o - (*this->vertices)[0]; + direction E1 = (*vertices)[1] - (*vertices)[0]; + direction E2 = (*vertices)[2] - (*vertices)[0]; + direction T = r.o - (*vertices)[0]; direction P = cross(r.d, E2); direction Q = cross(T, E1); - float s = 1/(dot(P, E1)); - float t = dot(Q, E2)*s; - float x = dot(P, T)*s; - float y = dot(Q, r.d)*s; + float s = 1 / (dot(P, E1)); + float t = dot(Q, E2) * s; + float x = dot(P, T) * s; + float y = dot(Q, r.d) * s; intersection out = intersection(); if (t < 2E-4 || x < 0 || y < 0 || x + y > 1) return out; - out.pos = std::shared_ptr(new point(r.o + r.d*(t - 1E-4))); + out.object = shared_from_this(); + out.pos = std::shared_ptr(new position(r.o + r.d * (t - 1E-5))); out.local_pos = std::shared_ptr(new vec2( - (1 - x - y)*(*this->texture_coords)[0][0] + x*(*this->texture_coords)[1][0] + y*(*this->texture_coords)[2][0], - (1 - x - y)*(*this->texture_coords)[0][1] + x*(*this->texture_coords)[1][1] + y*(*this->texture_coords)[2][1])); - out.norm = this->get_barycentric_normal(1 - x - y, x, y); + (1 - x - y) * (*texture_coords)[0][0] + x * (*texture_coords)[1][0] + y * (*texture_coords)[2][0], + (1 - x - y) * (*texture_coords)[0][1] + x * (*texture_coords)[1][1] + y * (*texture_coords)[2][1])); + out.norm = get_barycentric_normal(1 - x - y, x, y); return out; } -bool triangle::intersect_shadow(const point &o, const direction &d) const { - direction E1 = (*this->vertices)[1] - (*this->vertices)[0]; - direction E2 = (*this->vertices)[2] - (*this->vertices)[0]; - direction T = o - (*this->vertices)[0]; +bool triangle::intersect_shadow(const position &o, const direction &d) const { + direction E1 = (*vertices)[1] - (*vertices)[0]; + direction E2 = (*vertices)[2] - (*vertices)[0]; + direction T = o - (*vertices)[0]; direction nd = normalise(d); direction P = cross(nd, E2); direction Q = cross(T, E1); - float s = 1/(dot(P, E1)); - float t = dot(Q, E2)*s; - float x = dot(P, T)*s; - float y = dot(Q, nd)*s; - return !(length(d) < t || t < 0 || x < 0 || y < 0 || x + y > 1); + float s = 1 / (dot(P, E1)); + float t = dot(Q, E2) * s; + float x = dot(P, T) * s; + float y = dot(Q, nd) * s; + return !(length(d) < t + 1e-3 || t < 0 || x < 0 || y < 0 || x + y > 1); } -bool triangle::intersect_quick(const point &o, const direction &inv_d) const { +bool triangle::intersect_quick(const position &o, const direction &inv_d) const { float tmin = -std::numeric_limits::infinity(), tmax = std::numeric_limits::infinity(); if (inv_d[0] != 0) { - float tx1 = (this->aabb[0][0] - o[0])*inv_d[0]; - float tx2 = (this->aabb[1][0] - o[0])*inv_d[0]; + float tx1 = (aabb[0][0] - o[0]) * inv_d[0]; + float tx2 = (aabb[1][0] - o[0]) * inv_d[0]; tmin = std::max(tmin, std::min(tx1, tx2)); tmax = std::min(tmax, std::max(tx1, tx2)); } if (inv_d[1] != 0) { - float tx1 = (this->aabb[0][1] - o[1])*inv_d[1]; - float tx2 = (this->aabb[1][1] - o[1])*inv_d[1]; + float tx1 = (aabb[0][1] - o[1]) * inv_d[1]; + float tx2 = (aabb[1][1] - o[1]) * inv_d[1]; tmin = std::max(tmin, std::min(tx1, tx2)); tmax = std::min(tmax, std::max(tx1, tx2)); } if (inv_d[2] != 0) { - float tx1 = (this->aabb[0][2] - o[2])*inv_d[2]; - float tx2 = (this->aabb[1][2] - o[2])*inv_d[2]; + float tx1 = (aabb[0][2] - o[2]) * inv_d[2]; + float tx2 = (aabb[1][2] - o[2]) * inv_d[2]; tmin = std::max(tmin, std::min(tx1, tx2)); tmax = std::min(tmax, std::max(tx1, tx2)); } @@ -75,14 +77,25 @@ bool triangle::intersect_quick(const point &o, const direction &inv_d) const { } std::shared_ptr triangle::get_avg_normal() const { - return std::shared_ptr(new normal(normalise( - (*this->normals)[0] + (*this->normals)[1] + (*this->normals)[2]))); + return std::shared_ptr(new normal(normalise((*normals)[0] + (*normals)[1] + (*normals)[2]))); } std::shared_ptr triangle::get_barycentric_normal(const float &a, const float &b, const float &c) const { - return std::shared_ptr(new normal(normalise( - (*this->normals)[0]*a + (*this->normals)[1]*b + (*this->normals)[2]*c))); + return std::shared_ptr(new normal(normalise((*normals)[0] * a + (*normals)[1] * b + (*normals)[2] * c))); } -const std::array &triangle::get_aabb() const { - return this->aabb; + +std::shared_ptr triangle::get_barycentric_position(const float &a, const float &b, const float &c) const { + return std::shared_ptr(new position((*vertices)[0] * a + (*vertices)[1] * b + (*vertices)[2] * c)); +} + +const std::array &triangle::get_aabb() const { + return aabb; +} + +const std::shared_ptr> triangle::get_normals() const { + return normals; +} + +const std::shared_ptr> triangle::get_vertices() const { + return vertices; } diff --git a/lib/geometry/shapes/triangle.h b/lib/geometry/shapes/triangle.h index a136507..0f961ae 100644 --- a/lib/geometry/shapes/triangle.h +++ b/lib/geometry/shapes/triangle.h @@ -5,22 +5,17 @@ #ifndef RAY_TRACER_TRIANGLE_H #define RAY_TRACER_TRIANGLE_H -#include "../point.h" -#include "../normal.h" -#include "../../math/vec2.h" -#include "../intersection.h" -#include "../ray.h" -#include +#include "shape.h" /** * Models a triangle */ -class triangle { +class triangle : public shape { protected: /** * The coordinates of the corners. The coordinates are expected to be in an object space shared between all triangles of a mesh. */ - std::shared_ptr> vertices; + std::shared_ptr> vertices; /** * The normals corresponding to the vertices. @@ -35,43 +30,35 @@ class triangle { /** * The minimal and maximal corners of the Axis Aligned Bounding Box of the triangle. */ - std::array aabb; - - /** - * Returns the normal at the given barycentric coordinates. The normals are scaled with their respective factors, then added, the result normalised. - * @param a The scaling factor for normals[0] - * @param b The scaling factor for normals[1] - * @param c The scaling factor for normals[2] - * @return The resulting normal - */ - virtual std::shared_ptr get_barycentric_normal(const float &a, const float &b, const float &c) const; + std::array aabb; public: - triangle(std::shared_ptr> vertices, + triangle(const direction &offset, const std::shared_ptr &matrl, + std::shared_ptr> vertices, std::shared_ptr> normals, std::shared_ptr> texture_coords); /** * Checks whether the \ref ray \p r hits the triangle. First it checks whether the bounding box is hit. If not, an empty intersection is returned. If it is hit it checks whether the plane of the triangle is intersected. If not, an empty intersection is returned. If it is hit it checks whether the intersection occurs inside the triangle. If not an empty intersection is returned. If it is hit the returned intersection contains the normal, texture coordinates and position (in object coordinates) of the intersection. * @param r The ray - * @return An intersction containing the necessary information to continue calculations if the ray intersects. An intersection with null pointers if it doesn't + * @return An intersection containing the necessary information to continue calculations if the ray intersects. An intersection with null pointers if it doesn't */ virtual intersection intersect_full(const ray &r) const; /** - * Checks whether the given \ref point is shadows by the shape for light coming from \p o+d . First it checks whether the bounding box is hit. If not, false is returned. If it is hit it checks whether the plane of the triangle is intersected. If not, false is returned. If it is hit it checks whether the intersection occurs inside the triangle. If not false is returned. If it is hit returns true. - * @param o The \ref point (in world coordinates) to check + * Checks whether the given \ref position is shadows by the shape for light coming from \p o+d . First it checks whether the bounding box is hit. If not, false is returned. If it is hit it checks whether the plane of the triangle is intersected. If not, false is returned. If it is hit it checks whether the intersection occurs inside the triangle. If not false is returned. If it is hit returns true. + * @param o The \ref position (in world coordinates) to check * @param d The vector pointing to the light source - * @return true if the point is shadowed by the shape, false otherwise + * @return true if the position is shadowed by the shape, false otherwise */ - virtual bool intersect_shadow(const point &o, const direction &d) const; + virtual bool intersect_shadow(const position &o, const direction &d) const; /** * Checks whether the bounding box is hit. For faster calculation \p d is expected to be 1/r.d of the original intersection ray. - * @param o The \ref point "origin" (in world coordinates) of the ray + * @param o The \ref position "origin" (in world coordinates) of the ray * @param d The inverted direction of the ray * @return true if bound box is hit, false otherwise */ - virtual bool intersect_quick(const point &o, const direction &inv_d) const; + virtual bool intersect_quick(const position &o, const direction &inv_d) const; /** * Returns the averaged normal of the triangle. All normals are added up and the result normalised. @@ -79,11 +66,26 @@ class triangle { */ virtual std::shared_ptr get_avg_normal() const; + /** + * Returns the normal at the given barycentric coordinates. The normals are scaled with their respective factors, then added, the result normalised. + * @param a The scaling factor for normals[0] + * @param b The scaling factor for normals[1] + * @param c The scaling factor for normals[2] + * @return The resulting normal + */ + virtual std::shared_ptr get_barycentric_normal(const float &a, const float &b, const float &c) const; + + virtual std::shared_ptr get_barycentric_position(const float &a, const float &b, const float &c) const; + /** * Returns the (object space) coordinates of the Axis Aligned Bounding Box. * @return The array of points. */ - const std::array &get_aabb() const; + const std::array &get_aabb() const; + + const std::shared_ptr> get_normals() const; + + const std::shared_ptr> get_vertices() const; }; #endif //RAY_TRACER_TRIANGLE_H diff --git a/lib/geometry/transform.cpp b/lib/geometry/transform.cpp index b8712a2..6e096ef 100644 --- a/lib/geometry/transform.cpp +++ b/lib/geometry/transform.cpp @@ -19,8 +19,8 @@ transform transform::operator()(const transform &t) const { std::shared_ptr(new mat4(*t.inv_trans*(*this->inv_trans)))); } -point transform::operator()(const point &p) const { - return point(*this->trans*p); +position transform::operator()(const position &p) const { + return position(*this->trans*p); } direction transform::operator()(const direction &v) const { diff --git a/lib/geometry/transform.h b/lib/geometry/transform.h index 649b7ee..f645e14 100644 --- a/lib/geometry/transform.h +++ b/lib/geometry/transform.h @@ -6,7 +6,7 @@ #define RAY_TRACER_TRANSFORM_H #include "../math/mat4.h" -#include "point.h" +#include "position.h" #include "normal.h" #include #include @@ -71,13 +71,13 @@ class transform { transform operator()(const transform &t) const; /** - * @brief Transform application to point + * @brief Transform application to position * - * Applies its transformations from the left to the given point. - * @param p The point to be transformed - * @return The new, transformed point + * Applies its transformations from the left to the given position. + * @param p The position to be transformed + * @return The new, transformed position */ - point operator()(const point &p) const; + position operator()(const position &p) const; /** * @brief Transform application to direction diff --git a/lib/light/ambient_light.cpp b/lib/light/ambient_light.cpp index c2b760f..c743ed3 100644 --- a/lib/light/ambient_light.cpp +++ b/lib/light/ambient_light.cpp @@ -4,17 +4,25 @@ #include "ambient_light.h" -ambient_light::ambient_light(const std::shared_ptr col) : light(col) { } +ambient_light::ambient_light(const std::shared_ptr emit_col) : light(direction()), point(direction(), + std::shared_ptr( + new solid_material( + emit_col, + std::shared_ptr( + new color())))) { } std::ostream &operator<<(std::ostream &out, const ambient_light &a) { - out << "Ambient light: color [r, g, b]: " << *a.emit_color; + out << "Ambient light: color [r, g, b]: " << a.matrl->get_emit_col(); return out; } -const std::shared_ptr ambient_light::get_direction(const point &pos) const { - return emit_dir; +const std::shared_ptr> ambient_light::get_directions(const position &pos, + const unsigned long &samples) const { + std::shared_ptr> out(new std::vector()); + out->push_back(direction()); + return out; } const std::shared_ptr ambient_light::emit(const direction &dir) const { - return emit_color; + return matrl->get_emit_col(); } \ No newline at end of file diff --git a/lib/light/ambient_light.h b/lib/light/ambient_light.h index 91e2ced..29d2591 100644 --- a/lib/light/ambient_light.h +++ b/lib/light/ambient_light.h @@ -5,14 +5,14 @@ #ifndef RAY_TRACER_AMBIENT_LIGHT_H #define RAY_TRACER_AMBIENT_LIGHT_H -#include "../geometry/color.h" #include "light.h" -#include "../geometry/direction.h" +#include "../geometry/shapes/point.h" +#include "../geometry/material/solid_material.h" /** * Models an ambient light source */ -class ambient_light: public light { +class ambient_light : public light, public point { public: /** * @brief Constructor @@ -20,7 +20,7 @@ class ambient_light: public light { * \p light::dir is set to [0, 0, 0, 0] as ambient light is omnidirectional. * @param col The \ref color of the ambient light */ - ambient_light(const std::shared_ptr col); + ambient_light(const std::shared_ptr emit_col); friend std::ostream &operator<<(std::ostream &out, const ambient_light &a); @@ -28,9 +28,10 @@ class ambient_light: public light { * @copybrief light::get_direction() * * Returns the 0 vector stored in \p light::dir - * @copydetails light::get_direction() + * @copydetails light::get_directions() */ - virtual const std::shared_ptr get_direction(const point &pos) const; + virtual const std::shared_ptr> get_directions(const position &pos, + const unsigned long &samples) const; /** * @copybrief light::emit() diff --git a/lib/light/lambertian_light.cpp b/lib/light/lambertian_light.cpp new file mode 100644 index 0000000..2783eb2 --- /dev/null +++ b/lib/light/lambertian_light.cpp @@ -0,0 +1,29 @@ +// +// Created by chais on 21/03/16. +// + +#include "lambertian_light.h" + +lambertian_light::lambertian_light(const std::shared_ptr emit_col, const direction &emit_dir, + const direction &offset) : + light(emit_dir), point(direction(), std::shared_ptr( + new solid_material(emit_col, std::shared_ptr(new color())))) { } + +const std::shared_ptr> lambertian_light::get_directions(const position &pos, + const unsigned long &samples) const { + std::shared_ptr> out(new std::vector()); + out->push_back(pos - object_to_world(offset)); + return out; +} + +const std::shared_ptr lambertian_light::emit(const direction &dir) const { + return std::make_shared(*matrl->get_emit_col() * std::max(dot(emit_dir, dir), 0.0f)); +} + +intersection lambertian_light::intersect_full(const ray &r) const { + return intersection(); +} + +bool lambertian_light::intersect_shadow(const position &o, const direction &d) const { + return false; +} \ No newline at end of file diff --git a/lib/light/lambertian_light.h b/lib/light/lambertian_light.h new file mode 100644 index 0000000..e14694d --- /dev/null +++ b/lib/light/lambertian_light.h @@ -0,0 +1,25 @@ +// +// Created by chais on 21/03/16. +// + +#ifndef RAY_TRACER_LAMBERTIAN_LIGHT_H +#define RAY_TRACER_LAMBERTIAN_LIGHT_H + +#include "light.h" +#include "../geometry/shapes/point.h" +#include "../geometry/material/solid_material.h" + +class lambertian_light: public light, public point { +public: + lambertian_light(const std::shared_ptr emit_col, const direction &emit_dir, const direction &offset); + + virtual const std::shared_ptr> get_directions(const position &pos, + const unsigned long &samples) const override; + + virtual const std::shared_ptr emit(const direction &dir) const override; + + virtual intersection intersect_full(const ray &r) const override; + + virtual bool intersect_shadow(const position &o, const direction &d) const override; +}; +#endif //RAY_TRACER_LAMBERTIAN_LIGHT_H diff --git a/lib/light/light.cpp b/lib/light/light.cpp index 3111d88..f3ba109 100644 --- a/lib/light/light.cpp +++ b/lib/light/light.cpp @@ -4,7 +4,4 @@ #include "light.h" -light::light(const std::shared_ptr col) : emit_color(col), emit_dir(new direction()) { } - -light::light(const std::shared_ptr col, const std::shared_ptr dir) - : emit_color(col), emit_dir(new direction(normalise(*dir))) { } \ No newline at end of file +light::light(const direction &emit_dir) : emit_dir(emit_dir) { } \ No newline at end of file diff --git a/lib/light/light.h b/lib/light/light.h index cc07929..4308459 100644 --- a/lib/light/light.h +++ b/lib/light/light.h @@ -5,9 +5,9 @@ #ifndef RAY_TRACER_LIGHT_H #define RAY_TRACER_LIGHT_H +#include "../geometry/direction.h" +#include "../geometry/position.h" #include "../geometry/color.h" -#include "../geometry/point.h" -#include "../geometry/transform.h" #include #include @@ -18,31 +18,22 @@ */ class light { protected: - const std::shared_ptr emit_color; - const std::shared_ptr emit_dir; - - /** - * @brief Minimal constructor - * - * \p dir is set to [0, 0, 0, 0] for omnidirectional lights - * @param col The light's \ref color - */ - light(const std::shared_ptr col); - + const direction emit_dir; + light() {} /** * Explicit constructor - * @param col The light's \ref color - * @param dir The light's \ref direction + * @param emit_dir The light's \ref direction */ - light(const std::shared_ptr col, const std::shared_ptr dir); + light(const direction &emit_dir); public: /** * @brief Returns the direction the light shines on the position \p pos in. - * @param pos The \ref point (in world coordinates) to shine light on + * @param pos The \ref position (in world coordinates) to shine light on * @return The \ref direction the light is going to reach \p pos */ - virtual const std::shared_ptr get_direction(const point &pos) const = 0; + virtual const std::shared_ptr> get_directions(const position &pos, + const unsigned long &samples) const = 0; /** * @brief Returns the color the light is emitting in the direction \p dir. @@ -51,5 +42,4 @@ class light { */ virtual const std::shared_ptr emit(const direction &dir) const = 0; }; - -#endif //RAY_TRACER_LIGHT_H +#endif //RAY_TRACER_LIGHT_H \ No newline at end of file diff --git a/lib/light/mesh_light.cpp b/lib/light/mesh_light.cpp new file mode 100644 index 0000000..0aab17c --- /dev/null +++ b/lib/light/mesh_light.cpp @@ -0,0 +1,94 @@ +// +// Created by chais on 21/03/16. +// + +#include "mesh_light.h" + +mesh_light::mesh_light(const direction &offset, const std::shared_ptr &matrl, + const std::shared_ptr>> &faces) + : light(direction()), mesh(offset, matrl, faces) { } + +mesh_light::mesh_light(const std::shared_ptr &m) : light(direction()), mesh(*m) { } + +intersection mesh_light::find_farthest(const ray &r) const { + intersection out = intersection(); + ray tr = world_to_object(r); + tr.o = tr.o - offset; + const direction inv_d = direction(1 / tr.d[0], 1 / tr.d[1], 1 / tr.d[2]); + if (!intersect_quick(tr.o, inv_d)) + return out; + float dist = 0; + for (std::shared_ptr t : *faces) { + if (!t->intersect_quick(tr.o, inv_d)) + continue; + std::shared_ptr n = t->get_avg_normal(); + if (dot(*n, tr.d) > 0) + continue; + intersection cur = t->intersect_full(tr); + if (!cur.pos) + continue; + float new_dist = length(tr.o - *cur.pos); + if (new_dist > dist) { + dist = new_dist; + out = cur; + *out.pos = object_to_world(*out.pos + offset); + *out.norm = normalise(object_to_world(*out.norm)); + } + } + return out; +} + +const std::shared_ptr> mesh_light::get_directions(const position &pos, + const unsigned long &samples) const { + std::vector valid_faces = std::vector(faces->size(), 0); + for (unsigned long i = 0; i < faces->size(); i++) + valid_faces[i] = i; + position tpos = world_to_object(pos); + std::shared_ptr> out(new std::vector()); + random_sampler s; + std::vector f = *s.get_1d_samples(static_cast(0), faces->size() - 1, samples); + std::vector c = *s.get_2d_samples(0, 1, 0, 1, samples); + for (unsigned long i = 0; i < samples; i++) { + while (true) { + if (valid_faces.size() == 0) + return out; + std::vector::iterator result = std::find(valid_faces.begin(), valid_faces.end(), f[i]); + if (result == valid_faces.end()) { + f[i] = s.get_1d_samples(static_cast(0), faces->size() - 1, 1)->at(0); + continue; + } + const std::shared_ptr> n = faces->at(f[i])->get_normals(); + const std::shared_ptr> d = faces->at(f[i])->get_vertices(); + bool valid = dot(*faces->at(f[i])->get_avg_normal(), + tpos - *faces->at(f[i])->get_barycentric_position(1.0f / 3, 1.0f / 3, 1.0f / 3)) > 1e-3; + if (!valid) { + valid_faces.erase(result); + continue; + } + vec2 lcoord = vec2(c[i][0] + c[i][1] > 1 ? 1 - c[i][1] : c[i][0], + c[i][0] + c[i][1] > 1 ? 1 - c[i][0] : c[i][1]); + position lpos = object_to_world( + *faces->at(f[i])->get_barycentric_position(1 - lcoord[0] - lcoord[1], lcoord[0], lcoord[1])); + intersection closest = find_farthest(ray(lpos, pos - lpos)); + if (closest.object && dot(*closest.norm, normalise(pos - *closest.pos)) <= 0) { + std::vector>::iterator cf = std::find(faces->begin(), faces->end(), + closest.object); + result = std::find(valid_faces.begin(), valid_faces.end(), std::distance(faces->begin(), cf)); + valid_faces.erase(result); + c[i] = s.get_2d_samples(0, 1, 0, 1, 1)->at(0); + continue; + } else if (dot(*faces->at(f[i])->get_barycentric_normal(1 - lcoord[0] - lcoord[1], lcoord[0], lcoord[1]), + normalise(pos - lpos)) <= 0) { + c[i] = s.get_2d_samples(0, 1, 0, 1, 1)->at(0); + continue; + } + out->push_back((pos - lpos) * 0.999); + break; + } + } + return out; +} + +const std::shared_ptr mesh_light::emit(const direction &dir) const { + return matrl->get_emit_col(); +} \ No newline at end of file diff --git a/lib/light/mesh_light.h b/lib/light/mesh_light.h new file mode 100644 index 0000000..eb7acc7 --- /dev/null +++ b/lib/light/mesh_light.h @@ -0,0 +1,28 @@ +// +// Created by chais on 21/03/16. +// + +#ifndef RAY_TRACER_MESH_LIGHT_H +#define RAY_TRACER_MESH_LIGHT_H + +#include "light.h" +#include "../geometry/shapes/mesh.h" +#include "../sampler/random_sampler.h" + +class mesh_light : public light, public mesh { +protected: + virtual intersection find_farthest(const ray &r) const; + +public: + mesh_light(const direction &offset, const std::shared_ptr &matrl, + const std::shared_ptr>> &faces); + + mesh_light(const std::shared_ptr &m); + + virtual const std::shared_ptr> get_directions(const position &pos, + const unsigned long &samples) const override; + + virtual const std::shared_ptr emit(const direction &dir) const override; +}; + +#endif //RAY_TRACER_MESH_LIGHT_H diff --git a/lib/light/parallel_light.cpp b/lib/light/parallel_light.cpp index b18be07..3ff1e1e 100644 --- a/lib/light/parallel_light.cpp +++ b/lib/light/parallel_light.cpp @@ -4,27 +4,34 @@ #include "parallel_light.h" -parallel_light::parallel_light(const std::shared_ptr &col, const std::shared_ptr &dir) - : light(col, dir), shape(std::shared_ptr(new direction()), - std::shared_ptr()) { } +parallel_light::parallel_light(const std::shared_ptr emit_col, const direction &emit_dir) : light(emit_dir), + point(direction(), + std::shared_ptr( + new solid_material( + emit_col, + std::shared_ptr( + new color())))) { } std::ostream &operator<<(std::ostream &out, const parallel_light &a) { - out << "Parallel light: color [r, g, b]: " << *a.emit_color << ", direction: " << *a.emit_dir; + out << "Parallel light: color [r, g, b]: " << a.matrl->get_emit_col() << ", direction: " << a.emit_dir; return out; } -const std::shared_ptr parallel_light::get_direction(const point &pos) const { - return std::make_shared(object_to_world(*emit_dir) * std::numeric_limits::max()); +const std::shared_ptr> parallel_light::get_directions(const position &pos, + const unsigned long &samples) const { + std::shared_ptr> out(new std::vector()); + out->push_back(object_to_world(emit_dir) * std::numeric_limits::max()); + return out; } const std::shared_ptr parallel_light::emit(const direction &dir) const { - return emit_color; + return matrl->get_emit_col(); } -intersection parallel_light::intersect_full(const ray &r) { +intersection parallel_light::intersect_full(const ray &r) const { return intersection(); } -bool parallel_light::intersect_shadow(const point &o, const direction &d) const { +bool parallel_light::intersect_shadow(const position &o, const direction &d) const { return false; -} +} \ No newline at end of file diff --git a/lib/light/parallel_light.h b/lib/light/parallel_light.h index 9cb7418..b537dda 100644 --- a/lib/light/parallel_light.h +++ b/lib/light/parallel_light.h @@ -6,17 +6,18 @@ #define RAY_TRACER_PARALLEL_LIGHT_H #include "light.h" -#include "../geometry/shapes/shape.h" +#include "../geometry/shapes/point.h" +#include "../geometry/material/solid_material.h" /** * Models a parallel light source */ -class parallel_light: public light, public shape { +class parallel_light : public light, public point { public: /** * @copydoc light::light() */ - parallel_light(const std::shared_ptr &col, const std::shared_ptr &dir); + parallel_light(const std::shared_ptr emit_col, const direction &emit_dir); friend std::ostream &operator<<(std::ostream &out, const parallel_light &a); @@ -24,18 +25,19 @@ class parallel_light: public light, public shape { * @copybrief light::get_direction() * * Returns the \ref direction stored in \p light::dir - * @copydetails light::get_direction() + * @copydetails light::get_directions() */ - virtual const std::shared_ptr get_direction(const point &pos) const; + virtual const std::shared_ptr> get_directions(const position &pos, + const unsigned long &samples) const; /** * @copydoc ambient_light::emit() */ virtual const std::shared_ptr emit(const direction &dir) const; - virtual intersection intersect_full(const ray &r) override; + virtual intersection intersect_full(const ray &r) const override; - virtual bool intersect_shadow(const point &o, const direction &d) const override; + virtual bool intersect_shadow(const position &o, const direction &d) const override; }; #endif //RAY_TRACER_PARALLEL_LIGHT_H diff --git a/lib/light/point_light.cpp b/lib/light/point_light.cpp index 55547ef..45a6038 100644 --- a/lib/light/point_light.cpp +++ b/lib/light/point_light.cpp @@ -4,22 +4,29 @@ #include "point_light.h" -point_light::point_light(const std::shared_ptr &col, const std::shared_ptr &offset) : light(col), - shape(offset, - std::shared_ptr()) { } +point_light::point_light(const std::shared_ptr emit_col, const direction &offset) : light(direction()), + point(direction(), + std::shared_ptr( + new solid_material( + emit_col, + std::shared_ptr( + new color())))) { } -const std::shared_ptr point_light::get_direction(const point &pos) const { - return std::shared_ptr(new direction(pos - object_to_world(*offset))); +const std::shared_ptr> point_light::get_directions(const position &pos, + const unsigned long &samples) const { + std::shared_ptr> out(new std::vector()); + out->push_back(pos - object_to_world(offset)); + return out; } const std::shared_ptr point_light::emit(const direction &dir) const { - return emit_color; + return matrl->get_emit_col(); } -intersection point_light::intersect_full(const ray &r) { +intersection point_light::intersect_full(const ray &r) const { return intersection(); } -bool point_light::intersect_shadow(const point &o, const direction &d) const { +bool point_light::intersect_shadow(const position &o, const direction &d) const { return false; -} +} \ No newline at end of file diff --git a/lib/light/point_light.h b/lib/light/point_light.h index 2d601b6..115bce3 100644 --- a/lib/light/point_light.h +++ b/lib/light/point_light.h @@ -6,36 +6,38 @@ #define RAY_TRACER_POINT_LIGHT_H #include "light.h" -#include "../geometry/shapes/shape.h" +#include "../geometry/shapes/point.h" +#include "../geometry/material/solid_material.h" /** - * Models a point light source + * Models a position light source */ -class point_light: public light, public shape { +class point_light : public light, public point { public: /** * @brief Explicit constructor * @param col The \ref color of the light - * @param pos The \ref point "position" stored in \p position + * @param pos The \ref position "position" stored in \p position */ - point_light(const std::shared_ptr &col, const std::shared_ptr &offset); + point_light(const std::shared_ptr emit_col, const direction &offset); /** * @copybrief light::get_direction() * * Returns \p pos - \p position. - * \copydetails light::get_direction() + * \copydetails light::get_directions() */ - virtual const std::shared_ptr get_direction(const point &pos) const; + virtual const std::shared_ptr> get_directions(const position &pos, + const unsigned long &samples) const; /** * @copydoc ambient_light::emit() */ virtual const std::shared_ptr emit(const direction &dir) const; - virtual intersection intersect_full(const ray &r) override; + virtual intersection intersect_full(const ray &r) const override; - virtual bool intersect_shadow(const point &o, const direction &d) const override; + virtual bool intersect_shadow(const position &o, const direction &d) const override; }; #endif //RAY_TRACER_POINT_LIGHT_H diff --git a/lib/light/sphere_light.cpp b/lib/light/sphere_light.cpp new file mode 100644 index 0000000..fdaa88a --- /dev/null +++ b/lib/light/sphere_light.cpp @@ -0,0 +1,56 @@ +// +// Created by chais on 12/04/16. +// + +#include "sphere_light.h" + +sphere_light::sphere_light(const direction &emit_dir, const direction &offset, const std::shared_ptr &matrl, + const float &radius) : light(emit_dir), sphere(offset, matrl, radius) { } + +sphere_light::sphere_light(const std::shared_ptr s) : light(direction()), sphere(*s) { } + +intersection sphere_light::find_farthest(const ray &r) const { + ray tr = sphere::world_to_object(r); + direction c = position(-tr.o) + offset; + float a = std::max(float(0.0), dot(tr.d, c)); + float b = std::sqrt(std::pow(length(c), 2) - std::pow(a, 2)); + intersection out = intersection(); + if (b < radius - 1E-4) { + float d = std::sqrt(std::pow(radius, 2) - std::pow(b, 2)); + std::shared_ptr x = std::shared_ptr(new position(tr.o + direction(tr.d * (a + d)))); + std::shared_ptr norm = std::shared_ptr(new normal(*x - offset)); + float local_len = length(*norm); + std::shared_ptr local_pos = std::shared_ptr( + new vec2((*norm)[0] / local_len, (*norm)[1] / local_len)); + *x = object_to_world(*x); + *norm = normalise(object_to_world(*norm)); + out.object = shared_from_this(); + out.norm = norm; + out.pos = x; + out.local_pos = local_pos; + } + return out; +} + +const std::shared_ptr> sphere_light::get_directions(const position &pos, + const unsigned long &samples) const { + std::shared_ptr> out(new std::vector()); + random_sampler s; + std::vector c = *s.get_2d_samples(0, static_cast(2 * M_PI), 0, 1, samples); + for (unsigned long i = 0; i < samples; i++) { + c[i][1] = std::acos(2 * c[i][1] - 1); + position lpos = object_to_world( + position(std::cos(c[i][0]) * std::sin(c[i][1]), std::sin(c[i][0]) * std::sin(c[i][1]), + std::cos(c[i][1]))); + intersection closest = find_farthest(ray(lpos, pos - lpos)); + if (closest.object) + out->push_back(pos - *closest.pos); + else + out->push_back(pos - lpos); + } + return out; +} + +const std::shared_ptr sphere_light::emit(const direction &dir) const { + return matrl->get_emit_col(); +} \ No newline at end of file diff --git a/lib/light/sphere_light.h b/lib/light/sphere_light.h new file mode 100644 index 0000000..3c7822c --- /dev/null +++ b/lib/light/sphere_light.h @@ -0,0 +1,26 @@ +// +// Created by chais on 12/04/16. +// + +#ifndef RAY_TRACER_SPHERE_LIGHT_H +#define RAY_TRACER_SPHERE_LIGHT_H + +#include "light.h" +#include "../geometry/shapes/sphere.h" +#include "../sampler/random_sampler.h" + +class sphere_light : public light, public sphere { +protected: + virtual intersection find_farthest(const ray &r) const; +public: + sphere_light(const direction &emit_dir, const direction &offset, const std::shared_ptr &matrl, + const float &radius); + sphere_light(const std::shared_ptr s); + + virtual const std::shared_ptr> get_directions(const position &pos, + const unsigned long &samples) const override; + + virtual const std::shared_ptr emit(const direction &dir) const override; +}; + +#endif //RAY_TRACER_SPHERE_LIGHT_H \ No newline at end of file diff --git a/lib/parser.cpp b/lib/parser.cpp index a598c58..030ada8 100644 --- a/lib/parser.cpp +++ b/lib/parser.cpp @@ -4,19 +4,39 @@ #include "parser.h" -std::shared_ptr parser::parse(const char *in_path, std::string &out_path) { +std::shared_ptr parser::parse(const char *in_path, std::string &out_path) { pugi::xml_document doc; pugi::xml_parse_result result = doc.load_file(in_path, pugi::parse_default | pugi::parse_doctype); assert(result); pugi::xml_node scene = doc.child("scene"); out_path = scene.attribute("output_file").value(); - std::shared_ptr background_color = parse_color(scene.child("background_color")); + if (std::string(scene.attribute("renderer").value()) == "raytracer") + return parse_whitted_rt(scene); + if (std::string(scene.attribute("renderer").value()) == "pathtracer") + return parse_pathtracer(scene); + return std::shared_ptr(); +} + +std::shared_ptr parser::parse_whitted_rt(const pugi::xml_node &scene) { + const std::shared_ptr background_color = parse_color(scene.child("background_color")); std::shared_ptr cam = parse_camera(scene.child("camera")); std::shared_ptr>> lights = parse_lights(scene.child("lights")); std::shared_ptr>> geometry = parse_surfaces(scene.child("surfaces")); + for (std::shared_ptr l : *lights) + geometry->push_back(std::dynamic_pointer_cast(l)); return std::shared_ptr(new whitted_rt(background_color, cam, lights, geometry)); } +std::shared_ptr parser::parse_pathtracer(const pugi::xml_node &scene) { + const std::shared_ptr background_color = parse_color(scene.child("background_color")); + std::shared_ptr cam = parse_camera(scene.child("camera")); + std::shared_ptr>> lights = parse_lights(scene.child("lights")); + std::shared_ptr>> geometry = parse_surfaces(scene.child("surfaces")); + for (std::shared_ptr l : *lights) + geometry->push_back(std::dynamic_pointer_cast(l)); + return std::shared_ptr(new pathtracer(background_color, cam, lights, geometry)); +} + std::shared_ptr parser::parse_camera(const pugi::xml_node &cam) { if (std::string(cam.attribute("type").value()) == "perspective") return parse_perspective_camera(cam); @@ -26,33 +46,34 @@ std::shared_ptr parser::parse_camera(const pugi::xml_node &cam) { } std::shared_ptr parser::parse_perspective_camera(const pugi::xml_node &cam) { - std::shared_ptr position = parse_position(cam.child("position")); - std::shared_ptr lookat = parse_position(cam.child("lookat")); - std::shared_ptr up = parse_direction(cam.child("up")); - std::array resolution = {std::strtoul(cam.child("resolution").attribute("horizontal").value(), - nullptr, 10), - std::strtoul(cam.child("resolution").attribute("vertical").value(), - nullptr, 10)}; + position pos = parse_position(cam.child("position")); + position lookat = parse_position(cam.child("lookat")); + direction up = parse_direction(cam.child("up")); + std::array resolution = { + std::strtoul(cam.child("resolution").attribute("horizontal").value(), nullptr, 10), + std::strtoul(cam.child("resolution").attribute("vertical").value(), nullptr, 10)}; unsigned long max_bounces = std::strtoul(cam.child("max_bounces").attribute("n").value(), nullptr, 10); float fov = std::strtof(cam.child("horizontal_fov").attribute("angle").value(), nullptr); + unsigned long shadowrays = std::strtoul(cam.child("shadow_rays").attribute("n").value(), nullptr, 10); unsigned long samples = 0; std::shared_ptr s = parse_sampler(cam.child("sampling"), samples); float defocus = std::strtof(cam.child("defocus").attribute("d").value(), nullptr); std::shared_ptr out( - new perspective_camera(*position, *lookat, *up, resolution, max_bounces, fov, samples, s, defocus)); + new perspective_camera(pos, lookat, up, resolution, max_bounces, fov, + samples, shadowrays, s, defocus)); return out; } std::shared_ptr parser::parse_realistic_camera(const pugi::xml_node &cam) { - std::shared_ptr position = parse_position(cam.child("position")); - std::shared_ptr lookat = parse_position(cam.child("lookat")); - std::shared_ptr up = parse_direction(cam.child("up")); - std::array resolution = {std::strtoul(cam.child("resolution").attribute("horizontal").value(), - nullptr, 10), - std::strtoul(cam.child("resolution").attribute("vertical").value(), - nullptr, 10)}; + position pos = parse_position(cam.child("position")); + position lookat = parse_position(cam.child("lookat")); + direction up = parse_direction(cam.child("up")); + std::array resolution = { + std::strtoul(cam.child("resolution").attribute("horizontal").value(), nullptr, 10), + std::strtoul(cam.child("resolution").attribute("vertical").value(), nullptr, 10)}; unsigned long max_bounces = std::strtoul(cam.child("max_bounces").attribute("n").value(), nullptr, 10); float fov = std::strtof(cam.child("horizontal_fov").attribute("angle").value(), nullptr); + unsigned long shadowrays = std::strtoul(cam.child("shadow_rays").attribute("n").value(), nullptr, 10); unsigned long samples = 1; std::shared_ptr s = parse_sampler(cam.child("sampling"), samples); float defocus = std::strtof(cam.child("defocus").attribute("d").value(), nullptr); @@ -60,33 +81,59 @@ std::shared_ptr parser::parse_realistic_camera(const pugi::xml_node &cam focus = focus == 0 ? std::numeric_limits::max() : focus; float aperture = std::strtof(cam.child("aperture").attribute("d").value(), nullptr); std::shared_ptr out( - new realistic_camera(*position, *lookat, *up, resolution, max_bounces, fov, samples, s, defocus, focus, + new realistic_camera(pos, lookat, up, resolution, max_bounces, fov, + samples, shadowrays, s, defocus, focus, aperture)); return out; } std::shared_ptr>> parser::parse_lights(const pugi::xml_node &lights) { std::shared_ptr>> out(new std::vector>()); - out->push_back(parse_ambient_light(lights.child("ambient_light"))); + for (pugi::xml_node l : lights.children("ambient_light")) + out->push_back(parse_ambient_light(l)); for (pugi::xml_node l : lights.children("parallel_light")) out->push_back(parse_parallel_light(l)); for (pugi::xml_node l : lights.children("point_light")) out->push_back(parse_point_light(l)); + for (pugi::xml_node l : lights.children("lambertian_light")) + out->push_back(parse_lambertian_light(l)); + for (pugi::xml_node l : lights.children("mesh_light")) + out->push_back(parse_mesh_light(l)); + for (pugi::xml_node l : lights.children("sphere_light")) + out->push_back(parse_sphere_light(l)); return out; } std::shared_ptr parser::parse_ambient_light(const pugi::xml_node &l) { - return std::make_shared(ambient_light(parse_color(l.child("color")))); + return std::shared_ptr(new ambient_light(parse_color(l.child("color")))); } std::shared_ptr parser::parse_parallel_light(const pugi::xml_node &l) { - return std::make_shared(parallel_light(parse_color(l.child("color")), - parse_direction(l.child("direction")))); + return std::shared_ptr(new parallel_light(parse_color(l.child("color")), + parse_direction(l.child("direction")))); } std::shared_ptr parser::parse_point_light(const pugi::xml_node &l) { - return std::make_shared(point_light(parse_color(l.child("color")), - parse_direction(l.child("position")))); + return std::shared_ptr(new point_light(parse_color(l.child("color")), + parse_direction(l.child("position")))); +} + +std::shared_ptr parser::parse_lambertian_light(const pugi::xml_node &l) { + return std::shared_ptr(new lambertian_light(parse_color(l.child("color")), + parse_direction(l.child("direction")), + parse_direction(l.child("position")))); +} + +std::shared_ptr parser::parse_mesh_light(const pugi::xml_node &l) { + std::shared_ptr m = parse_mesh(l.child("mesh")); + parse_transform(l.child("mesh").child("transform"), m); + return std::shared_ptr(new mesh_light(m)); +} + +std::shared_ptr parser::parse_sphere_light(const pugi::xml_node &l) { + std::shared_ptr s = parse_sphere(l.child("sphere")); + parse_transform(l.child("sphere").child("transform"), s); + return std::shared_ptr(new sphere_light(s)); } std::shared_ptr>> parser::parse_surfaces(const pugi::xml_node &surfaces) { @@ -102,11 +149,10 @@ std::shared_ptr>> parser::parse_surfaces(cons return out; } -std::shared_ptr parser::parse_sphere(pugi::xml_node &s) { +std::shared_ptr parser::parse_sphere(const pugi::xml_node &s) { std::shared_ptr matrl = parse_material(s.child("material")); - return std::make_shared(sphere(std::strtof(s.attribute("radius").value(), nullptr), - parse_direction(s.child("position")), - matrl)); + return std::make_shared( + sphere(parse_direction(s.child("position")), matrl, std::strtof(s.attribute("radius").value(), nullptr))); } std::shared_ptr parser::parse_mesh(const pugi::xml_node &m) { @@ -118,15 +164,18 @@ std::shared_ptr parser::parse_mesh(const pugi::xml_node &m) { exit(1); } std::shared_ptr>> triangles(new std::vector>()); + std::shared_ptr matrl; + if (materials.empty()) + matrl = parse_material(m.child("material")); for (tinyobj::shape_t s : shapes) for (int i = 0; i < s.mesh.indices.size() / 3; i++) { - std::shared_ptr> vertices(new std::array()); + std::shared_ptr> vertices(new std::array()); std::shared_ptr> normals(new std::array()); std::shared_ptr> texture_coords(new std::array()); for (unsigned long ii = 0; ii < 3; ii++) { - vertices->at(ii) = point(s.mesh.positions[s.mesh.indices[i * 3 + ii] * 3], - s.mesh.positions[s.mesh.indices[i * 3 + ii] * 3 + 1], - s.mesh.positions[s.mesh.indices[i * 3 + ii] * 3 + 2]); + vertices->at(ii) = position(s.mesh.positions[s.mesh.indices[i * 3 + ii] * 3], + s.mesh.positions[s.mesh.indices[i * 3 + ii] * 3 + 1], + s.mesh.positions[s.mesh.indices[i * 3 + ii] * 3 + 2]); normals->at(ii) = normalise(normal(s.mesh.normals[s.mesh.indices[i * 3 + ii] * 3], s.mesh.normals[s.mesh.indices[i * 3 + ii] * 3 + 1], s.mesh.normals[s.mesh.indices[i * 3 + ii] * 3 + 2])); @@ -136,12 +185,10 @@ std::shared_ptr parser::parse_mesh(const pugi::xml_node &m) { else texture_coords->at(ii) = vec2(); } - triangles->push_back(std::make_shared(triangle(vertices, normals, texture_coords))); + triangles->push_back( + std::make_shared(triangle(direction(), matrl, vertices, normals, texture_coords))); } - std::shared_ptr matrl; - if (materials.empty()) - matrl = parse_material(m.child("material")); - return std::shared_ptr(new mesh(std::shared_ptr(new direction()), matrl, triangles)); + return std::shared_ptr(new mesh(direction(), matrl, triangles)); } void parser::parse_transform(const pugi::xml_node &t, std::shared_ptr s) { @@ -160,104 +207,75 @@ void parser::parse_transform(const pugi::xml_node &t, std::shared_ptr s) else if (std::string(r.name()) == "rotateZ") s->rotateZ(std::strtof(r.attribute("theta").value(), nullptr)); else if (std::string(r.name()) == "translate") { - std::shared_ptr d = parse_direction(r); - s->translate(*d); + direction d = parse_direction(r); + s->translate(d); } } } std::shared_ptr parser::parse_material(const pugi::xml_node &m) { - if (std::string(m.attribute("type").value()) == "phong") { - return std::make_shared(phong_material(*parse_color(m.child("color")), - std::strtof(m.child("phong").attribute("ka").value(), - nullptr), - std::strtof(m.child("phong").attribute("kd").value(), - nullptr), - std::strtof(m.child("phong").attribute("ks").value(), - nullptr), - std::strtof( - m.child("phong").attribute("exponent").value(), - nullptr))); - } else if (std::string(m.attribute("type").value()) == "lambertian") { - return std::make_shared(lambertian_material(*parse_color(m.child("color")), - std::strtof(m.child("lambertian").attribute( - "ka").value(), - nullptr), - std::strtof(m.child("lambertian").attribute( - "kd").value(), - nullptr))); - } else if (std::string(m.attribute("type").value()) == "specular") { - return std::make_shared(specular_material(*parse_color(m.child("color")), - std::strtof( - m.child("phong").attribute("ka").value(), - nullptr), - std::strtof( - m.child("phong").attribute("kd").value(), - nullptr), - std::strtof( - m.child("phong").attribute("ks").value(), - nullptr), - std::strtof(m.child("phong").attribute( - "exponent").value(), - nullptr), - std::strtof(m.child("reflectance").attribute( - "r").value(), - nullptr))); - } else if (std::string(m.attribute("type").value()) == "transparent") { - return std::make_shared(transparent_material(*parse_color(m.child("color")), - std::strtof(m.child("phong").attribute( - "ka").value(), - nullptr), - std::strtof(m.child("phong").attribute( - "kd").value(), - nullptr), - std::strtof(m.child("phong").attribute( - "ks").value(), - nullptr), - std::strtof(m.child("phong").attribute( - "exponent").value(), - nullptr), - std::strtof(m.child("reflectance").attribute( - "r").value(), - nullptr), - std::strtof( - m.child("transmittance").attribute( - "t").value(), - nullptr), - std::strtof(m.child("refraction").attribute( - "iof").value(), - nullptr))); - } else if (std::string(m.attribute("type").value()) == "textured") { + if (std::string(m.attribute("type").value()) == "solid") + return std::make_shared( + solid_material(parse_color(m.child("emit")), parse_color(m.child("color")))); + if (std::string(m.attribute("type").value()) == "lambertian") + return std::make_shared( + lambertian_material(parse_color(m.child("emit")), parse_color(m.child("color")), + std::strtof(m.child("lambertian").attribute("ka").value(), nullptr), + std::strtof(m.child("lambertian").attribute("kd").value(), nullptr))); + if (std::string(m.attribute("type").value()) == "phong") + return std::make_shared( + phong_material(parse_color(m.child("emit")), parse_color(m.child("color")), + std::strtof(m.child("phong").attribute("ka").value(), nullptr), + std::strtof(m.child("phong").attribute("kd").value(), nullptr), + std::strtof(m.child("phong").attribute("ks").value(), nullptr), + std::strtof(m.child("phong").attribute("exponent").value(), nullptr))); + if (std::string(m.attribute("type").value()) == "specular") + return std::make_shared( + specular_material(parse_color(m.child("emit")), parse_color(m.child("color")), + std::strtof(m.child("phong").attribute("ka").value(), nullptr), + std::strtof(m.child("phong").attribute("kd").value(), nullptr), + std::strtof(m.child("phong").attribute("ks").value(), nullptr), + std::strtof(m.child("phong").attribute("exponent").value(), nullptr), + std::strtof(m.child("reflectance").attribute("r").value(), nullptr))); + if (std::string(m.attribute("type").value()) == "transparent") + return std::make_shared( + transparent_material(parse_color(m.child("emit")), parse_color(m.child("color")), + std::strtof(m.child("phong").attribute("ka").value(), nullptr), + std::strtof(m.child("phong").attribute("kd").value(), nullptr), + std::strtof(m.child("phong").attribute("ks").value(), nullptr), + std::strtof(m.child("phong").attribute("exponent").value(), nullptr), + std::strtof(m.child("reflectance").attribute("r").value(), nullptr), + std::strtof(m.child("transmittance").attribute("t").value(), nullptr), + std::strtof(m.child("refraction").attribute("iof").value(), nullptr))); + if (std::string(m.attribute("type").value()) == "textured") return std::make_shared( - textured_material(std::strtof(m.child("phong").attribute("ka").value(), - nullptr), - std::strtof(m.child("phong").attribute("kd").value(), - nullptr), - std::strtof(m.child("phong").attribute("ks").value(), - nullptr), - std::strtof(m.child("phong").attribute("exponent").value(), - nullptr), + textured_material(parse_color(m.child("emit")), + std::strtof(m.child("phong").attribute("ka").value(), nullptr), + std::strtof(m.child("phong").attribute("kd").value(), nullptr), + std::strtof(m.child("phong").attribute("ks").value(), nullptr), + std::strtof(m.child("phong").attribute("exponent").value(), nullptr), load_image(m.child("texture").attribute("name").value()))); - } return std::shared_ptr(nullptr); } std::shared_ptr parser::parse_color(const pugi::xml_node &col) { - return std::make_shared(color(std::strtof(col.attribute("r").value(), nullptr), - std::strtof(col.attribute("g").value(), nullptr), - std::strtof(col.attribute("b").value(), nullptr))); + return std::shared_ptr(new color(std::strtof(col.attribute("r").value(), nullptr), + std::strtof(col.attribute("g").value(), nullptr), + std::strtof(col.attribute("b").value(), nullptr))); } -std::shared_ptr parser::parse_position(const pugi::xml_node &p) { - return std::make_shared(point(std::strtof(p.attribute("x").value(), nullptr), - std::strtof(p.attribute("y").value(), nullptr), - std::strtof(p.attribute("z").value(), nullptr))); +position parser::parse_position(const pugi::xml_node &p) { + position out = position(std::strtof(p.attribute("x").value(), nullptr), + std::strtof(p.attribute("y").value(), nullptr), + std::strtof(p.attribute("z").value(), nullptr)); + return out; } -std::shared_ptr parser::parse_direction(const pugi::xml_node &d) { - return std::make_shared(direction(std::strtof(d.attribute("x").value(), nullptr), - std::strtof(d.attribute("y").value(), nullptr), - std::strtof(d.attribute("z").value(), nullptr))); +direction parser::parse_direction(const pugi::xml_node &d) { + direction out = direction(std::strtof(d.attribute("x").value(), nullptr), + std::strtof(d.attribute("y").value(), nullptr), + std::strtof(d.attribute("z").value(), nullptr)); + return out; } std::shared_ptr>> parser::load_image(const std::string path) { @@ -267,9 +285,8 @@ std::shared_ptr>> parser::load_image(const std::s for (unsigned int y = 0; y < img.height(); y++) { out->push_back(std::vector()); out->at(y).reserve(static_cast(img.width())); - for (unsigned int x = 0; x < img.width(); x++) { + for (unsigned int x = 0; x < img.width(); x++) out->at(y).push_back(color(img(x, y, 0) / 255.0f, img(x, y, 1) / 255.0f, img(x, y, 2) / 255.0f)); - } } return out; } @@ -284,4 +301,4 @@ std::shared_ptr parser::parse_sampler(const pugi::xml_node &node, unsig std::shared_ptr parser::parse_random_sampler(const pugi::xml_node &node) { return std::shared_ptr(new random_sampler()); -} +} \ No newline at end of file diff --git a/lib/parser.h b/lib/parser.h index e830a10..18457e7 100644 --- a/lib/parser.h +++ b/lib/parser.h @@ -5,12 +5,16 @@ #ifndef RAY_TRACER_PARSER_H #define RAY_TRACER_PARSER_H -#include "whitted_rt.h" +#include "renderer/whitted_rt.h" +#include "renderer/pathtracer.h" #include "camera/perspective_camera.h" #include "camera/realistic_camera.h" #include "light/ambient_light.h" #include "light/parallel_light.h" #include "light/point_light.h" +#include "light/lambertian_light.h" +#include "light/mesh_light.h" +#include "light/sphere_light.h" #include "geometry/shapes/sphere.h" #include "geometry/shapes/mesh.h" #include "geometry/material/phong_material.h" @@ -70,12 +74,18 @@ class parser { static std::shared_ptr parse_parallel_light(const pugi::xml_node &l); /** - * @brief Parses a point light + * @brief Parses a position light * @param l The \ node * @return A shared_ptr to a point_light object */ static std::shared_ptr parse_point_light(const pugi::xml_node &l); + static std::shared_ptr parse_lambertian_light(const pugi::xml_node &l); + + static std::shared_ptr parse_mesh_light(const pugi::xml_node &l); + + static std::shared_ptr parse_sphere_light(const pugi::xml_node &l); + /** * @brief Parses all surfaces into a vector * @@ -90,7 +100,7 @@ class parser { * @param s The \ node * @return A shared_ptr to a sphere object */ - static std::shared_ptr parse_sphere(pugi::xml_node &s); + static std::shared_ptr parse_sphere(const pugi::xml_node &s); /** * @brief Parses a mesh @@ -127,16 +137,16 @@ class parser { /** * @brief Parses a position * @param p The \ node - * @return A shared_ptr to a point object + * @return A shared_ptr to a position object */ - static std::shared_ptr parse_position(const pugi::xml_node &p); + static position parse_position(const pugi::xml_node &p); /** * @brief Parses a direction * @param d The \ node * @return A shared_ptr to a direction object */ - static std::shared_ptr parse_direction(const pugi::xml_node &d); + static direction parse_direction(const pugi::xml_node &d); /** * @brief Loads an image from the disk @@ -151,6 +161,7 @@ class parser { static std::shared_ptr parse_random_sampler(const pugi::xml_node &node); + static std::shared_ptr parse_whitted_rt(const pugi::xml_node &scene); public: /** * @brief Parses an XML file @@ -158,7 +169,9 @@ class parser { * @param out_path The variable to store the output path (specified in the XML file) * @return A shared_ptr to a whitted_rt object that complies to the specifications in the XML file */ - static std::shared_ptr parse(const char *in_path, std::string &out_path); + static std::shared_ptr parse(const char *in_path, std::string &out_path); + + static std::shared_ptr parse_pathtracer(const pugi::xml_node &scene); }; -#endif //RAY_TRACER_PARSER_H +#endif //RAY_TRACER_PARSER_H \ No newline at end of file diff --git a/lib/renderer/pathtracer.cpp b/lib/renderer/pathtracer.cpp new file mode 100644 index 0000000..75ff45f --- /dev/null +++ b/lib/renderer/pathtracer.cpp @@ -0,0 +1,72 @@ +// +// Created by chais on 01/04/16. +// + +#include "pathtracer.h" + +pathtracer::pathtracer(const std::shared_ptr &background_color, const std::shared_ptr &cam, + const std::shared_ptr>> &lights, + const std::shared_ptr>> &scene) : renderer(background_color, + cam, lights, + scene) { } + +color pathtracer::cast_ray(ray r, int step, bool internal) { + intersection is = find_nearest(r); + if (!is.object) // No intersection + return *background_color; + color out = color(); + for (std::shared_ptr l : *lights) { + color lcol = color(); + if (is.object.get() == dynamic_cast(l.get())) { + out += *is.object->shade(color(), direction(), *is.norm, -r.d, *is.local_pos, internal); + continue; + } + const std::shared_ptr> dirs = l->get_directions(*is.pos, cam->shadow_rays); + for (direction light_dir : *dirs) + if (!cast_shadow(*is.pos, -light_dir)) { + light_dir = normalise(light_dir); + lcol += *is.object->shade(*l->emit(light_dir), -light_dir, *is.norm, -r.d, *is.local_pos, internal); + } + if (dirs->size() > 0) + out += lcol * (1.0f / dirs->size()); + } + if (step < cam->max_bounces-1) { + std::shared_ptr sctr_ray(new ray(*is.pos, *rnd_in_cone(*is.norm, static_cast(M_PI/2)))); + color sctr_col = *is.object->shade(cast_ray(*sctr_ray, step + 1, internal), sctr_ray->d, *is.norm, -r.d, *is.local_pos, internal); + out += sctr_col;// * dot(*is.norm, sctr_ray->d); + std::shared_ptr refl_ray = internal ? nullptr : is.object->reflect(-r.d, *is.norm, *is.pos); + if (refl_ray) { + color refl_col = cast_ray(*refl_ray, step + 1, internal); + refl_col = refl_col * is.object->get_reflectance(); + out += refl_col; + } + std::shared_ptr refr_ray(is.object->refract(-r.d, internal ? -*is.norm : *is.norm, *is.pos, internal)); + if (refr_ray) { + color refr_col = color(); + refr_col += cast_ray(*refr_ray, step + 1, + dot(refr_ray->d, internal ? -*is.norm : *is.norm) < 0 == !internal); + refr_col = refr_col * (internal ? std::exp(-(1 - is.object->get_transmittance()) * length(*is.pos - r.o)) + : is.object->get_transmittance()); + out += refr_col; + } + } + return out; +} + +std::shared_ptr pathtracer::rnd_in_cone(const direction ¢er, const float &angle) const { + assert(angle <= static_cast(M_PI)); + float x = std::abs(center[0]), y = std::abs(center[1]), z = std::abs(center[2]); + float min = std::min(x, std::min(y, z)); + direction ax = direction(); + if (min == x) + ax[0] = 1; + else if (min == y) + ax[1] = 1; + else + ax[2] = 1; + direction u = cross(center, ax), v = cross(center, u); + vec2 s = cam->s->get_2d_samples(0, static_cast(2 * M_PI), std::cos(angle), 1, 1)->at(0); + s[1] = std::acos(s[1]); + return std::make_shared( + std::sin(s[1]) * (std::cos(s[0]) * u + std::sin(s[0]) * v) + std::cos(s[1]) * center); +} \ No newline at end of file diff --git a/lib/renderer/pathtracer.h b/lib/renderer/pathtracer.h new file mode 100644 index 0000000..a1947f0 --- /dev/null +++ b/lib/renderer/pathtracer.h @@ -0,0 +1,21 @@ +// +// Created by chais on 01/04/16. +// + +#ifndef RAY_TRACER_PATHTRACER_H +#define RAY_TRACER_PATHTRACER_H + +#include "renderer.h" + +class pathtracer : public renderer { +protected: + virtual color cast_ray(ray r, int step, bool internal) override; + virtual std::shared_ptr rnd_in_cone(const direction ¢er, const float &angle) const; + +public: + pathtracer(const std::shared_ptr &background_color, const std::shared_ptr &cam, + const std::shared_ptr>> &lights, + const std::shared_ptr>> &scene); +}; + +#endif //RAY_TRACER_PATHTRACER_H \ No newline at end of file diff --git a/lib/renderer/renderer.cpp b/lib/renderer/renderer.cpp new file mode 100644 index 0000000..1632e6c --- /dev/null +++ b/lib/renderer/renderer.cpp @@ -0,0 +1,65 @@ +// +// Created by chais on 01/04/16. +// + +#include +#include "renderer.h" + +renderer::renderer(const std::shared_ptr &background_color, const std::shared_ptr &cam, + const std::shared_ptr>> &lights, + const std::shared_ptr>> &scene) : background_color( + background_color), cam(cam), lights(lights), scene(scene) { } + +bool renderer::cast_shadow(const position &o, const direction &d) { + for (std::shared_ptr obstacle : *scene) + if (obstacle->intersect_shadow(o, d)) + return true; + return false; +} + +intersection renderer::find_nearest(ray r) { + intersection out = intersection(); + float dist = std::numeric_limits::max(); + for (std::shared_ptr object : *scene) { + intersection cur = object->intersect_full(r); + if (!cur.object) + continue; + float new_dist = length(r.o - *cur.pos); + if (new_dist < dist) { + dist = new_dist; + out = cur; + } + } + return out; +} + +std::shared_ptr renderer::get_pixel(const unsigned long &x, const unsigned long &y) const { + return cam->get_pixel(x, y); +} + +const std::array &renderer::get_resolution() const { + return cam->resolution; +} + +void renderer::render() { + const std::array resolution = cam->resolution; + float completed = 0; + float perline = 100.0f/resolution[1]; + std::cout << '\r' << std::setw(6) << std::fixed << std::setprecision(2) << completed << " %"; + std::cout.flush(); + for (unsigned long y = 0; y < resolution[1]; y++) { +#ifdef NDEBUG +#pragma omp parallel for +#endif + for (unsigned long x = 0; x < resolution[0]; x++) { + std::shared_ptr> rays = cam->get_rays(x, y); + std::vector data; + data.reserve(rays->size()); + for (auto &r : *rays) + data.push_back(cast_ray(r, 0, false)); + cam->set_data(x, y, data); + } + std::cout << '\r' << std::setw(6) << std::fixed << std::setprecision(2) << (completed+=perline) << " %"; + std::cout.flush(); + } +} \ No newline at end of file diff --git a/lib/renderer/renderer.h b/lib/renderer/renderer.h new file mode 100644 index 0000000..d8faf55 --- /dev/null +++ b/lib/renderer/renderer.h @@ -0,0 +1,73 @@ +// +// Created by chais on 01/04/16. +// + +#ifndef RAY_TRACER_RENDERER_H +#define RAY_TRACER_RENDERER_H + +#include "../geometry/color.h" +#include "../camera/camera.h" +#include "../light/light.h" +#include "../geometry/shapes/shape.h" +#include + +class renderer { +protected: + const std::shared_ptr background_color; + std::shared_ptr cam; + std::shared_ptr>> lights; + std::shared_ptr>> scene; + + renderer(const std::shared_ptr &background_color, const std::shared_ptr &cam, + const std::shared_ptr>> &lights, + const std::shared_ptr>> &scene); + + /** + * @brief Casts the ray \p r and returns the color it produces. + * + * To do this it first determines the object closest to \p r's origin in \p r's direction. It then shades the position of intersection according to the light situation and material. If necessary it recurses to handle reflection and refraction. The colors of reflected/refracted rays are added to the local color after multiplication with a material factor. If step reaches the camera's \p max_bounces no recursion will be done. + * @param r The ray + * @param step The current recursion level + * @param internal A flag for whether or not the ray is inside a geometry + * @return The color produced by that ray + */ + virtual color cast_ray(ray r, int step, bool internal) = 0; + + /** + * @brief Casts a shadow ray from the position \p o in the direction of \p d. + * + * \p d is expected to be not normalised and position exactly on the light source. If any geometry intersects that ray between \p o and \verbatim o+d\endverbatim \p o is considered to be in the shadow with respect to that light source. + * @param o The position (in world coordinates) to check + * @param d The vector from \p o to the light source + * @return true if any geometry lies between \p o and the light source, false otherwise + */ + bool cast_shadow(const position &o, const direction &d); + + /** + * @brief Finds the object closest to \p r.o in the direction \p r.d. + * + * Checks for intersection of every object in \p scene with \p r. If an intersection is found the distance to \p r.o is calculated. If it is shorter than the previous one the new intersection replaces the old one. + * @param r The ray to intersect with \p scene + * @return If an intersection was found the intersection object contains a reference to the geometry that intersected as well as important surface properties and the world coordinates of the intersection. If no intersection was found the intersection object contains only null pointers. + */ + intersection find_nearest(ray r); + +public: + /** + * @brief Fetches the color of a pixel from the camera and returns it. + * @param x The x-coordinate of the pixel + * @param y the y-coordinate of the pixel + * @return The color of the pixel + */ + std::shared_ptr get_pixel(const unsigned long &x, const unsigned long &y) const; + + /** + * @brief Returns the resolution of the camera and thus the image + * @return The resolution as a std::array + */ + const std::array &get_resolution() const; + + void render(); +}; + +#endif //RAY_TRACER_RENDERER_H \ No newline at end of file diff --git a/lib/renderer/whitted_rt.cpp b/lib/renderer/whitted_rt.cpp new file mode 100644 index 0000000..42b6617 --- /dev/null +++ b/lib/renderer/whitted_rt.cpp @@ -0,0 +1,50 @@ +// +// Created by chais on 08.07.15. +// + +#include "whitted_rt.h" + +whitted_rt::whitted_rt(const std::shared_ptr background_color, std::shared_ptr cam, + std::shared_ptr>> lights, + std::shared_ptr>> scene) : renderer(background_color, cam, + lights, scene) { } + +color whitted_rt::cast_ray(ray r, int step, bool internal) { + intersection is = find_nearest(r); + if (!is.object) // No intersection + return *background_color; + color out = color(); + for (std::shared_ptr l : *lights) { + color lcol = color(); + if (is.object.get() == dynamic_cast(l.get())) { + out += *is.object->shade(color(), direction(), *is.norm, -r.d, *is.local_pos, internal); + continue; + } + const std::shared_ptr> dirs = l->get_directions(*is.pos, cam->shadow_rays); + for (direction light_dir : *dirs) + if (!cast_shadow(*is.pos, -light_dir)) { + light_dir = normalise(light_dir); + lcol += *is.object->shade(*l->emit(light_dir), -light_dir, *is.norm, -r.d, *is.local_pos, internal); + } + if (dirs->size() > 0) + out += lcol * (1.0f / dirs->size()); + } + if (step < cam->max_bounces-1) { + std::shared_ptr refl_ray = internal ? nullptr : is.object->reflect(-r.d, *is.norm, *is.pos); + if (refl_ray) { + color refl_col = cast_ray(*refl_ray, step + 1, internal); + refl_col = refl_col * is.object->get_reflectance(); + out += refl_col; + } + std::shared_ptr refr_ray(is.object->refract(-r.d, internal ? -*is.norm : *is.norm, *is.pos, internal)); + if (refr_ray) { + color refr_col = color(); + refr_col += cast_ray(*refr_ray, step + 1, + dot(refr_ray->d, internal ? -*is.norm : *is.norm) < 0 == !internal); + refr_col = refr_col * (internal ? std::exp(-(1 - is.object->get_transmittance()) * length(*is.pos - r.o)) + : is.object->get_transmittance()); + out += refr_col; + } + } + return out; +} \ No newline at end of file diff --git a/lib/renderer/whitted_rt.h b/lib/renderer/whitted_rt.h new file mode 100644 index 0000000..adef676 --- /dev/null +++ b/lib/renderer/whitted_rt.h @@ -0,0 +1,24 @@ +/** + * @file whittedrt.h + * @author Philip Abernethy + */ + +#ifndef RAY_TRACER_WHITTEDRT_H +#define RAY_TRACER_WHITTEDRT_H + +#include "renderer.h" + +/** + * @brief A whitted ray tracer. + */ +class whitted_rt : public renderer { +protected: + virtual color cast_ray(ray r, int step, bool internal) override; + +public: + whitted_rt(const std::shared_ptr background_color, std::shared_ptr cam, + std::shared_ptr>> lights, + std::shared_ptr>> scene); +}; + +#endif //RAY_TRACER_WHITTEDRT_H \ No newline at end of file diff --git a/lib/sampler/random_sampler.cpp b/lib/sampler/random_sampler.cpp index 7fe15ce..799e5f0 100644 --- a/lib/sampler/random_sampler.cpp +++ b/lib/sampler/random_sampler.cpp @@ -6,20 +6,45 @@ random_sampler::random_sampler() { } -std::shared_ptr> random_sampler::get_samples(const float &width, const float &height, - const unsigned long &count) const { - assert(width >= 0); - assert(height >= 0); +std::shared_ptr> random_sampler::get_1d_samples(const float &lower, const float &upper, + const unsigned long &count) const { assert(count > 0); - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_real_distribution wdis(-width / 2, - std::nextafter(width / 2, std::numeric_limits::max())); - std::uniform_real_distribution hdis(-height / 2, - std::nextafter(height / 2, std::numeric_limits::max())); - std::shared_ptr> out = std::make_shared>(); - for (int i = 0; i < count; i++) { - out->push_back(vec2(wdis(gen), hdis(gen))); - } + std::uniform_real_distribution dis(lower, upper); + std::shared_ptr> out(new std::vector()); + for (unsigned long i = 0; i < count; i++) + out->push_back(dis(gen)); + return out; +} + +std::shared_ptr> random_sampler::get_1d_samples(const long &lower, const long &upper, + const unsigned long &count) const { + assert(count > 0); + std::uniform_int_distribution dis(lower, upper); + std::shared_ptr> out(new std::vector()); + for (unsigned long i = 0; i < count; i++) + out->push_back(dis(gen)); + return out; +} + +std::shared_ptr> random_sampler::get_1d_samples(const unsigned long &lower, const unsigned long &upper, + const unsigned long &count) const { + assert(count > 0); + std::uniform_int_distribution dis(lower, upper); + std::shared_ptr> out(new std::vector()); + for (unsigned long i = 0; i < count; i++) + out->push_back(dis(gen)); + return out; +} + +std::shared_ptr> random_sampler::get_2d_samples(const float &lowerx, const float &upperx, + const float &lowery, + const float &uppery, + const unsigned long &count) const { + assert(count > 0); + std::uniform_real_distribution xdis(lowerx, upperx); + std::uniform_real_distribution ydis(lowery, uppery); + std::shared_ptr> out(new std::vector()); + for (int i = 0; i < count; i++) + out->push_back(vec2(xdis(gen), ydis(gen))); return out; } diff --git a/lib/sampler/random_sampler.h b/lib/sampler/random_sampler.h index ffd99d8..b041f48 100644 --- a/lib/sampler/random_sampler.h +++ b/lib/sampler/random_sampler.h @@ -11,9 +11,17 @@ class random_sampler : public sampler { public: random_sampler(); - virtual std::shared_ptr> get_samples(const float &width, const float &height, + virtual std::shared_ptr> get_1d_samples(const float &lower, const float &upper, const unsigned long &count) const override; -}; + virtual std::shared_ptr> get_1d_samples(const long &lower, const long &upper, + const unsigned long &count) const override; + + virtual std::shared_ptr> get_1d_samples(const unsigned long &lower, const unsigned long &upper, + const unsigned long &count) const override; -#endif //RAY_TRACER_RANDOM_SAMPLER_H + virtual std::shared_ptr> get_2d_samples(const float &lowerx, const float &upperx, + const float &lowery, const float &uppery, + const unsigned long &count) const override; +}; +#endif //RAY_TRACER_RANDOM_SAMPLER_H \ No newline at end of file diff --git a/lib/sampler/sampler.cpp b/lib/sampler/sampler.cpp index d66c4a2..ea0622b 100644 --- a/lib/sampler/sampler.cpp +++ b/lib/sampler/sampler.cpp @@ -4,16 +4,38 @@ #include "sampler.h" -sampler::sampler() { } +sampler::sampler() : gen(rd()) { +} -std::shared_ptr> sampler::get_samples(const float &width, const float &height, +std::shared_ptr> sampler::get_1d_samples(const float &lower, const float &upper, const unsigned long &count) const { - assert(width >= 0); - assert(height >= 0); + assert(count > 0); + std::shared_ptr> out = std::make_shared>(); + out->push_back(0); + return out; +} + +std::shared_ptr> sampler::get_1d_samples(const long &lower, const long &upper, + const unsigned long &count) const { + assert(count > 0); + std::shared_ptr> out = std::make_shared>(); + out->push_back(0); + return out; +} + +std::shared_ptr> sampler::get_1d_samples(const unsigned long &lower, const unsigned long &upper, + const unsigned long &count) const { + assert(count > 0); + std::shared_ptr> out = std::make_shared>(); + out->push_back(0); + return out; +} + +std::shared_ptr> sampler::get_2d_samples(const float &lowerx, const float &upperx, + const float &lowery, + const float &uppery, const unsigned long &count) const { assert(count > 0); std::shared_ptr> out = std::make_shared>(); - for (int i = 0; i < count; i++) { - out->push_back(vec2()); - } + out->push_back(vec2()); return out; } \ No newline at end of file diff --git a/lib/sampler/sampler.h b/lib/sampler/sampler.h index c7161f1..f4de278 100644 --- a/lib/sampler/sampler.h +++ b/lib/sampler/sampler.h @@ -5,17 +5,30 @@ #ifndef RAY_TRACER_SAMPLER_H #define RAY_TRACER_SAMPLER_H -#include +#include #include "../math/vec2.h" +#include "../geometry/direction.h" #include #include class sampler { +protected: + std::random_device rd; + mutable std::mt19937 gen; public: sampler(); - virtual std::shared_ptr> get_samples(const float &width, const float &height, - const unsigned long &count) const; -}; + virtual std::shared_ptr> get_1d_samples(const float &lower, const float &upper, + const unsigned long &count) const; + + virtual std::shared_ptr> get_1d_samples(const long &lower, const long &upper, + const unsigned long &count) const; + virtual std::shared_ptr> get_1d_samples(const unsigned long &lower, const unsigned long &upper, + const unsigned long &count) const; + + virtual std::shared_ptr> get_2d_samples(const float &lowerx, const float &upperx, + const float &lowery, + const float &uppery, const unsigned long &count) const; +}; #endif //RAY_TRACER_SAMPLER_H \ No newline at end of file diff --git a/lib/tiny_obj_loader.cpp b/lib/tiny_obj_loader.cpp index 5a29fa1..ddbc948 100644 --- a/lib/tiny_obj_loader.cpp +++ b/lib/tiny_obj_loader.cpp @@ -101,7 +101,7 @@ static inline int parseInt(const char *&token) { return i; } -// Tries to parse a floating point number located at s. +// Tries to parse a floating position number located at s. // // s_end should be a location in the string where reading should absolutely // stop. For example at the end of the string, to prevent buffer overflows. diff --git a/lib/whitted_rt.cpp b/lib/whitted_rt.cpp deleted file mode 100644 index 78bc41c..0000000 --- a/lib/whitted_rt.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// -// Created by chais on 08.07.15. -// - -#include "whitted_rt.h" - -whitted_rt::whitted_rt(std::shared_ptr background_color, - std::shared_ptr cam, - std::shared_ptr>> lights, - std::shared_ptr>> scene) : background_color( - background_color), cam(cam), lights(lights), scene(scene) { } - -color whitted_rt::cast_ray(ray r, int step, bool internal) { - intersection is = find_nearest(r); - if (!is.object) // No intersection - return *background_color; - color out = color(); - for (std::shared_ptr l : *lights) { - const std::shared_ptr light_dir(l->get_direction(*is.pos)); - if (!cast_shadow(*is.pos, -*light_dir)) { - *light_dir = normalise(*light_dir); - out += *is.object->shade(*l->emit(*light_dir), -*light_dir, *is.norm, -r.d, *is.local_pos, internal); - } - } - if (step < this->cam->get_max_bounces()) { - std::shared_ptr> - refl_ray = internal ? nullptr : is.object->reflect(-r.d, *is.norm, *is.pos, 1); - if (refl_ray != nullptr) { - color refl_col = color(); - for (ray &rr : *refl_ray) { - refl_col += this->cast_ray(rr, step + 1, internal); - } - refl_col = (refl_col * (1 / refl_ray->size())) * is.object->get_reflectance(); - out += refl_col; - } - std::shared_ptr> - refr_ray(is.object->refract(-r.d, internal ? -*is.norm : *is.norm, *is.pos, internal)); - if (refr_ray) { - color refr_col = color(); - for (ray &rr : *refr_ray) { - refr_col += this->cast_ray(rr, step + 1, dot(rr.d, internal ? -*is.norm : *is.norm) < 0 == !internal); - } - refr_col = (refr_col * (1 / refr_ray->size())) * - (internal ? std::exp(-(1-is.object->get_transmittance()) * length(*is.pos - r.o)) - : is.object->get_transmittance()); - out += refr_col; - } - } - return out; -} - -void whitted_rt::render() { - const std::array resolution = this->cam->get_resolution(); - for (unsigned long y = 0; y < resolution[1]; y++) { -#ifdef NDEBUG - #pragma omp parallel for -#endif - for (unsigned long x = 0; x < resolution[0]; x++) { - std::shared_ptr> rays = this->cam->get_rays(x, y); - std::vector data; - data.reserve(rays->size()); - for (auto &r : *rays) - data.push_back(cast_ray(r, 0, false)); - this->cam->set_data(x, y, data); - } - std::cout << "."; - std::cout.flush(); - } -} - -bool whitted_rt::cast_shadow(const point &o, const direction &d) { - for (std::shared_ptr obstacle : *this->scene) - if (obstacle->intersect_shadow(o, d)) - return true; - return false; -} - -intersection whitted_rt::find_nearest(ray r) { - intersection out = intersection(); - float dist = std::numeric_limits::max(); - for (std::shared_ptr object : *this->scene) { - intersection cur = object->intersect_full(r); - if (!cur.object) - continue; - float new_dist = length(r.o - *cur.pos); - if (new_dist < dist) { - dist = new_dist; - out = cur; - } - } - return out; -} - -std::shared_ptr whitted_rt::get_pixel(const unsigned long &x, const unsigned long &y) const { - return this->cam->get_pixel(x, y); -} - -const std::array &whitted_rt::get_resolution() const { - return this->cam->get_resolution(); -} \ No newline at end of file diff --git a/lib/whitted_rt.h b/lib/whitted_rt.h deleted file mode 100644 index aa693f6..0000000 --- a/lib/whitted_rt.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - * @file whittedrt.h - * @author Philip Abernethy - */ - -#ifndef RAY_TRACER_WHITTEDRT_H -#define RAY_TRACER_WHITTEDRT_H - -#include "camera/camera.h" -#include "light/light.h" -#include "geometry/shapes/shape.h" -#include -#include - -/** - * @brief A whitted ray tracer. - */ -class whitted_rt { -private: - const std::shared_ptr background_color; - std::shared_ptr cam; - std::shared_ptr>> lights; - std::shared_ptr>> scene; - - /** - * @brief Casts the ray \p r and returns the color it produces. - * - * To do this it first determines the object closest to \p r's origin in \p r's direction. It then shades the point of intersection according to the light situation and material. If necessary it recurses to handle reflection and refraction. The colors of reflected/refracted rays are added to the local color after multiplication with a material factor. If step reaches the camera's \p max_bounces no recursion will be done. - * @param r The ray - * @param step The current recursion level - * @param internal A flag for whether or not the ray is inside a geometry - * @return The color produced by that ray - */ - color cast_ray(ray r, int step, bool internal); - - /** - * @brief Casts a shadow ray from the point \p o in the direction of \p d. - * - * \p d is expected to be not normalised and point exactly on the light source. If any geometry intersects that ray between \p o and \verbatim o+d\endverbatim \p o is considered to be in the shadow with respect to that light source. - * @param o The point (in world coordinates) to check - * @param d The vector from \p o to the light source - * @return true if any geometry lies between \p o and the light source, false otherwise - */ - bool cast_shadow(const point &o, const direction &d); - - /** - * @brief Finds the object closest to \p r.o in the direction \p r.d. - * - * Checks for intersection of every object in \p scene with \p r. If an intersection is found the distance to \p r.o is calculated. If it is shorter than the previous one the new intersection replaces the old one. - * @param r The ray to intersect with \p scene - * @return If an intersection was found the intersection object contains a reference to the geometry that intersected as well as important surface properties and the world coordinates of the intersection. If no intersection was found the intersection object contains only null pointers. - */ - intersection find_nearest(ray r); - -public: - /** - * Constructor - * @param background_color The color to user in case no geometry is hit by a ray - * @param cam The camera to use - * @param lights A vector of all the lights in the scene, including the ambient light - * @param scene A vector of all the geometry in the scene - */ - whitted_rt(std::shared_ptr background_color, - std::shared_ptr cam, - std::shared_ptr>> lights, - std::shared_ptr>> scene); - - /** - * @brief The main render loop - * - * Retrieves the ray(s) for all pixels from the camera and casts them. The returned color is saved to the camera's data vector. Produces a single '.' on stdout for every line finished of pixels. - */ - void render(); - - /** - * @brief Fetches the color of a pixel from the camera and returns it. - * @param x The x-coordinate of the pixel - * @param y the y-coordinate of the pixel - * @return The color of the pixel - */ - std::shared_ptr get_pixel(const unsigned long &x, const unsigned long &y) const; - - /** - * @brief Returns the resolution of the camera and thus the image - * @return The resolution as a std::array - */ - const std::array &get_resolution() const; -}; - -#endif //RAY_TRACER_WHITTEDRT_H diff --git a/main.cpp b/main.cpp index 97661c1..ee96442 100644 --- a/main.cpp +++ b/main.cpp @@ -1,25 +1,23 @@ -#include "lib/whitted_rt.h" +#include "lib/renderer/whitted_rt.h" #include "lib/parser.h" -using namespace std; - int main(int argc, char *argv[]) { if (argc == 1) { - cout << "Usage: " << argv[0] << " " << endl - << " - Path of the XML file specifying the camera, lights and geometry." << endl; + std::cout << "Usage: " << argv[0] << " " << std::endl + << " - Path of the XML file specifying the camera, lights and geometry." << std::endl; return 0; } - string out_file; - shared_ptr rt = parser::parse(argv[1], out_file); + std::string out_file; + std::shared_ptr rt = parser::parse(argv[1], out_file); rt->render(); - array res = rt->get_resolution(); + std::array res = rt->get_resolution(); cimg_library::CImg img(res[0], res[1], 1, 3); cimg_forXY(img, x, y) { - array p = rgb(*rt->get_pixel(x, y)); - img(x, y, 0) = p[0]; - img(x, y, 1) = p[1]; - img(x, y, 2) = p[2]; - } + std::array p = rgb(*rt->get_pixel(x, y)); + img(x, y, 0) = p[0]; + img(x, y, 1) = p[1]; + img(x, y, 2) = p[2]; + } img.save_png(out_file.c_str()); return 0; } \ No newline at end of file