From 17fd35fe765c989cba0ed615ba0e9d00432a4d08 Mon Sep 17 00:00:00 2001 From: Maximilian Giller Date: Tue, 24 Jan 2023 21:11:30 +0100 Subject: [PATCH] Adds rich transparency for shaders and implements if for light calculations --- light/pointlight.cpp | 6 +++-- light/spotlight.cpp | 3 ++- light/sunlight.cpp | 4 +++- scene/fastscene.cpp | 24 ++++++++++++++++++++ scene/fastscene.h | 1 + scene/scene.h | 1 + scene/simplescene.cpp | 44 ++++++++++++++++++++++++++++--------- scene/simplescene.h | 1 + shader/refractionshader.cpp | 31 ++++++++++++++++++++++---- shader/refractionshader.h | 3 +++ shader/shader.h | 28 ++++++++++++++++------- 11 files changed, 120 insertions(+), 26 deletions(-) diff --git a/light/pointlight.cpp b/light/pointlight.cpp index 95c6549..359352a 100644 --- a/light/pointlight.cpp +++ b/light/pointlight.cpp @@ -20,8 +20,10 @@ Light::Illumination PointLight::illuminate(Scene const &scene, Ray const &ray) c lightRay.length = distance - LGT_EPS; // If the target is not in shadow... - if (!scene.findOcclusion(lightRay)) + if (!scene.findOcclusion(lightRay)) { // ... compute the attenuation and light color - illum.color = 1.0f / (distance * distance) * this->color * this->intensity; + Color rayTransparency = scene.getTransparency(lightRay, distance); + illum.color = 1.0f / (distance * distance) * this->color * this->intensity * rayTransparency; + } return illum; } diff --git a/light/spotlight.cpp b/light/spotlight.cpp index 0828a8e..3376249 100644 --- a/light/spotlight.cpp +++ b/light/spotlight.cpp @@ -28,7 +28,8 @@ Light::Illumination SpotLight::illuminate(Scene const &scene, Ray const &ray) co // ... and not in shadow ... if (!scene.findOcclusion(lightRay)) { // ... compute the attenuation and light color ... - illum.color = 1.0f / (distance * distance) * this->color * this->intensity; + Color rayTransparency = scene.getTransparency(lightRay, distance); + illum.color = 1.0f / (distance * distance) * this->color * this->intensity * rayTransparency; // ... then compute the falloff towards the edge of the cone if (this->alphaMin < alpha) illum.color *= 1.0f - (alpha - this->alphaMin) / (this->alphaMax - this->alphaMin); diff --git a/light/sunlight.cpp b/light/sunlight.cpp index dc355fb..c34bdf1 100644 --- a/light/sunlight.cpp +++ b/light/sunlight.cpp @@ -25,7 +25,9 @@ Light::Illumination SunLight::illuminate(Scene const &scene, Ray const &ray) con // Look at angleIntensity of light float angleIntensity = dotProduct(-ray.normal, this->direction); // 0 if light is behind surface, 1 if light is in front of surface - illum.color = this->color * this->intensity * angleIntensity; + + Color rayTransparency = scene.getTransparency(lightRay, INFINITY); + illum.color = this->color * this->intensity * angleIntensity * rayTransparency; } return illum; } \ No newline at end of file diff --git a/scene/fastscene.cpp b/scene/fastscene.cpp index 232b131..740f3ad 100644 --- a/scene/fastscene.cpp +++ b/scene/fastscene.cpp @@ -221,3 +221,27 @@ std::unique_ptr FastScene::build(Vector3d const &minimumBounds, Vector3d c node->child[1] = this->build(minimumSplit, maximumBounds, rightPrimitives, depth); return node; } + +Color FastScene::getTransparency(Ray &ray, float maxDistance) const +{ + // TODO: Not taking advantage of the tree structure! + + ray.length = maxDistance; + ray.primitive = nullptr; + + Color transparency(1, 1, 1); + for (auto i: this->primitives()) + { + Ray r = ray; + if (i->intersect(r) && r.length < maxDistance && i->shader()->isTransparent()) + { + r.length = maxDistance; // To allow transparency to be calculated partially for objects + + Color t = i->shader()->transparency(*this, r); + transparency.r *= t.r; + transparency.g *= t.g; + transparency.b *= t.b; + } + } + return transparency; +} diff --git a/scene/fastscene.h b/scene/fastscene.h index fd59324..6c2be6d 100644 --- a/scene/fastscene.h +++ b/scene/fastscene.h @@ -28,6 +28,7 @@ public: // Raytracing functions bool findIntersection(Ray &ray) const override; bool findOcclusion(Ray &ray) const override; + Color getTransparency(Ray &ray, float maxDistance) const override; int countNodeIntersections(const Ray &ray) const; // Setup functions diff --git a/scene/scene.h b/scene/scene.h index a373c26..01379ce 100644 --- a/scene/scene.h +++ b/scene/scene.h @@ -41,6 +41,7 @@ public: Color traceRay(Ray &ray) const; virtual bool findIntersection(Ray &ray) const = 0; virtual bool findOcclusion(Ray &ray) const = 0; + virtual Color getTransparency(Ray &ray, float maxDistance) const = 0; protected: Color backgroundColor; diff --git a/scene/simplescene.cpp b/scene/simplescene.cpp index 4d006cb..2aaf286 100644 --- a/scene/simplescene.cpp +++ b/scene/simplescene.cpp @@ -2,16 +2,40 @@ #include "primitive/primitive.h" #include "shader/shader.h" -bool SimpleScene::findIntersection(Ray &ray) const { - bool hit = false; - for (auto i : this->primitives()) - hit |= i->intersect(ray); - return hit; +bool SimpleScene::findIntersection(Ray &ray) const +{ + bool hit = false; + for (auto i: this->primitives()) + hit |= i->intersect(ray); + return hit; } -bool SimpleScene::findOcclusion(Ray &ray) const { - for (auto i : this->primitives()) - if (i->intersect(ray) && !i->shader()->isTransparent()) - return true; - return false; +bool SimpleScene::findOcclusion(Ray &ray) const +{ + for (auto i: this->primitives()) + if (i->intersect(ray) && !i->shader()->isTransparent()) + return true; + return false; +} + +Color SimpleScene::getTransparency(Ray &ray, float maxDistance) const +{ + ray.length = maxDistance; + ray.primitive = nullptr; + + Color transparency(1, 1, 1); + for (auto i: this->primitives()) + { + Ray r = ray; + if (i->intersect(r) && r.length < maxDistance && i->shader()->isTransparent()) + { + r.length = maxDistance; // To allow transparency to be calculated partially for objects + + Color t = i->shader()->transparency(*this, r); + transparency.r *= t.r; + transparency.g *= t.g; + transparency.b *= t.b; + } + } + return transparency; } diff --git a/scene/simplescene.h b/scene/simplescene.h index 070c391..b9be64d 100644 --- a/scene/simplescene.h +++ b/scene/simplescene.h @@ -9,6 +9,7 @@ public: // Raytracing functions bool findIntersection(Ray &ray) const override; bool findOcclusion(Ray &ray) const override; + Color getTransparency(Ray &ray, float maxDistance) const override; }; #endif diff --git a/shader/refractionshader.cpp b/shader/refractionshader.cpp index 5312e30..f6aa6d1 100644 --- a/shader/refractionshader.cpp +++ b/shader/refractionshader.cpp @@ -1,7 +1,8 @@ #include "scene/scene.h" #include "shader/refractionshader.h" -RefractionShader::RefractionShader(float indexInside, float indexOutside, Color const &objectColor, float lightLoss) : indexInside( +RefractionShader::RefractionShader(float indexInside, float indexOutside, Color const &objectColor, float lightLoss) + : indexInside( indexInside), indexOutside(indexOutside), objectColor(objectColor), lightLoss(lightLoss) {} @@ -52,14 +53,36 @@ Color RefractionShader::shade(Scene const &scene, Ray const &ray) const // Send out a new refracted ray into the scene Color hitColor = scene.traceRay(refractionRay); - - // Calculate light lost float lightRemaining = 1; - if (refractionRay.primitive == ray.primitive) lightRemaining = 1 - lightLoss + exp(-refractionRay.length / 10) * lightLoss; + if (ray.primitive == refractionRay.primitive) lightRemaining = remainingLightIntensity(refractionRay.length); + return hitColor * objectColor * lightRemaining; } return Color(0.0f, 0.0f, 0.0f); } +float RefractionShader::remainingLightIntensity(float distanceThroughObject) const +{ + return 1 - lightLoss + exp(-distanceThroughObject / 10) * lightLoss; +} + bool RefractionShader::isTransparent() const { return true; } + +Color RefractionShader::transparency(const Scene &scene, const Ray &ray) const +{ + // Determine length through the object + Ray lengthRay = ray; + // Reset the ray + lengthRay.length = INFINITY; + lengthRay.primitive = nullptr; + lengthRay.origin = ray.origin + (ray.length + REFR_EPS) * ray.direction; + + scene.traceRay(lengthRay); + + float transparencyDistance = std::min(ray.length, lengthRay.length); + float lightRemaining = 1; + if (ray.primitive == lengthRay.primitive) lightRemaining = remainingLightIntensity(transparencyDistance); + + return objectColor * lightRemaining; +} diff --git a/shader/refractionshader.h b/shader/refractionshader.h index 1da09c0..f3873f1 100644 --- a/shader/refractionshader.h +++ b/shader/refractionshader.h @@ -14,12 +14,15 @@ public: Color shade(Scene const &scene, Ray const &ray) const override; bool isTransparent() const override; + Color transparency(Scene const &scene, Ray const &ray) const override; private: float indexInside; float indexOutside; float lightLoss; Color objectColor; + + float remainingLightIntensity(float distanceThroughObject) const; }; #endif diff --git a/shader/shader.h b/shader/shader.h index 397b94f..9ba1342 100644 --- a/shader/shader.h +++ b/shader/shader.h @@ -7,17 +7,29 @@ // Forward declarations class Scene; -class Shader { +class Shader +{ public: - // Constructor / Desctructor - Shader() = default; - virtual ~Shader() = default; + // Constructor / Desctructor + Shader() = default; - // Get - virtual bool isTransparent() const { return false; } + virtual ~Shader() = default; - // Shader functions - virtual Color shade(Scene const &scene, Ray const &ray) const = 0; + // Get + virtual bool isTransparent() const + { return false; } + + /** + * Especially used for lighting calculations. + * @brief Returns the light let through the shader in opposite direction of the given ray. + * @param ray Origin and direction of the desired path. + * @return 0 if the shader is opaque, 1 if the shader is transparent, for each color channel. + */ + virtual Color transparency(Scene const &scene, Ray const &ray) const + { return isTransparent() ? Color(1, 1, 1) : Color(0, 0, 0); } + + // Shader functions + virtual Color shade(Scene const &scene, Ray const &ray) const = 0; }; #endif