From 46b5cb9f97de1aff803df49b2824e248c8cc6520 Mon Sep 17 00:00:00 2001 From: Derek McBlane Date: Wed, 2 Mar 2022 16:12:55 -0500 Subject: [PATCH 1/5] export compile commands for language server config --- .gitignore | 2 ++ CMakeLists.txt | 1 + build.sh | 19 +++++++++++++++++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 40089a1..9a25653 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,9 @@ *.sln *.suo *.vcxproj* +.cache/ tags +compile_commands.json # Build Files *.exe diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e64002..9e24518 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.15.0) project(RayTracer VERSION 0.1.0) set(CMAKE_CXX_STANDARD 20) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) add_compile_definitions( $<$:DEBUG> diff --git a/build.sh b/build.sh index 0493228..e1f1450 100755 --- a/build.sh +++ b/build.sh @@ -1,7 +1,22 @@ #!/bin/bash BUILD_TYPE=${1:-Release} -mkdir $BUILD_TYPE >/dev/null +mkdir $BUILD_TYPE 2>/dev/null pushd $BUILD_TYPE -cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} && cmake --build . && cmake --install . --prefix . + +CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=${BUILD_TYPE}" + +if cmake .. ${CMAKE_OPTIONS} ; then + echo "CMake finished" +else + exit $? +fi +if cmake --build . && cmake --install . --prefix . ; then + echo "Build finished" +else + exit $? +fi + +cp compile_commands.json .. 2>/dev/null + popd From 5b05c5a28654c11b3cc34c6c4c3c8fcc7b8b3363 Mon Sep 17 00:00:00 2001 From: Derek McBlane Date: Sat, 22 Oct 2022 12:42:59 -0400 Subject: [PATCH 2/5] add build/ to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9a25653..5d5bdd5 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ compile_commands.json x86/ x64/ bin/ +build/ */Debug/* */Release/* Release/ From 5744104eae097be24ea68293f10a7831500074ad Mon Sep 17 00:00:00 2001 From: Derek McBlane Date: Sun, 23 Oct 2022 22:00:57 -0400 Subject: [PATCH 3/5] fix getter for shadowColor fixup rename arg in setBias --- src/RayTracer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/RayTracer.cpp b/src/RayTracer.cpp index a964fbe..1fa2513 100644 --- a/src/RayTracer.cpp +++ b/src/RayTracer.cpp @@ -49,7 +49,7 @@ size_t RayTracer::maxNumReflections() const { } Color RayTracer::shadowColor() const { - return backgroundColor_; + return shadowColor_; } Color RayTracer::backgroundColor() const { @@ -57,8 +57,8 @@ Color RayTracer::backgroundColor() const { } -void RayTracer::setBias(float shadowBias) { - this->bias_ = shadowBias; +void RayTracer::setBias(float bias) { + this->bias_ = bias; } void RayTracer::setMaxNumReflections(size_t maxNumReflections) { From 177ee1041da463d25a20b321dcca1e5947d8d4d2 Mon Sep 17 00:00:00 2001 From: Derek McBlane Date: Sat, 22 Oct 2022 12:45:12 -0400 Subject: [PATCH 4/5] remove #pragma once in .cpp files --- src/App.cpp | 1 - src/Files.cpp | 1 - src/FrameBuffer.cpp | 1 - src/StopWatch.cpp | 2 +- 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/App.cpp b/src/App.cpp index 2a1d910..c0b5db7 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -1,4 +1,3 @@ -#pragma once #include "App.hpp" #include "Text.hpp" #include "Files.hpp" diff --git a/src/Files.cpp b/src/Files.cpp index 5feee42..b55283f 100644 --- a/src/Files.cpp +++ b/src/Files.cpp @@ -1,4 +1,3 @@ -#pragma once #include "Files.hpp" #include "FrameBuffer.hpp" #include diff --git a/src/FrameBuffer.cpp b/src/FrameBuffer.cpp index 092325d..716ca05 100644 --- a/src/FrameBuffer.cpp +++ b/src/FrameBuffer.cpp @@ -1,4 +1,3 @@ -#pragma once #include "FrameBuffer.hpp" #include "Math.hpp" #include "Color.hpp" diff --git a/src/StopWatch.cpp b/src/StopWatch.cpp index 1b96c59..7bf1640 100644 --- a/src/StopWatch.cpp +++ b/src/StopWatch.cpp @@ -1,4 +1,4 @@ -#include "Stopwatch.hpp" +#include "StopWatch.hpp" #include #include From 7f01f5d43ac1e262e48b98575848241a390c3720 Mon Sep 17 00:00:00 2001 From: Derek McBlane Date: Tue, 1 Nov 2022 10:54:45 -0400 Subject: [PATCH 5/5] material changes to support refraction; refractive light incidence calculations; update traceRay for refraction; make specular light color come from light not material; various updates to scenes --- src/CMakeLists.txt | 1 + src/LightIncidence.cpp | 70 +++++++++++++++ src/LightIncidence.hpp | 50 +++++++++++ src/Main.cpp | 156 +++++++++++++++------------------- src/Material.cpp | 129 +++++++++++++++------------- src/Material.hpp | 64 ++++++++------ src/Math.hpp | 4 + src/Objects.cpp | 7 +- src/Objects.hpp | 6 +- src/RayTracer.cpp | 87 ++++++++++++------- src/RayTracer.hpp | 12 +-- src/Scene.cpp | 7 ++ src/Scene.hpp | 3 + tests/CMakeLists.txt | 3 +- tests/LightIncidence_test.cpp | 31 +++++++ tests/Objects_test.cpp | 6 ++ tests/RayTracer_test.cpp | 25 ------ 17 files changed, 413 insertions(+), 248 deletions(-) create mode 100644 src/LightIncidence.cpp create mode 100644 src/LightIncidence.hpp create mode 100644 tests/LightIncidence_test.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3adb700..0548010 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,6 +3,7 @@ add_library(RayTracerCore FrameBuffer.cpp Lights.cpp Material.cpp + LightIncidence.cpp Objects.cpp RayTracer.cpp Scene.cpp diff --git a/src/LightIncidence.cpp b/src/LightIncidence.cpp new file mode 100644 index 0000000..c90483c --- /dev/null +++ b/src/LightIncidence.cpp @@ -0,0 +1,70 @@ +#include "LightIncidence.hpp" +#include "Ray.hpp" +#include "Math.hpp" + +#include + +LightIncidenceFresnel::LightIncidenceFresnel(const Ray& ray, const Intersection& intersection, float indexOfRefractionFrom, float indexOfRefractionTo) : + ray_(ray), + intersection_(intersection), + n1_(indexOfRefractionFrom), + n2_(indexOfRefractionTo) +{ + computeHelperVars(); +} + +void LightIncidenceFresnel::computeHelperVars() +{ + n1_over_n2_ = n1_ / n2_; + + cos_theta_i_ = -Math::dot(ray_.direction, intersection_.normal); + cos_theta_i_squared_ = Math::square(cos_theta_i_); + sin_theta_i_squared_ = 1.0f - cos_theta_i_squared_; + + if (Math::square(n1_over_n2_) * sin_theta_i_squared_ > 1.0f) { + reflectedProportion_ = 1.0f; + transmittedProportion_ = 0.0f; + return; + } + + sin_theta_t_squared_ = Math::square(n1_over_n2_)*sin_theta_i_squared_; + cos_theta_t_squared_ = 1.0f - sin_theta_t_squared_; + cos_theta_t_ = Math::squareRoot(cos_theta_t_squared_); + + R_normal_ = Math::square((n1_ * cos_theta_i_ - n2_ * cos_theta_t_) / (n1_ * cos_theta_i_ + n2_ * cos_theta_t_)); + R_tangent_ = Math::square((n2_ * cos_theta_i_ - n1_ * cos_theta_t_) / (n2_ * cos_theta_i_ + n1_ * cos_theta_t_)); + + reflectedProportion_ = (R_normal_ + R_tangent_) / 2.0f; + transmittedProportion_ = 1.0f - reflectedProportion_; +} + +Vec3 LightIncidenceFresnel::reflectedDirection() const +{ + return ray_.direction + 2.0f * cos_theta_i_ * intersection_.normal; +} + +Vec3 LightIncidenceFresnel::refractedDirection() const +{ + return n1_over_n2_ * ray_.direction + (n1_over_n2_ * cos_theta_i_ - Math::squareRoot(1.0f - sin_theta_t_squared_)) * intersection_.normal; +} + +float LightIncidenceFresnel::reflectedProportion() const +{ + return reflectedProportion_; +} + +float LightIncidenceFresnel::transmittedProportion() const +{ + return transmittedProportion_; +} + + +LightIncidenceReflect::LightIncidenceReflect(const Ray &ray, const Intersection &intersection) : + ray_(ray), + intersection_(intersection) +{ +} + +Vec3 LightIncidenceReflect::reflectedDirection() const { + return ray_.direction + 2.0f * -Math::dot(ray_.direction, intersection_.normal) * intersection_.normal; +} diff --git a/src/LightIncidence.hpp b/src/LightIncidence.hpp new file mode 100644 index 0000000..80b041a --- /dev/null +++ b/src/LightIncidence.hpp @@ -0,0 +1,50 @@ +#include "Math.hpp" +#include "Ray.hpp" + +#include + +class LightIncidenceFresnel { +public: + LightIncidenceFresnel(const Ray &ray, const Intersection &intersection, + const float indexOfRefractionFrom, + const float indexOfRefractionTo); + + Vec3 reflectedDirection() const; + Vec3 refractedDirection() const; + float reflectedProportion() const; + float transmittedProportion() const; + +private: + void computeHelperVars(); + + const Ray& ray_; + const Intersection& intersection_; + const float n1_; + const float n2_; + + bool total_internal_reflection_; + float n1_over_n2_; + float cos_theta_i_; + float cos_theta_i_squared_; + float sin_theta_i_squared_; + float sin_theta_t_squared_; + float cos_theta_t_squared_; + float cos_theta_t_; + + float R_normal_; + float R_tangent_; + float reflectedProportion_; + float transmittedProportion_; +}; + +class LightIncidenceReflect { +public: + LightIncidenceReflect(const Ray &ray, const Intersection &intersection); + + Vec3 reflectedDirection() const; + +private: + const Ray &ray_; + const Intersection &intersection_; +}; + diff --git a/src/Main.cpp b/src/Main.cpp index ec18004..336e965 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -29,59 +29,23 @@ void addGround(Scene& scene, const Material& groundMaterial) { scene.addSceneObject(Triangle(v21, v22, v23, groundMaterial)); } -Scene createTriangleScene() { +Scene createRandomGroundScene() { Scene scene{}; + scene.addAmbientLight({0.0f, 0.0f, 0.0f}); + scene.addLight(PointLight(Vec3(50.0f, 200.0f, -10.0f), Palette::antiqueWhite)); + scene.addLight(PointLight(Vec3(-50.0f, 200.0f, -10.0f), Palette::antiqueWhite)); - Color color = Palette::gray; - float intrinsity = 0.8; - float reflectivity = 1.0f - intrinsity; - Material Material{}; - Material.setWeights(intrinsity, reflectivity); - Material.setColors(color, color, color); - Material.setShininess(1); - - float L = 10.0; - - Vec3 v1{0.0f, 0.0f, 0.0f}; - Vec3 v2{L, 0.0f, 0.0f}; - Vec3 v3{L, L, 0.0f}; - scene.addSceneObject(Triangle(v1, v2, v3, Material)); - - return scene; -} - -Scene createSimpleScene(const Vec3& localOrigin=Vec3::zero()) { - Material reflectiveRed{}; - reflectiveRed.setWeights(0.50f, 0.50f); - reflectiveRed.setColors(Palette::darkRed, Palette::red, Palette::orangeRed); - reflectiveRed.setShininess(10); - - Material reflectiveGreen{}; - reflectiveGreen.setWeights(0.50f, 0.50f); - reflectiveGreen.setColors(Palette::darkGreen, Palette::green, Palette::lightGreen); - reflectiveGreen.setShininess(10); - - Material reflectiveBlue{}; - reflectiveBlue.setWeights(0.50f, 0.50f); - reflectiveBlue.setColors(Palette::darkBlue, Palette::blue, Palette::lightBlue); - reflectiveBlue.setShininess(10); - - Scene scene{}; - scene.addLight(PointLight(localOrigin + Vec3(0, 55, 50), Palette::antiqueWhite)); - scene.addSceneObject(Sphere(localOrigin + Vec3(0, 80, 0), 10.00f, reflectiveRed)); - scene.addSceneObject(Sphere(localOrigin + Vec3(0, 55, 0), 15.00f, reflectiveGreen)); - scene.addSceneObject(Sphere(localOrigin + Vec3(0, 20, 0), 20.00f, reflectiveBlue)); - return scene; -} + Color groundColor = Palette::darkGreen; + float groundReflectivity = 0.15f; + Material groundMaterial{}; + groundMaterial.setReflectivity(groundReflectivity); + groundMaterial.setColor(groundColor); -Scene createRandomFloatingScene() { - Scene scene{}; - scene.addLight(PointLight(Vec3(0.0f, 0.0f, 0.0f), Palette::antiqueWhite)); + addGround(scene, groundMaterial); std::random_device rd; // Will be used to obtain a seed for the random number engine std::mt19937 gen{rd()}; // Standard mersenne_twister_engine seeded with rd() std::uniform_real_distribution x_dis(-100.0, 100.0); - std::uniform_real_distribution y_dis(-100.0, 100.0); std::uniform_real_distribution z_dis(10.0, 100.0); std::uniform_real_distribution r_dis(1.0, 10.0); std::uniform_real_distribution c_dis(0.0, 1.0); @@ -93,29 +57,27 @@ Scene createRandomFloatingScene() { for (int i = 0; i < N_SPHERES; i++) { bool validSphere = false; while (!validSphere) { + float R = r_dis(gen); float x = x_dis(gen); - float y = y_dis(gen); + float y = R; float z = z_dis(gen); - float R = r_dis(gen); float r = c_dis(gen); float g = c_dis(gen); float b = c_dis(gen); - float intrinsity = c_dis(gen); - float reflectivity = 1.0f - intrinsity; + float reflectivity = c_dis(gen); Color c{r,g,b}; Material material{}; - material.setWeights(intrinsity, reflectivity); - material.setColors(c, c, c); - material.setShininess(10); + material.setReflectivity(reflectivity); + material.setColor(c); Sphere s{Vec3(x, y, z), R, material}; if (!sphereCollidesWithAnyOtherSpheres(spheres, s)) { validSphere = true; - spheres.push_back(s); + spheres.push_back(std::move(s)); } } } @@ -127,18 +89,17 @@ Scene createRandomFloatingScene() { return scene; } -Scene createRandomGroundScene() { +Scene createRandomGroundSceneRefraction() { Scene scene{}; + scene.addAmbientLight({0.0f, 0.0f, 0.0f}); scene.addLight(PointLight(Vec3(50.0f, 200.0f, -10.0f), Palette::antiqueWhite)); scene.addLight(PointLight(Vec3(-50.0f, 200.0f, -10.0f), Palette::antiqueWhite)); - Color groundColor = Palette::gray; - float groundIntrinsivity = 0.85; - float groundReflectivity = 1.0f - groundIntrinsivity; + Color groundColor = Palette::darkGreen; + float groundReflectivity = 0.15f; Material groundMaterial{}; - groundMaterial.setWeights(groundIntrinsivity, groundReflectivity); - groundMaterial.setColors(groundColor, groundColor, groundColor); - groundMaterial.setShininess(1); + groundMaterial.setReflectivity(groundReflectivity); + groundMaterial.setColor(groundColor); addGround(scene, groundMaterial); @@ -148,6 +109,7 @@ Scene createRandomGroundScene() { std::uniform_real_distribution z_dis(10.0, 100.0); std::uniform_real_distribution r_dis(1.0, 10.0); std::uniform_real_distribution c_dis(0.0, 1.0); + std::uniform_real_distribution ior_dis(1.0, 3.0); const size_t N_SPHERES = 100; std::vector spheres; @@ -165,20 +127,19 @@ Scene createRandomGroundScene() { float g = c_dis(gen); float b = c_dis(gen); - float intrinsity = c_dis(gen); - float reflectivity = 1.0f - intrinsity; + float reflectivity = c_dis(gen); + float ior = ior_dis(gen); Color c{r,g,b}; Material material{}; - material.setWeights(intrinsity, reflectivity); - material.setColors(c, c, c); - material.setShininess(10); + material.setReflectivity(reflectivity); + material.setColor(c); + material.setIndexOfRefraction(ior); Sphere s{Vec3(x, y, z), R, material}; if (!sphereCollidesWithAnyOtherSpheres(spheres, s)) { validSphere = true; - spheres.push_back(std::move(s)); } } } @@ -192,34 +153,51 @@ Scene createRandomGroundScene() { Scene createSimpleGroundScene() { Scene scene{}; + scene.addAmbientLight({0.0f, 0.0f, 0.0f}); scene.addLight(PointLight(Vec3(50.0f, 200.0f, -10.0f), Palette::antiqueWhite)); scene.addLight(PointLight(Vec3(-50.0f, 200.0f, -10.0f), Palette::antiqueWhite)); - Color groundColor = Palette::darkGreen; - float groundIntrinsivity = 0.95; - float groundReflectivity = 1.0f - groundIntrinsivity; - Material groundMaterial{}; - groundMaterial.setWeights(groundIntrinsivity, groundReflectivity); - groundMaterial.setColors(groundColor, groundColor, groundColor); - groundMaterial.setShininess(1); - + Material groundMaterial{MaterialSample::matteGray}; addGround(scene, groundMaterial); - Material reflectiveBlue{}; - reflectiveBlue.setWeights(0.50f, 0.50f); - reflectiveBlue.setColors(Palette::darkBlue, Palette::blue, Palette::lightBlue); - reflectiveBlue.setShininess(10); + float R; + R = 30.0f; + scene.addSceneObject(Sphere(Vec3(-1.5f*R, R, 0.0f), R, MaterialSample::reflectiveBlue)); + R = 20.0f; + scene.addSceneObject(Sphere(Vec3(1.5f*R, R, 0.0f), R, MaterialSample::reflectiveRed)); + + return scene; +} - Material reflectiveRed{}; - reflectiveRed.setWeights(0.50f, 0.50f); - reflectiveRed.setColors(Palette::darkRed, Palette::red, Palette::orangeRed); - reflectiveRed.setShininess(10); +Scene createSimpleGroundSceneRefraction() { + Scene scene{}; + scene.addAmbientLight({0.0f, 0.0f, 0.0f}); + scene.addLight(PointLight(Vec3(50.0f, 200.0f, -10.0f), Palette::antiqueWhite)); + scene.addLight(PointLight(Vec3(-50.0f, 200.0f, -10.0f), Palette::antiqueWhite)); + + Material groundMaterial{MaterialSample::matteGray}; + addGround(scene, groundMaterial); + + Material refractive{}; + refractive.setColor(Palette::white); + refractive.setReflectivity(0.3f); + refractive.setShininess(5); + refractive.setTransparency(0.8f); + refractive.setIndexOfRefraction(1.2f); float R; + R = 30.0f; - scene.addSceneObject(Sphere(Vec3(-1.5f*R, R, 0.0f), R, reflectiveBlue)); - R = 20.0f; - scene.addSceneObject(Sphere(Vec3(1.5f*R, R, 0.0f), R, reflectiveRed)); + scene.addSceneObject(Sphere(Vec3(-1.5f*R, R, 0.0f), R, MaterialSample::reflectiveBlue)); + R = 15.0f; + scene.addSceneObject(Sphere(Vec3(1.5f*R, R, 0.0f), R, MaterialSample::reflectiveRed)); + + R = 15.0f; + refractive.setIndexOfRefraction(1.2f); + scene.addSceneObject(Sphere(Vec3(0.5f*R, R, R), R, refractive)); + R = 15.0f; + refractive.setIndexOfRefraction(1.4f); + scene.addSceneObject(Sphere(Vec3(-0.5f*R, R, -R), R, refractive)); return scene; } @@ -227,13 +205,13 @@ Scene createSimpleGroundScene() { int main() { AppOptions options; options.imageOutputFile = "./scene.ppm"; - options.imageOutputSize = CommonResolutions::HD_8K; - options.rayTracingReflectionLimit = 4; + options.imageOutputSize = CommonResolutions::HD_4K; + options.rayTracingReflectionLimit = 5; options.skyBoxColor = Palette::skyBlue; options.shadowColor = Color(0.125f, 0.125f, 0.125f); options.viewTarget = Vec3(0, 0, 0); options.viewOffset = Vec3(0, 50, 150); - App app{ createRandomGroundScene(), options }; + App app{ createSimpleGroundSceneRefraction(), options }; app.run(); } diff --git a/src/Material.cpp b/src/Material.cpp index 672566c..291f1d5 100644 --- a/src/Material.cpp +++ b/src/Material.cpp @@ -6,78 +6,60 @@ Material::Material() : Material( - DEFAULT_AMBIENT_COLOR, DEFAULT_DIFFUSE_COLOR, - DEFAULT_SPECULAR_COLOR, - DEFAULT_INTRINSITY, DEFAULT_REFLECTIVITY, - DEFAULT_REFRACTIVITY, - DEFAULT_SPECULAR_EXPONENT) {} + DEFAULT_SPECULAR_EXPONENT, + DEFAULT_TRANSPARENCY, + DEFAULT_INDEX_OF_REFRACTION) {} -Material::Material(const Color& ambientColor, const Color& diffuseColor, const Color& specularColor) +Material::Material(const Color& diffuseColor) : Material( - ambientColor, diffuseColor, - specularColor, - DEFAULT_INTRINSITY, DEFAULT_REFLECTIVITY, - DEFAULT_REFRACTIVITY, - DEFAULT_SPECULAR_EXPONENT) {} - -Material::Material(const Color& ambientColor, const Color& diffuseColor, const Color& specularColor, - float intrinsity, float reflectivity, float refractivity, float shininess) - : ambientColor_ (ambientColor), - diffuseColor_ (diffuseColor), - specularColor_(specularColor), - intrinsity_ (intrinsity), - reflectivity_ (reflectivity), - refractivity_ (refractivity), - shininess_ (shininess) { - validateWeights(intrinsity, reflectivity, refractivity); - validateShininess(shininess); + DEFAULT_SPECULAR_EXPONENT, + DEFAULT_TRANSPARENCY, + DEFAULT_INDEX_OF_REFRACTION) {} + +Material::Material(const Color& diffuseColor, float reflectivity, float shininess, float transparency, float indexOfRefraction) + : diffuseColor_ (diffuseColor), + reflectivity_ (reflectivity), + shininess_ (shininess), + transparency_ (transparency), + indexOfRefraction_ (indexOfRefraction) { + validateReflectivity(reflectivity_); + validateShininess(shininess_); + validateTransparency(transparency_); + validateIndexOfRefraction(indexOfRefraction_); } -Color Material::ambientColor() const { - return ambientColor_; -} - Color Material::diffuseColor() const { return diffuseColor_; } -Color Material::specularColor() const { - return specularColor_; -} - -float Material::intrinsity() const { - return intrinsity_; -} - float Material::reflectivity() const { return reflectivity_; } -float Material::refractivity() const { - return refractivity_; -} - float Material::shininess() const { return shininess_; } +float Material::transparency() const { + return transparency_; +} + +float Material::indexOfRefraction() const { + return indexOfRefraction_; +} -void Material::setColors(const Color& ambientColor, const Color& diffuseColor, const Color& specularColor) { - this->ambientColor_ = ambientColor; +void Material::setColor(const Color& diffuseColor) { this->diffuseColor_ = diffuseColor; - this->specularColor_ = specularColor; } -void Material::setWeights(float intrinsity, float reflectivity, float refractivity) { - validateWeights(intrinsity, reflectivity, refractivity); - this->intrinsity_ = intrinsity; +void Material::setReflectivity(float reflectivity) { + validateReflectivity(reflectivity); this->reflectivity_ = reflectivity; - this->refractivity_ = refractivity; } void Material::setShininess(float shininess) { @@ -85,13 +67,18 @@ void Material::setShininess(float shininess) { this->shininess_ = shininess; } +void Material::setTransparency(float transparency) { + this->transparency_ = transparency; +} + +void Material::setIndexOfRefraction(float indexOfRefraction) { + validateIndexOfRefraction(indexOfRefraction); + this->indexOfRefraction_ = indexOfRefraction; +} -void Material::validateWeights(float intrinsity, float reflectivity, float refractivity) { - if (!Math::isApproximately(intrinsity + reflectivity + refractivity, 1.00f) || - intrinsity < 0.00f || intrinsity > 1.00f || - reflectivity < 0.00f || reflectivity > 1.00f || - refractivity < 0.00f || refractivity > 1.00f) { - throw std::invalid_argument("weights must be in range[0.0, 1.0] and sum to 1.0"); +void Material::validateReflectivity(float reflectivity) { + if (reflectivity < 0.00f || reflectivity > 1.00f) { + throw std::invalid_argument("reflectivity must be in range [0.0, 1.0]"); } } @@ -101,19 +88,41 @@ void Material::validateShininess(float shininess) { } } +void Material::validateTransparency(float transparency) { + if (transparency < 0.00f || transparency > 1.00f) { + throw std::invalid_argument("transparency must be in range [0.0, 1.0]"); + } +} + +void Material::validateIndexOfRefraction(float indexOfRefraction) { + if (indexOfRefraction < 1.00f) { + throw std::invalid_argument("index of refraction must be at least 1.0"); + } +} std::ostream& operator<<(std::ostream& os, const Material& material) { os << "Material(" - << "Colors{" - << "ambient:(" << material.ambientColor() << ")," - << "diffuse:(" << material.diffuseColor() << ")," - << "specular:(" << material.specularColor() << ")}, " - << "Weights{" - << "intrinsic:" << material.intrinsity() << "," + << "Color: " << material.diffuseColor() << ", " + << "Properies{" << "reflective:" << material.reflectivity() << "," - << "refractive:" << material.refractivity() << "}, " + << "transparency:" << material.transparency() << "," + << "index-of-refraction:" << material.indexOfRefraction() << "," + << "}, " << "Shininess{" - << "specular-exponent:" << material.shininess() << "}" + << "specular-exponent:" << material.shininess() + << "}, " << ")"; return os; } + +namespace MaterialSample { + const Material matteBlack {Palette::black, 0.0f, 1.0f, 0.0f, 1.0f}; + const Material matteGray {Palette::darkSlateGray, 0.0f, 1.0f, 0.0f, 1.0f}; + const Material reflectiveRed {Palette::red, 0.5f, 15.0f, 0.0f, 3.0f}; + const Material reflectiveBlue {Palette::blue, 0.5f, 15.0f, 0.0f, 3.0f}; + const Material reflectiveGreen {Palette::green, 0.5f, 15.0f, 0.0f, 3.0f}; + const Material transparentRed {Palette::red, 0.5f, 15.0f, 0.8f, 1.2f}; + const Material transparentGreen {Palette::green, 0.5f, 15.0f, 0.8f, 1.2f}; + const Material transparentBlue {Palette::blue, 0.5f, 15.0f, 0.8f, 1.2f}; +} + diff --git a/src/Material.hpp b/src/Material.hpp index 2114383..eea919c 100644 --- a/src/Material.hpp +++ b/src/Material.hpp @@ -2,45 +2,53 @@ #include "Math.hpp" #include "Color.hpp" - -// color of diffuse/ambient/specular reflectance, as well as weighted intrinsic/reflectivity/refraction +// color of diffuse reflectance, as well as weighted intrinsic/reflectivity/refraction class Material { public: Material(); - Material(const Color& ambientColor, const Color& diffuseColor, const Color& specularColor); - Material(const Color& ambientColor, const Color& diffuseColor, const Color& specularColor, - float intrinsity, float reflectivity, float refractivity, float shininess); - - Color ambientColor() const; - Color diffuseColor() const; - Color specularColor() const; - float intrinsity() const; - float reflectivity() const; - float refractivity() const; - float shininess() const; - - void setColors(const Color& ambientColor, const Color& diffuseColor, const Color& specularColor); - void setWeights(float intrinsity, float reflectivity, float refractivity = 0.00f); + Material(const Color& diffuseColor); + Material(const Color& diffuseColor, float reflectivity, float shininess, float transparency, float indexOfRefraction); + + Color diffuseColor() const; + float reflectivity() const; + float shininess() const; + float transparency() const; + float indexOfRefraction() const; + + void setColor(const Color& diffuseColor); + void setReflectivity(float reflectivity); void setShininess(float shininess); + void setTransparency(float transparency); + void setIndexOfRefraction(float indexOfRefraction); private: - Color ambientColor_; Color diffuseColor_; - Color specularColor_; - float intrinsity_; float reflectivity_; - float refractivity_; float shininess_; + float transparency_; + float indexOfRefraction_; - static constexpr Color DEFAULT_AMBIENT_COLOR { 0.20f, 0.20f, 0.20f }; - static constexpr Color DEFAULT_DIFFUSE_COLOR { 0.80f, 0.80f, 0.80f }; - static constexpr Color DEFAULT_SPECULAR_COLOR { 0.00f, 0.00f, 0.00f }; - static constexpr float DEFAULT_INTRINSITY { 1.00f }; - static constexpr float DEFAULT_REFLECTIVITY { 0.00f }; - static constexpr float DEFAULT_REFRACTIVITY { 0.00f }; - static constexpr float DEFAULT_SPECULAR_EXPONENT { 1.00f }; + static constexpr Color DEFAULT_DIFFUSE_COLOR { 0.80f, 0.80f, 0.80f }; + static constexpr float DEFAULT_REFLECTIVITY { 0.00f }; + static constexpr float DEFAULT_SPECULAR_EXPONENT { 1.00f }; + static constexpr float DEFAULT_TRANSPARENCY { 0.00f }; + static constexpr float DEFAULT_INDEX_OF_REFRACTION { 1.00f }; - static void validateWeights(float intrinsity, float reflectivity, float refractivity=0.00f); + static void validateReflectivity(float reflectivity); static void validateShininess(float shininess); + static void validateTransparency(float transparency); + static void validateIndexOfRefraction(float indexOfRefraction); }; std::ostream& operator<<(std::ostream& os, const Material& material); + + +namespace MaterialSample { + extern const Material matteBlack; + extern const Material matteGray; + extern const Material reflectiveRed; + extern const Material reflectiveGreen; + extern const Material reflectiveBlue; + extern const Material transparentRed; + extern const Material transparentGreen; + extern const Material transparentBlue; +} diff --git a/src/Math.hpp b/src/Math.hpp index f234026..1692387 100644 --- a/src/Math.hpp +++ b/src/Math.hpp @@ -208,6 +208,10 @@ namespace Math { } + inline float exp(float a) { + return std::exp(a); + } + inline float pow(float a, float b) { return std::powf(a, b); } diff --git a/src/Objects.cpp b/src/Objects.cpp index 47aff63..bd85ccf 100644 --- a/src/Objects.cpp +++ b/src/Objects.cpp @@ -65,6 +65,7 @@ bool Sphere::intersect(const Ray& ray, Intersection& result) const { result.point = ray.origin + ray.direction * result.t; result.normal = Math::direction(this->position_, result.point); + result.normal *= (Math::dot(result.normal, ray.direction) > 0) ? -1.0f : 1.0f; result.object = this; return true; @@ -115,7 +116,7 @@ Triangle::Triangle(const Vec3& vert0, const Vec3& vert1, const Vec3& vert2, cons // given ray intersects triangle IFF given ray [X(t) = P + tD] intersects the plane [P.n = k] in a way such that // our intersection point is always to the LEFT side of EVERY edge -// (aka our plane intersection point @[t = (k – P.n)/(D.n)] lies in between our triangle vertices) +// (aka our plane intersection point @[t = (k � P.n)/(D.n)] lies in between our triangle vertices) bool Triangle::intersect(const Ray& ray, Intersection& result) const { const float k = Math::dot(vert0_, planeNormal_); const float t = (k - Math::dot(ray.origin, planeNormal_)) / Math::dot(ray.direction, planeNormal_); @@ -126,7 +127,7 @@ bool Triangle::intersect(const Ray& ray, Intersection& result) const { if (contains(pointIntersectingPlane)) { result.t = t; result.point = pointIntersectingPlane; - result.normal = planeNormal_; + result.normal = (Math::dot(ray.origin, planeNormal_) > 0.0f ? 1.0f : -1.0f) * planeNormal_; result.object = this; return true; } @@ -146,7 +147,7 @@ std::string Triangle::description() const { // recall, a point lies in a triangle if.. -// (e0 x(R – P0)).n > 0 & (e1 x(R – P1)).n > 0 & (e2 x(R – P2)).n > 0 +// (e0 x(R � P0)).n > 0 & (e1 x(R � P1)).n > 0 & (e2 x(R � P2)).n > 0 // aka R is always to the LEFT side of EVERY edge bool Triangle::contains(const Vec3& point) const { return (Math::dot(planeNormal_, Math::cross(edge0_, (point - vert0_))) > 0.00f) && diff --git a/src/Objects.hpp b/src/Objects.hpp index d731230..23a1dc1 100644 --- a/src/Objects.hpp +++ b/src/Objects.hpp @@ -10,7 +10,7 @@ class IObject { virtual ~IObject() = default; virtual bool intersect(const Ray& ray, Intersection& result) const = 0; virtual std::string description() const = 0; - + constexpr const Vec3& position() const { return position_; } constexpr const Material& material() const { return material_; } @@ -47,7 +47,7 @@ class Sphere final : public virtual IObject { class Triangle final : public virtual IObject { public: Triangle(const Vec3& vert0, const Vec3& vert1, const Vec3& vert2, const Material& material); - + virtual bool intersect(const Ray& ray, Intersection& result) const override; virtual std::string description() const override; @@ -58,7 +58,7 @@ class Triangle final : public virtual IObject { Vec3 vert2() const; Vec3 center() const; Vec3 planeNormal() const; - + private: Vec3 vert0_; Vec3 vert1_; diff --git a/src/RayTracer.cpp b/src/RayTracer.cpp index 1fa2513..aadc872 100644 --- a/src/RayTracer.cpp +++ b/src/RayTracer.cpp @@ -4,14 +4,14 @@ #include "Camera.hpp" #include "Lights.hpp" #include "Objects.hpp" +#include "LightIncidence.hpp" #include "Scene.hpp" #include "FrameBuffer.hpp" #include - RayTracer::RayTracer() : bias_ (DEFAULT_BIAS), - maxNumReflections_(DEFAULT_MAX_NUM_REFLECTIONS), + maxIncidences_ (DEFAULT_MAX_NUM_INCIDENCES), shadowColor_ (DEFAULT_SHADOW_COLOR), backgroundColor_ (DEFAULT_BACKGROUND_COLOR) {} @@ -35,7 +35,7 @@ void RayTracer::traceScene(const Camera& camera, const Scene& scene, FrameBuffer const auto [row, col] = frameBuffer.getPixelRowCol(i); const Vec3 viewportPosition{ (col + 0.50f) * invWidth, (row + 0.50f) * invHeight, 0.00f }; const Ray primaryRay = camera.viewportPointToRay(viewportPosition); - const Color pixelColor = traceRay(camera, scene, primaryRay, 0); + const Color pixelColor = traceRay(camera, scene, nullptr, primaryRay, 0); frameBuffer.setPixel(height - 1 - row, col, pixelColor); // invert y (since viewport and row start opposite) } } @@ -44,8 +44,8 @@ float RayTracer::bias() const { return bias_; } -size_t RayTracer::maxNumReflections() const { - return maxNumReflections_; +size_t RayTracer::maxNumIncidences() const { + return maxIncidences_; } Color RayTracer::shadowColor() const { @@ -62,7 +62,7 @@ void RayTracer::setBias(float bias) { } void RayTracer::setMaxNumReflections(size_t maxNumReflections) { - this->maxNumReflections_ = maxNumReflections; + this->maxIncidences_ = maxNumReflections; } void RayTracer::setShadowColor(const Color& shadowColor) { @@ -73,49 +73,71 @@ void RayTracer::setBackgroundColor(const Color& backgroundColor) { this->backgroundColor_ = backgroundColor; } - - -Color RayTracer::traceRay(const Camera& camera, const Scene& scene, const Ray& ray, size_t depth=0) const { +Color RayTracer::traceRay(const Camera& camera, const Scene& scene, const IObject* inObject, const Ray& ray, size_t depth) const { Intersection intersection{}; if (!findNearestIntersection(camera, scene, ray, intersection)) { return backgroundColor_; } - Color reflectedColor = {0.0f, 0.0f, 0.0f}; - if (depth < maxNumReflections_ && intersection.object->material().reflectivity() > 0.00f) { - reflectedColor = traceRay(camera, scene, reflectRay(ray, intersection), depth + 1); + const Material& object_material = intersection.object->material(); + + Color blendedColor; + Color intrinsicColor = scene.getAmbientLightColor(); + Color reflectedColor = backgroundColor_; + Color refractedColor = backgroundColor_; + float transmittedProportion = 1.0; + float reflectedProportion = 0.0; + + Ray outgoingRay{}; + + if (depth < maxIncidences_) { + float n1, n2; + n1 = (inObject != nullptr) ? inObject->material().indexOfRefraction() : 1.0f; + n2 = object_material.indexOfRefraction(); + + LightIncidenceFresnel incidence{ray, intersection, n1, n2}; + + outgoingRay = Ray{intersection.point, incidence.reflectedDirection()}; + biasRayInDirection(outgoingRay, intersection.normal); + reflectedColor = traceRay(camera, scene, inObject, outgoingRay, depth + 1); + reflectedProportion = object_material.reflectivity() * incidence.reflectedProportion(); + + if (reflectedProportion < 0.90) { + outgoingRay = Ray{intersection.point, incidence.refractedDirection()}; + biasRayInDirection(outgoingRay, -intersection.normal); + const IObject* nextInObject = (intersection.object == inObject) ? nullptr : intersection.object; + refractedColor = traceRay(camera, scene, nextInObject, outgoingRay, depth + 1); + } } - - Color nonReflectedColor = intersection.object->material().ambientColor(); + + // lights for (size_t index = 0; index < scene.getNumLights(); index++) { const ILight& light = scene.getLight(index); Color diffuse = computeDiffuseColor(intersection, light); Color specular = computeSpecularColor(intersection, light, camera); Color lightIntensityAtPoint = light.computeIntensityAtPoint(intersection.point); - nonReflectedColor += lightIntensityAtPoint * (diffuse + specular); + intrinsicColor += lightIntensityAtPoint * (diffuse + specular); } - - // blend intrinsic and reflected color using our light and intersected object - Color blendedColor = (intersection.object->material().intrinsity() * nonReflectedColor) + - (intersection.object->material().reflectivity() * reflectedColor); - // shadows for (size_t index = 0; index < scene.getNumLights(); index++) { const ILight& light = scene.getLight(index); if (isInShadow(camera, intersection, light, scene)) { - blendedColor -= shadowColor_; + intrinsicColor -= shadowColor_; } } - + + transmittedProportion = 1.0f - reflectedProportion; + const float& transparency = intersection.object->material().transparency(); + blendedColor = transmittedProportion * ((1.0f - transparency)*intrinsicColor + transparency*refractedColor) + + reflectedProportion * reflectedColor; + + return blendedColor; } -// reflect our ray using a slight direction offset to avoid infinite reflections -Ray RayTracer::reflectRay(const Ray& ray, const Intersection& intersection) const { - const Vec3 reflectedDirection = Math::reflect(ray.direction, intersection.normal); - const float biasDirection = ( Math::dot(intersection.normal, reflectedDirection) > 0 ) ? 1.0f : -1.0f; - return Ray(intersection.point + (bias_ * biasDirection * intersection.normal), reflectedDirection); +void RayTracer::biasRayInDirection(Ray& ray, const Vec3& direction) const { + ray.origin += bias_ * direction; } bool RayTracer::findNearestIntersection(const Camera& camera, const Scene& scene, const Ray& ray, Intersection& result) const { @@ -133,13 +155,12 @@ bool RayTracer::findNearestIntersection(const Camera& camera, const Scene& scene // check if there exists another object blocking light from reaching our hit-point bool RayTracer::isInShadow(const Camera& camera, const Intersection& intersection, const ILight& light, const Scene& scene) const { const Vec3 directionToLight = Math::direction(intersection.point, light.position()); - const float biasDirection = ( Math::dot(intersection.normal, directionToLight) > 0 ) ? 1.0f : -1.0f; - const Ray shadowRay{ intersection.point + (bias_ * biasDirection * intersection.normal), directionToLight }; + const Ray shadowRay{ intersection.point, directionToLight }; const float distanceToLight = Math::distance(shadowRay.origin, light.position()); for (size_t index = 0; index < scene.getNumObjects(); index++) { Intersection occlusion; - if (scene.getObject(index).intersect(shadowRay, occlusion) && - occlusion.object != intersection.object && + bool intersected = scene.getObject(index).intersect(shadowRay, occlusion); + if (intersected && occlusion.object != intersection.object && Math::distance(occlusion.point, light.position()) < distanceToLight) { return true; } @@ -159,7 +180,7 @@ Color RayTracer::computeSpecularColor(const Intersection& intersection, const IL const Vec3 halfwayVec = Math::normalize(directionToCam + light.position()); const Material surfaceMaterial = intersection.object->material(); const float strengthAtCamAngle = Math::max(0.00f, Math::dot(intersection.normal, halfwayVec)); - return Math::pow(strengthAtCamAngle, surfaceMaterial.shininess()) * surfaceMaterial.specularColor(); + return Math::pow(strengthAtCamAngle, surfaceMaterial.shininess()) * light.intensity(); } @@ -168,7 +189,7 @@ std::ostream& operator<<(std::ostream& os, const RayTracer& rayTracer) { << "shadow-color:(" << rayTracer.shadowColor() << ")," << "background-color:(" << rayTracer.backgroundColor() << ")," << "bias:" << rayTracer.bias() << "," - << "max-num-reflections:" << rayTracer.maxNumReflections() + << "max-num-incidences:" << rayTracer.maxNumIncidences() << ")"; return os; } diff --git a/src/RayTracer.hpp b/src/RayTracer.hpp index 9008fd2..b176991 100644 --- a/src/RayTracer.hpp +++ b/src/RayTracer.hpp @@ -16,7 +16,7 @@ class RayTracer { void traceScene(const Camera& camera, const Scene& scene, FrameBuffer& frameBuffer) const; float bias() const; - size_t maxNumReflections() const; + size_t maxNumIncidences() const; Color shadowColor() const; Color backgroundColor() const; @@ -25,22 +25,22 @@ class RayTracer { void setShadowColor(const Color& shadowColor); void setBackgroundColor(const Color& backgroundColor); - Ray reflectRay(const Ray& ray, const Intersection& intersection) const; + void biasRayInDirection(Ray& ray, const Vec3& direction) const; bool findNearestIntersection(const Camera& camera, const Scene& scene, const Ray& ray, Intersection& result) const; private: float bias_; - size_t maxNumReflections_; + size_t maxIncidences_; Color shadowColor_; Color backgroundColor_; static constexpr float DEFAULT_BIAS = 1e-02f; - static constexpr size_t DEFAULT_MAX_NUM_REFLECTIONS = 3; + static constexpr size_t DEFAULT_MAX_NUM_INCIDENCES = 3; static constexpr Color DEFAULT_SHADOW_COLOR { 0.125f, 0.125f, 0.125f }; static constexpr Color DEFAULT_BACKGROUND_COLOR { 0.500f, 0.500f, 0.500f }; - Color traceRay(const Camera& camera, const Scene& scene, const Ray& ray, size_t depth) const; - + Color traceRay(const Camera& camera, const Scene& scene, const IObject* fromObject, const Ray& ray, size_t depth=0) const; + bool isInShadow(const Camera& camera, const Intersection& intersection, const ILight& light, const Scene& scene) const; Color computeDiffuseColor(const Intersection& intersection, const ILight& light) const; diff --git a/src/Scene.cpp b/src/Scene.cpp index ac306b7..12e724d 100644 --- a/src/Scene.cpp +++ b/src/Scene.cpp @@ -5,6 +5,10 @@ #include +void Scene::addAmbientLight(Color&& ambientLightColor) { + ambientLightColor_ = std::move(ambientLightColor); +} + void Scene::addLight(PointLight&& light) { lights_.push_back(std::make_unique(std::move(light))); } @@ -18,6 +22,9 @@ void Scene::addSceneObject(Triangle&& object) { } +const Color& Scene::getAmbientLightColor() const { + return ambientLightColor_; +} const ILight& Scene::getLight(size_t index) const { assert(index >= 0 && index < lights_.size()); diff --git a/src/Scene.hpp b/src/Scene.hpp index 135105a..dcda246 100644 --- a/src/Scene.hpp +++ b/src/Scene.hpp @@ -14,10 +14,12 @@ class Scene { public: Scene() = default; + void addAmbientLight(Color&& ambientLightColor); void addLight(PointLight&& light); void addSceneObject(Sphere&& object); void addSceneObject(Triangle&& object); + const Color& getAmbientLightColor() const; const ILight& getLight(size_t index) const; const IObject& getObject(size_t index) const; @@ -25,6 +27,7 @@ class Scene { size_t getNumObjects() const; private: + Color ambientLightColor_; std::vector> lights_; std::vector> objects_; }; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6243f2d..3d68d05 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,7 +1,8 @@ add_executable(RunUnitTests Main.cpp - RayTracer_test.cpp Objects_test.cpp + LightIncidence_test.cpp + RayTracer_test.cpp ) target_include_directories(RunUnitTests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(RunUnitTests PRIVATE RayTracerCore) diff --git a/tests/LightIncidence_test.cpp b/tests/LightIncidence_test.cpp new file mode 100644 index 0000000..dad1131 --- /dev/null +++ b/tests/LightIncidence_test.cpp @@ -0,0 +1,31 @@ +#include "LightIncidence.hpp" + +#include "gtest/gtest.h" + + +TEST(Reflect, OppositeDirectionNormal) +{ + Ray ray{{1.0, 0.0, 0.0}, {-1.0, 0.0, 0.0}}; + Intersection intersection{{0.0, 0.0, 0.0}, Math::normalize(Vec3(1.0, 1.0, 0.0)), 0.0, NULL}; + + LightIncidenceFresnel lightIncidence{ray, intersection, 1.0f, 1.0f}; + Vec3 reflected_direction = lightIncidence.reflectedDirection(); + + EXPECT_NEAR(reflected_direction.x, 0.0f, 0.1f); + EXPECT_NEAR(reflected_direction.y, 1.0f, 0.1f); + EXPECT_NEAR(reflected_direction.z, 0.0f, 0.1f); +} + +TEST(Reflect, SameDirectionNormal) +{ + Ray ray{{1.0, 0.0, 0.0}, {-1.0, 0.0, 0.0}}; + Intersection intersection{{0.0, 0.0, 0.0}, Math::normalize(Vec3(-1.0, -1.0, 0.0)), 0.0, NULL}; + + LightIncidenceFresnel lightIncidence{ray, intersection, 1.0f, 1.0f}; + Vec3 reflected_direction = lightIncidence.reflectedDirection(); + + EXPECT_NEAR(reflected_direction.x, 0.0f, 0.1f); + EXPECT_NEAR(reflected_direction.y, 1.0f, 0.1f); + EXPECT_NEAR(reflected_direction.z, 0.0f, 0.1f); +} + diff --git a/tests/Objects_test.cpp b/tests/Objects_test.cpp index c51a264..657b9d9 100644 --- a/tests/Objects_test.cpp +++ b/tests/Objects_test.cpp @@ -80,6 +80,9 @@ TEST(Intersection, TriangleCCW) EXPECT_NEAR(intersection.point.x, L/2.0f, 0.1f); EXPECT_NEAR(intersection.point.y, L/4.0f, 0.1f); EXPECT_NEAR(intersection.point.z, 0.0f, 0.1f); + EXPECT_NEAR(intersection.normal.x, 0.0f, 0.1f); + EXPECT_NEAR(intersection.normal.y, 0.0f, 0.1f); + EXPECT_NEAR(intersection.normal.z, -1.0f, 0.1f); } TEST(Intersection, TriangleCW) @@ -100,6 +103,9 @@ TEST(Intersection, TriangleCW) EXPECT_NEAR(intersection.point.x, L/2.0f, 0.1f); EXPECT_NEAR(intersection.point.y, L/4.0f, 0.1f); EXPECT_NEAR(intersection.point.z, 0.0f, 0.1f); + EXPECT_NEAR(intersection.normal.x, 0.0f, 0.1f); + EXPECT_NEAR(intersection.normal.y, 0.0f, 0.1f); + EXPECT_NEAR(intersection.normal.z, -1.0f, 0.1f); } TEST(Intersection, TriangleRayPointingAway) diff --git a/tests/RayTracer_test.cpp b/tests/RayTracer_test.cpp index ba31bb8..38c1f4f 100644 --- a/tests/RayTracer_test.cpp +++ b/tests/RayTracer_test.cpp @@ -9,31 +9,6 @@ #include -TEST(Reflect, SameDirectionNormal) -{ - RayTracer ray_tracer; - Ray ray{{1.0, 0.0, 0.0}, {-1.0, 0.0, 0.0}}; - Intersection intersection{{0.0, 0.0, 0.0}, Math::normalize(Vec3(-1.0, -1.0, 0.0)), 0.0, NULL}; - - Ray reflected_ray = ray_tracer.reflectRay(ray, intersection); - - EXPECT_NEAR(reflected_ray.direction.x, 0.0f, 0.1f); - EXPECT_NEAR(reflected_ray.direction.y, 1.0f, 0.1f); - EXPECT_NEAR(reflected_ray.direction.z, 0.0f, 0.1f); -} - -TEST(Reflect, OppositeDirectionNormal) -{ - RayTracer ray_tracer; - Ray ray{{1.0, 0.0, 0.0}, {-1.0, 0.0, 0.0}}; - Intersection intersection{{0.0, 0.0, 0.0}, Math::normalize(Vec3(1.0, 1.0, 0.0)), 0.0, NULL}; - - Ray reflected_ray = ray_tracer.reflectRay(ray, intersection); - - EXPECT_NEAR(reflected_ray.direction.x, 0.0f, 0.1f); - EXPECT_NEAR(reflected_ray.direction.y, 1.0f, 0.1f); - EXPECT_NEAR(reflected_ray.direction.z, 0.0f, 0.1f); -} TEST(NearestIntersection, Simple) {