From 3b76969ff2f19e6979a1fcb5a335a63aaaa7ed50 Mon Sep 17 00:00:00 2001 From: Maximilian Giller Date: Tue, 24 Jan 2023 19:59:13 +0100 Subject: [PATCH 01/14] Colored refraction shader --- shader/refractionshader.cpp | 95 ++++++++++++++++++++----------------- shader/refractionshader.h | 19 ++++---- 2 files changed, 63 insertions(+), 51 deletions(-) diff --git a/shader/refractionshader.cpp b/shader/refractionshader.cpp index 80d2124..e13916a 100644 --- a/shader/refractionshader.cpp +++ b/shader/refractionshader.cpp @@ -1,51 +1,60 @@ #include "scene/scene.h" #include "shader/refractionshader.h" -RefractionShader::RefractionShader(float indexInside, float indexOutside) : indexInside(indexInside), indexOutside(indexOutside) {} +RefractionShader::RefractionShader(float indexInside, float indexOutside, Color const &objectColor) : indexInside( + indexInside), indexOutside(indexOutside), objectColor(objectColor) +{} -Color RefractionShader::shade(Scene const &scene, Ray const &ray) const { - // Circumvent getting environment map color into the mix - if (ray.getRemainingBounces() > 0) { - // Get the normal of the primitive which was hit - Vector3d normalVector = ray.normal; +Color RefractionShader::shade(Scene const &scene, Ray const &ray) const +{ + // Circumvent getting environment map color into the mix + if (ray.getRemainingBounces() > 0) + { + // Get the normal of the primitive which was hit + Vector3d normalVector = ray.normal; - // Calculate the index of refraction - float refractiveIndex = indexOutside / indexInside; - // What if we are already inside the object? - if (dotProduct(normalVector, ray.direction) > 0) { - normalVector = -normalVector; - refractiveIndex = indexInside / indexOutside; + // Calculate the index of refraction + float refractiveIndex = indexOutside / indexInside; + // What if we are already inside the object? + if (dotProduct(normalVector, ray.direction) > 0) + { + normalVector = -normalVector; + refractiveIndex = indexInside / indexOutside; + } + + // Using the notation from the lecture + float cosineTheta = dotProduct(normalVector, -ray.direction); + float cosinePhi = std::sqrt(1 + refractiveIndex * refractiveIndex * (cosineTheta * cosineTheta - 1)); + // Calculate t, the new ray direction + Vector3d t = refractiveIndex * ray.direction + (refractiveIndex * cosineTheta - cosinePhi) * normalVector; + + // Create the refraction ray + Ray refractionRay = ray; + // Reset the ray + refractionRay.length = INFINITY; + refractionRay.primitive = nullptr; + + // Check whether it is a refraction. + if (dotProduct(t, normalVector) <= 0.0) + { + refractionRay.origin = ray.origin + (ray.length + REFR_EPS) * ray.direction; + refractionRay.direction = normalized(t); + } else + { // Otherwise, it is a total reflection. + refractionRay.origin = ray.origin + (ray.length - REFR_EPS) * ray.direction; + // Next we get the reflection vector + Vector3d const reflectionVector = + ray.direction - 2.0f * dotProduct(normalVector, ray.direction) * normalVector; + + // Change the ray direction and origin + refractionRay.direction = normalized(reflectionVector); + } + + // Send out a new refracted ray into the scene + return scene.traceRay(refractionRay) * objectColor; } - - // Using the notation from the lecture - float cosineTheta = dotProduct(normalVector, -ray.direction); - float cosinePhi = std::sqrt(1 + refractiveIndex * refractiveIndex * (cosineTheta * cosineTheta - 1)); - // Calculate t, the new ray direction - Vector3d t = refractiveIndex * ray.direction + (refractiveIndex * cosineTheta - cosinePhi) * normalVector; - - // Create the refraction ray - Ray refractionRay = ray; - // Reset the ray - refractionRay.length = INFINITY; - refractionRay.primitive = nullptr; - - // Check whether it is a refraction. - if (dotProduct(t, normalVector) <= 0.0) { - refractionRay.origin = ray.origin + (ray.length + REFR_EPS) * ray.direction; - refractionRay.direction = normalized(t); - } else { // Otherwise, it is a total reflection. - refractionRay.origin = ray.origin + (ray.length - REFR_EPS) * ray.direction; - // Next we get the reflection vector - Vector3d const reflectionVector = ray.direction - 2.0f * dotProduct(normalVector, ray.direction) * normalVector; - - // Change the ray direction and origin - refractionRay.direction = normalized(reflectionVector); - } - - // Send out a new refracted ray into the scene - return scene.traceRay(refractionRay); - } - return Color(0.0f, 0.0f, 0.0f); + return Color(0.0f, 0.0f, 0.0f); } -bool RefractionShader::isTransparent() const { return true; } +bool RefractionShader::isTransparent() const +{ return true; } diff --git a/shader/refractionshader.h b/shader/refractionshader.h index 5c1043e..7b489da 100644 --- a/shader/refractionshader.h +++ b/shader/refractionshader.h @@ -3,19 +3,22 @@ #include "shader/shader.h" -class RefractionShader : public Shader { +class RefractionShader : public Shader +{ public: - // Constructor - RefractionShader(float indexInside, float indexOutside); + // Constructor + RefractionShader(float indexInside, float indexOutside, Color const &objectColor = Color(1, 1, 1)); - // Shader functions - Color shade(Scene const &scene, Ray const &ray) const override; - bool isTransparent() const override; + // Shader functions + Color shade(Scene const &scene, Ray const &ray) const override; + + bool isTransparent() const override; private: - float indexInside; - float indexOutside; + float indexInside; + float indexOutside; + Color objectColor; }; #endif From dcae587b8cc9171dc23e08ec31f5b1720ed392e1 Mon Sep 17 00:00:00 2001 From: Maximilian Giller Date: Tue, 24 Jan 2023 20:23:16 +0100 Subject: [PATCH 02/14] Added light loss to refraction shader --- shader/refractionshader.cpp | 11 ++++++++--- shader/refractionshader.h | 3 ++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/shader/refractionshader.cpp b/shader/refractionshader.cpp index e13916a..5312e30 100644 --- a/shader/refractionshader.cpp +++ b/shader/refractionshader.cpp @@ -1,8 +1,8 @@ #include "scene/scene.h" #include "shader/refractionshader.h" -RefractionShader::RefractionShader(float indexInside, float indexOutside, Color const &objectColor) : indexInside( - indexInside), indexOutside(indexOutside), objectColor(objectColor) +RefractionShader::RefractionShader(float indexInside, float indexOutside, Color const &objectColor, float lightLoss) : indexInside( + indexInside), indexOutside(indexOutside), objectColor(objectColor), lightLoss(lightLoss) {} Color RefractionShader::shade(Scene const &scene, Ray const &ray) const @@ -51,7 +51,12 @@ Color RefractionShader::shade(Scene const &scene, Ray const &ray) const } // Send out a new refracted ray into the scene - return scene.traceRay(refractionRay) * objectColor; + Color hitColor = scene.traceRay(refractionRay); + + // Calculate light lost + float lightRemaining = 1; + if (refractionRay.primitive == ray.primitive) lightRemaining = 1 - lightLoss + exp(-refractionRay.length / 10) * lightLoss; + return hitColor * objectColor * lightRemaining; } return Color(0.0f, 0.0f, 0.0f); } diff --git a/shader/refractionshader.h b/shader/refractionshader.h index 7b489da..1da09c0 100644 --- a/shader/refractionshader.h +++ b/shader/refractionshader.h @@ -8,7 +8,7 @@ class RefractionShader : public Shader public: // Constructor - RefractionShader(float indexInside, float indexOutside, Color const &objectColor = Color(1, 1, 1)); + RefractionShader(float indexInside, float indexOutside, Color const &objectColor = Color(1, 1, 1), float lightLoss = 0); // Shader functions Color shade(Scene const &scene, Ray const &ray) const override; @@ -18,6 +18,7 @@ public: private: float indexInside; float indexOutside; + float lightLoss; Color objectColor; }; From 17fd35fe765c989cba0ed615ba0e9d00432a4d08 Mon Sep 17 00:00:00 2001 From: Maximilian Giller Date: Tue, 24 Jan 2023 21:11:30 +0100 Subject: [PATCH 03/14] 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 From 5db502d77af4fe436926f8cc4e8004677ffa2390 Mon Sep 17 00:00:00 2001 From: Maximilian Giller Date: Tue, 24 Jan 2023 22:03:33 +0100 Subject: [PATCH 04/14] Adds concept of distance to illumination --- light/ambientlight.cpp | 2 +- light/light.h | 1 + light/pointlight.cpp | 1 + light/spotlight.cpp | 1 + light/sunlight.cpp | 1 + 5 files changed, 5 insertions(+), 1 deletion(-) diff --git a/light/ambientlight.cpp b/light/ambientlight.cpp index 0bde3bb..bac614d 100644 --- a/light/ambientlight.cpp +++ b/light/ambientlight.cpp @@ -1,5 +1,5 @@ #include "light/ambientlight.h" Light::Illumination AmbientLight::illuminate(Scene const &scene, Ray const &ray) const { - return {this->color * this->intensity, -ray.normal}; + return {this->color * this->intensity, -ray.normal, 0}; } diff --git a/light/light.h b/light/light.h index 08f5732..6abc093 100644 --- a/light/light.h +++ b/light/light.h @@ -13,6 +13,7 @@ public: struct Illumination { Color color; Vector3d direction; + float distance; }; // Constructor / Destructor diff --git a/light/pointlight.cpp b/light/pointlight.cpp index 359352a..fa02b84 100644 --- a/light/pointlight.cpp +++ b/light/pointlight.cpp @@ -12,6 +12,7 @@ Light::Illumination PointLight::illuminate(Scene const &scene, Ray const &ray) c // Precompute the distance from the light source float const distance = length(target - this->position); + illum.distance = distance; // Define a secondary ray from the surface point to the light source. Ray lightRay; diff --git a/light/spotlight.cpp b/light/spotlight.cpp index 3376249..5ef3427 100644 --- a/light/spotlight.cpp +++ b/light/spotlight.cpp @@ -13,6 +13,7 @@ Light::Illumination SpotLight::illuminate(Scene const &scene, Ray const &ray) co // Precompute the distance from the light source float const distance = length(target - this->position); + illum.distance = distance; // Define a secondary ray from the surface point to the light source Ray lightRay; diff --git a/light/sunlight.cpp b/light/sunlight.cpp index c34bdf1..6c6b569 100644 --- a/light/sunlight.cpp +++ b/light/sunlight.cpp @@ -12,6 +12,7 @@ Light::Illumination SunLight::illuminate(Scene const &scene, Ray const &ray) con // Illumination object Illumination illum; illum.direction = this->direction; + illum.distance = INFINITY; // Define a secondary ray from the surface point to the light source. Ray lightRay; From da1f885e9a2d2b10a460ed958b25134b7a191ac6 Mon Sep 17 00:00:00 2001 From: Maximilian Giller Date: Tue, 24 Jan 2023 22:55:48 +0100 Subject: [PATCH 05/14] Trying to add volumetric light to clouds --- common/noise/noise.cpp | 2 + fancy1.cpp | 18 ++++-- scene/fastscene.cpp | 4 +- scene/simplescene.cpp | 4 +- shader/cloudshader.cpp | 106 ++++++++++++++++++++---------------- shader/cloudshader.h | 7 ++- shader/refractionshader.cpp | 4 +- shader/refractionshader.h | 2 +- shader/shader.h | 5 +- 9 files changed, 86 insertions(+), 66 deletions(-) diff --git a/common/noise/noise.cpp b/common/noise/noise.cpp index 079e58c..12b8699 100644 --- a/common/noise/noise.cpp +++ b/common/noise/noise.cpp @@ -87,6 +87,8 @@ Noise::Noise(int size) */ float Noise::interpolate(float a0, float a1, float w) const { + if (0.0 > w) return a0; + if (1.0 < w) return a1; /* // You may want clamping by inserting: * if (0.0 > w) return a0; * if (1.0 < w) return a1; diff --git a/fancy1.cpp b/fancy1.cpp index 83169a3..f2c2eac 100644 --- a/fancy1.cpp +++ b/fancy1.cpp @@ -22,6 +22,8 @@ #include "shader/noiseshader.h" #include "common/noise/cloudnoise.h" #include "light/sunlight.h" +#include "scene/simplescene.h" +#include "shader/refractionshader.h" int main() { @@ -34,9 +36,15 @@ int main() scene.add(std::make_shared(0.3f)); // Add the bus - auto busShader = std::make_shared(mainLight); - scene.addObj("data/Bus/source/T07-11M.obj", Vector3d(1.0f, 1.0f, 1.0f), Vector3d(10.0f, 0.0f, 10.0f), - busShader); +// auto busShader = std::make_shared(mainLight); +// scene.addObj("data/Bus/source/T07-11M.obj", Vector3d(1.0f, 1.0f, 1.0f), Vector3d(15.0f, 0.0f, 10.0f), +// busShader); + + // Refraction boxes + auto boxShader = std::make_shared(1.05f, 1, Color(1,1,0), 0.7f); + scene.add(std::make_shared(Vector3d(5.0f, 3.0f, 10.0f), Vector3d(3.0f, 3.0f, 3.0f), boxShader)); + auto boxShader1 = std::make_shared(1.05f, 1, Color(0,1,1), 0.7f); + scene.add(std::make_shared(Vector3d(9.0f, 3.0f, 12.0f), Vector3d(3.0f, 3.0f, 3.0f), boxShader1)); // Add floor auto floorShader = std::make_shared(Color(0.9f, 0.9f, 0.9f)); @@ -50,7 +58,7 @@ int main() cloudSettings.densityTreshold = 0.49f; cloudSettings.densityAbsorption = 0.9f; auto cloudShader = std::make_shared(cloudSettings); - scene.add(std::make_shared(Vector3d(5.0f, 8.0f, 5.0f), Vector3d(50.0f, 5.0f, 50.0f), cloudShader)); + scene.add(std::make_shared(Vector3d(20.0f, 8.0f, 20.0f), Vector3d(50.0f, 5.0f, 50.0f), cloudShader)); // build the tree scene.buildTree(); @@ -58,7 +66,7 @@ int main() // Set up the camera PerspectiveCamera camera; camera.setPosition(Vector3d(0.0f, 3.0f, 0.0f)); - camera.setForwardDirection(normalized(Vector3d(1.0f, 0.15f, 0.6f))); + camera.setForwardDirection(normalized(Vector3d(1.0f, 0.15f, 1.0f))); camera.setUpDirection(normalized(Vector3d(0.0f, 1.0f, 0.0f))); camera.setFovAngle(90.0f); diff --git a/scene/fastscene.cpp b/scene/fastscene.cpp index 740f3ad..80156ef 100644 --- a/scene/fastscene.cpp +++ b/scene/fastscene.cpp @@ -235,9 +235,7 @@ Color FastScene::getTransparency(Ray &ray, float maxDistance) const 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); + Color t = i->shader()->transparency(*this, r, maxDistance); transparency.r *= t.r; transparency.g *= t.g; transparency.b *= t.b; diff --git a/scene/simplescene.cpp b/scene/simplescene.cpp index 2aaf286..891b30d 100644 --- a/scene/simplescene.cpp +++ b/scene/simplescene.cpp @@ -29,9 +29,7 @@ Color SimpleScene::getTransparency(Ray &ray, float maxDistance) const 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); + Color t = i->shader()->transparency(*this, r, maxDistance); transparency.r *= t.r; transparency.g *= t.g; transparency.b *= t.b; diff --git a/shader/cloudshader.cpp b/shader/cloudshader.cpp index b01ac27..2f0e120 100644 --- a/shader/cloudshader.cpp +++ b/shader/cloudshader.cpp @@ -1,6 +1,5 @@ #include "cloudshader.h" #include "common/noise/cloudnoise.h" -#include "common/noise/perlinnoise.h" Color CloudShader::shade(const Scene &scene, const Ray &ray) const @@ -46,22 +45,15 @@ Color CloudShader::shade(const Scene &scene, const Ray &ray) const // Get data at point float sampleDensity = getCloudDensity(samplePoint) * stepLength; -// cloudColor += lightMarch(scene, samplePoint, ray); + + if (sampleDensity > REFR_EPS) { + cloudColor += lightMarch(scene, samplePoint, ray); + } transmittance *= exp(-sampleDensity * stepLength * settings.densityAbsorption); if (transmittance <= TRANSMITTANCE_BREAK) break; // No need to continue } - // Add some ambient and diffuse lighting -// cloud += scene.ambientLight() * material.ambient(); -// for (const auto &light: scene.lights()) -// { -// Light::Illumination illumination = light->illuminate(scene, ray); -// if (illumination.distance == 0.0f) continue; // Skip ambient light -// float diffuse = dotProduct(normal, illumination.direction); -// cloud += material.cloud() * illumination.cloud * diffuse; -// } - return background * transmittance + (1.0f - transmittance) * cloudColor; } @@ -82,7 +74,8 @@ float CloudShader::getCloudDensity(Vector3d point) const float density = cloudNoise.getNoise(point); - // Treshold + // Threshold + // TODO: Smooth out! density = std::max(0.0f, density - settings.densityTreshold) * settings.densityIntensity; return density; @@ -97,45 +90,64 @@ Color CloudShader::lightMarch(const Scene &scene, Vector3d position, const Ray & { auto illumination = light->illuminate(scene, position); - // Get light direction - Vector3d lightDirection = normalized(illumination.direction); // Points from surface to light - - // Get length of remaining cloud in light direction - float cloudLength = 0.0f; - - Ray cloudRay = ray; - cloudRay.origin = position; - cloudRay.direction = lightDirection; - cloudRay.length = INFINITY; - cloudRay.primitive = nullptr; - - // Find other end of cloud - if (!ray.primitive->intersect(cloudRay) || cloudRay.length == INFINITY) - { - // No cloud or at edge + // Handle ambient lights + if (illumination.distance == 0.0f) { + cloudColor += illumination.color; continue; } - cloudLength = cloudRay.length; - // Calculate step length - int lightSamples = settings.lightSamples; - float stepLength = cloudLength / lightSamples; + // Light ray + Ray lightRay; + lightRay.origin = position; + lightRay.direction = illumination.direction; + lightRay.length = 0; // Starting in cloud itself - // Step through cloud - float transmittance = 0.0f; - for (int i = 0; i < lightSamples; ++i) - { - Vector3d samplePoint = position + i * stepLength * lightDirection; - float density = getCloudDensity(samplePoint) * stepLength; - transmittance *= exp(-density * settings.densityAbsorption); - - if (transmittance <= TRANSMITTANCE_BREAK) break; // No need to continue - } - -// float lightAbsorption = dotProduct(lightDirection, ray.direction); // Approaches 1 when light is parallel to ray - - cloudColor += transmittance * illumination.color; + Color transparency = this->transparency(scene, lightRay, illumination.distance); + cloudColor += transparency * illumination.color; } return cloudColor; } + +Color CloudShader::transparency(const Scene &scene, const Ray &ray, float maxLength) const +{ + Vector3d startPoint = ray.origin + ray.direction * (ray.length + 0.0001f); + + // Determine length of cloud + float cloudLength = 0.0f; + + Ray cloudRay = ray; + cloudRay.origin = startPoint; + cloudRay.length = INFINITY; + cloudRay.primitive = nullptr; + + // Get out of cloud primitive first + if (!ray.primitive->intersect(cloudRay) || cloudRay.length == INFINITY || cloudRay.length <= 0) + { + // Something went wrong + return Color(1, 1, 1); + } + cloudLength = std::min(cloudRay.length, maxLength - ray.length); + + // Calculate step length + int noiseSamples = settings.lightSamples; + float stepLength = cloudLength / noiseSamples; + + // Step through cloud + float transmittance = 1.0f; + for (int i = 0; i < noiseSamples; ++i) + { + // Get sample point + Vector3d samplePoint = startPoint + i * stepLength * ray.direction; + + // Get data at point + float sampleDensity = getCloudDensity(samplePoint) * stepLength; + + transmittance *= exp(-sampleDensity * stepLength); + if (transmittance <= TRANSMITTANCE_BREAK) break; // No need to continue + } + + transmittance = 1 - (1 - transmittance) * settings.shadowIntensity; + + return Color(1, 1, 1) * transmittance; +} diff --git a/shader/cloudshader.h b/shader/cloudshader.h index e2c6148..82670c5 100644 --- a/shader/cloudshader.h +++ b/shader/cloudshader.h @@ -8,18 +8,18 @@ #include "common/noise/worleynoise.h" int const NOISE_SIZE = 128; -int const TRANSMITTANCE_BREAK = 0.01f; // If transmittance goes below this limit, the cloud is considered opaque +int const TRANSMITTANCE_BREAK = 0.005f; // If transmittance goes below this limit, the cloud is considered opaque struct CloudSettings { int densitySamples = 100; - int lightSamples = 25; + int lightSamples = 20; float scale = 10; float densityTreshold = 0.55f; float densityIntensity = 2.5f; float densityAbsorption = 2; - Color cloudColor = Color(1, 1, 1); float darknessThreshold = 0.1f; + float shadowIntensity = 0.8f; }; class CloudShader : public Shader @@ -29,6 +29,7 @@ public: // Shader functions Color shade(Scene const &scene, Ray const &ray) const; + Color transparency(const Scene &scene, const Ray &ray, float maxLength) const override; private: CloudSettings settings; diff --git a/shader/refractionshader.cpp b/shader/refractionshader.cpp index f6aa6d1..8cfa49f 100644 --- a/shader/refractionshader.cpp +++ b/shader/refractionshader.cpp @@ -69,7 +69,7 @@ float RefractionShader::remainingLightIntensity(float distanceThroughObject) con bool RefractionShader::isTransparent() const { return true; } -Color RefractionShader::transparency(const Scene &scene, const Ray &ray) const +Color RefractionShader::transparency(const Scene &scene, const Ray &ray, float maxLength) const { // Determine length through the object Ray lengthRay = ray; @@ -80,7 +80,7 @@ Color RefractionShader::transparency(const Scene &scene, const Ray &ray) const scene.traceRay(lengthRay); - float transparencyDistance = std::min(ray.length, lengthRay.length); + float transparencyDistance = std::min(maxLength - ray.length, lengthRay.length); float lightRemaining = 1; if (ray.primitive == lengthRay.primitive) lightRemaining = remainingLightIntensity(transparencyDistance); diff --git a/shader/refractionshader.h b/shader/refractionshader.h index f3873f1..f6c2903 100644 --- a/shader/refractionshader.h +++ b/shader/refractionshader.h @@ -14,7 +14,7 @@ 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; + Color transparency(const Scene &scene, const Ray &ray, float maxLength) const override; private: float indexInside; diff --git a/shader/shader.h b/shader/shader.h index 9ba1342..0391418 100644 --- a/shader/shader.h +++ b/shader/shader.h @@ -22,10 +22,11 @@ public: /** * 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. + * @param ray Origin and direction of the desired path through the object. Origin might be inside or outside the object. Length of the ray is upper bound for the destination point, and might not go through the object completely. + * @param maxLength Maximum length of the ray. If the ray through the object is longer than this, it is cut off. * @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 + virtual Color transparency(const Scene &scene, const Ray &ray, float maxLength) const { return isTransparent() ? Color(1, 1, 1) : Color(0, 0, 0); } // Shader functions From 9dd5878f316733c2c98f7e8884aac50eaf410ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?arvid=20schr=C3=B6der?= Date: Wed, 25 Jan 2023 00:07:44 +0100 Subject: [PATCH 06/14] fixed nullptr --- shader/cloudshader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shader/cloudshader.cpp b/shader/cloudshader.cpp index 2f0e120..11a8160 100644 --- a/shader/cloudshader.cpp +++ b/shader/cloudshader.cpp @@ -122,7 +122,7 @@ Color CloudShader::transparency(const Scene &scene, const Ray &ray, float maxLen cloudRay.primitive = nullptr; // Get out of cloud primitive first - if (!ray.primitive->intersect(cloudRay) || cloudRay.length == INFINITY || cloudRay.length <= 0) + if (ray.primitive != nullptr && !ray.primitive->intersect(cloudRay) || cloudRay.length == INFINITY || cloudRay.length <= 0) { // Something went wrong return Color(1, 1, 1); From 8a1ec659a238ae365fe464a2d1dc58ffcd3a9361 Mon Sep 17 00:00:00 2001 From: Maximilian Giller Date: Thu, 26 Jan 2023 05:04:30 +0100 Subject: [PATCH 07/14] Clouds now interacting with lights, but especially sunlight is still buggy --- fancy1.cpp | 11 ++-- shader/cloudshader.cpp | 108 ++++++++++++++++++++++++++++-------- shader/cloudshader.h | 22 +++++--- shader/refractionshader.cpp | 97 ++++++++++++++++---------------- 4 files changed, 153 insertions(+), 85 deletions(-) diff --git a/fancy1.cpp b/fancy1.cpp index f2c2eac..9600c1b 100644 --- a/fancy1.cpp +++ b/fancy1.cpp @@ -31,9 +31,11 @@ int main() scene.setBackgroundColor(Color(0.529f, 0.808f, 0.922f)); // Add lights - auto mainLight = std::make_shared(Vector3d(-1.0f, -0.5f, -1.0f), 10.0f); + auto mainLight = std::make_shared(Vector3d(-1.0f, -0.5f, -1.0f), 10.0f, Color(1, 0.79f, 0.62f)); scene.add(mainLight); - scene.add(std::make_shared(0.3f)); +// scene.add(std::make_shared(0.3f)); +// auto light = std::make_shared(Vector3d(25.0f, 10.0f, 25.0f), 100.0f); +// scene.add(light); // Add the bus // auto busShader = std::make_shared(mainLight); @@ -54,11 +56,8 @@ int main() // Add box for volume shader auto cloudSettings = CloudSettings(); cloudSettings.scale = 15.0f; - cloudSettings.densityIntensity = 10.0f; - cloudSettings.densityTreshold = 0.49f; - cloudSettings.densityAbsorption = 0.9f; auto cloudShader = std::make_shared(cloudSettings); - scene.add(std::make_shared(Vector3d(20.0f, 8.0f, 20.0f), Vector3d(50.0f, 5.0f, 50.0f), cloudShader)); + scene.add(std::make_shared(Vector3d(20.0f, 10.0f, 20.0f), Vector3d(50.0f, 10.0f, 50.0f), cloudShader)); // build the tree scene.buildTree(); diff --git a/shader/cloudshader.cpp b/shader/cloudshader.cpp index 11a8160..1a88fac 100644 --- a/shader/cloudshader.cpp +++ b/shader/cloudshader.cpp @@ -36,25 +36,28 @@ Color CloudShader::shade(const Scene &scene, const Ray &ray) const float stepLength = cloudLength / noiseSamples; // Step through cloud - float transmittance = 1.0f; - Color cloudColor = Color(1, 1, 1); + float transmittance = 1; + Color cloudColor = Color(0, 0, 0); for (int i = 0; i < noiseSamples; ++i) { // Get sample point - Vector3d samplePoint = hitPoint + i * stepLength * ray.direction; + Vector3d lengthDirection = i * stepLength * ray.direction; + Vector3d samplePoint = hitPoint + lengthDirection; // Get data at point float sampleDensity = getCloudDensity(samplePoint) * stepLength; - if (sampleDensity > REFR_EPS) { - cloudColor += lightMarch(scene, samplePoint, ray); + if (sampleDensity > 0) + { + cloudColor += lightMarch(scene, samplePoint, lengthDirection) * stepLength * sampleDensity; } - transmittance *= exp(-sampleDensity * stepLength * settings.densityAbsorption); - if (transmittance <= TRANSMITTANCE_BREAK) break; // No need to continue + transmittance *= exp(-sampleDensity * stepLength * settings.lightAbsorptionThroughCloud); + + if (transmittance < TRANSMITTANCE_BREAK) break; // Cloud is effectively opaque } - return background * transmittance + (1.0f - transmittance) * cloudColor; + return background * transmittance + cloudColor; } bool CloudShader::isTransparent() const @@ -76,40 +79,60 @@ float CloudShader::getCloudDensity(Vector3d point) const // Threshold // TODO: Smooth out! - density = std::max(0.0f, density - settings.densityTreshold) * settings.densityIntensity; + density = std::max(0.0f, density + settings.densityOffset) * settings.densityIntensity; return density; } -Color CloudShader::lightMarch(const Scene &scene, Vector3d position, const Ray &ray) const +Color CloudShader::lightMarch(const Scene &scene, Vector3d currentInCloudPosition, Vector3d lengthDistance) const { Color cloudColor; // For alle lights for (const auto &light: scene.lights()) { - auto illumination = light->illuminate(scene, position); + Ray ray = Ray(currentInCloudPosition - lengthDistance, normalized(lengthDistance)); + ray.length = length(lengthDistance); + auto illumination = light->illuminate(scene, ray); // Handle ambient lights - if (illumination.distance == 0.0f) { + if (illumination.distance == 0.0f) + { cloudColor += illumination.color; continue; } // Light ray Ray lightRay; - lightRay.origin = position; + lightRay.origin = currentInCloudPosition; lightRay.direction = illumination.direction; lightRay.length = 0; // Starting in cloud itself - Color transparency = this->transparency(scene, lightRay, illumination.distance); - cloudColor += transparency * illumination.color; + float density = this->rayDensity(lightRay, illumination.distance); + density *= settings.lightAbsorptionTowardsLight; + + // Proper light calculation + float transmittance = exp(-density) * (1 - exp(-density * 2)); + float scatter = scatterFactor(normalized(lengthDistance), illumination.direction); + + float factor = settings.darknessThreshold + (scatter * transmittance) * (1 - settings.darknessThreshold); // (transmittance * scatter) + cloudColor += factor * illumination.color; } return cloudColor; } Color CloudShader::transparency(const Scene &scene, const Ray &ray, float maxLength) const +{ + float density = rayDensity(ray, maxLength); + + float transmittance = exp(-density * settings.shadowLightAbsorption); + transmittance = 1 - (1 - transmittance) * settings.shadowIntensity; + + return Color(1, 1, 1) * transmittance; +} + +float CloudShader::rayDensity(const Ray &ray, float maxLength) const { Vector3d startPoint = ray.origin + ray.direction * (ray.length + 0.0001f); @@ -122,19 +145,20 @@ Color CloudShader::transparency(const Scene &scene, const Ray &ray, float maxLen cloudRay.primitive = nullptr; // Get out of cloud primitive first - if (ray.primitive != nullptr && !ray.primitive->intersect(cloudRay) || cloudRay.length == INFINITY || cloudRay.length <= 0) + if (ray.primitive != nullptr && !ray.primitive->intersect(cloudRay) || cloudRay.length == INFINITY || + cloudRay.length <= 0) { - // Something went wrong - return Color(1, 1, 1); + // Something went wrong => No density + return 0; } cloudLength = std::min(cloudRay.length, maxLength - ray.length); // Calculate step length int noiseSamples = settings.lightSamples; - float stepLength = cloudLength / noiseSamples; + float stepLength = cloudLength / (float) noiseSamples; // Step through cloud - float transmittance = 1.0f; + float density = 1.0f; for (int i = 0; i < noiseSamples; ++i) { // Get sample point @@ -143,11 +167,47 @@ Color CloudShader::transparency(const Scene &scene, const Ray &ray, float maxLen // Get data at point float sampleDensity = getCloudDensity(samplePoint) * stepLength; - transmittance *= exp(-sampleDensity * stepLength); - if (transmittance <= TRANSMITTANCE_BREAK) break; // No need to continue + density += sampleDensity * stepLength; } - transmittance = 1 - (1 - transmittance) * settings.shadowIntensity; + // If there is length left, check if it is in the cloud recursively + if (cloudLength < maxLength - ray.length) + { + Vector3d endPoint = startPoint + (cloudLength + REFR_EPS) * ray.direction; + Ray recursiveRay = cloudRay; + recursiveRay.origin = endPoint; + recursiveRay.length = 0; + recursiveRay.primitive = nullptr; - return Color(1, 1, 1) * transmittance; + if (ray.primitive != nullptr && ray.primitive->intersect(recursiveRay) && recursiveRay.length > 0) + { + density += rayDensity(recursiveRay, maxLength - (cloudLength + REFR_EPS)); + } + } + + return density; +} + +float CloudShader::scatterFactor(Vector3d visualRay, Vector3d illuminationRay) const +{ + // The asymmetry parameter + float g = 0.7f; + + // The angle between the visual and illumination rays + float cosTheta = dotProduct(visualRay, illuminationRay); + + // The Dual-Lob Henyey-Greenstein function + float blend = .5; + float scatter = HenyeyGreenstein(cosTheta,g) * (1-blend) + HenyeyGreenstein(cosTheta,-g) * blend; + + // Clamp the result to the range [0, 1] + scatter = std::max(std::min(scatter, 1.0f), 0.0f); + + return scatter; +} + +float CloudShader::HenyeyGreenstein(float cosTheta, float g) const +{ + float g2 = g * g; + return (1 - g2) / (4 * 3.1415f * pow(1 + g2 - 2 * g * (cosTheta), 1.5f)); } diff --git a/shader/cloudshader.h b/shader/cloudshader.h index 82670c5..9b89ca7 100644 --- a/shader/cloudshader.h +++ b/shader/cloudshader.h @@ -8,18 +8,20 @@ #include "common/noise/worleynoise.h" int const NOISE_SIZE = 128; -int const TRANSMITTANCE_BREAK = 0.005f; // If transmittance goes below this limit, the cloud is considered opaque +float const TRANSMITTANCE_BREAK = 0.005f; // If transmittance goes below this limit, the cloud is considered opaque struct CloudSettings { int densitySamples = 100; - int lightSamples = 20; + int lightSamples = 100; float scale = 10; - float densityTreshold = 0.55f; - float densityIntensity = 2.5f; - float densityAbsorption = 2; - float darknessThreshold = 0.1f; + float densityOffset = -0.55f; + float densityIntensity = 7.0f; + float darknessThreshold = 0.07f; + float shadowLightAbsorption = 2; float shadowIntensity = 0.8f; + float lightAbsorptionTowardsLight = 0.94f; + float lightAbsorptionThroughCloud = 0.85f; }; class CloudShader : public Shader @@ -41,7 +43,13 @@ private: float getCloudDensity(Vector3d point) const; - Color lightMarch(const Scene &scene, Vector3d position, const Ray &ray) const; + Color lightMarch(const Scene &scene, Vector3d currentInCloudPosition, Vector3d lengthDistance) const; + + float rayDensity(const Ray &ray, float maxLength) const; + + float scatterFactor(Vector3d visualRay, Vector3d illuminationRay) const; + + float HenyeyGreenstein(float cosTheta, float g) const; }; diff --git a/shader/refractionshader.cpp b/shader/refractionshader.cpp index 8cfa49f..114f75c 100644 --- a/shader/refractionshader.cpp +++ b/shader/refractionshader.cpp @@ -9,56 +9,57 @@ RefractionShader::RefractionShader(float indexInside, float indexOutside, Color Color RefractionShader::shade(Scene const &scene, Ray const &ray) const { // Circumvent getting environment map color into the mix - if (ray.getRemainingBounces() > 0) + if (ray.getRemainingBounces() <= 0) { - // Get the normal of the primitive which was hit - Vector3d normalVector = ray.normal; - - // Calculate the index of refraction - float refractiveIndex = indexOutside / indexInside; - // What if we are already inside the object? - if (dotProduct(normalVector, ray.direction) > 0) - { - normalVector = -normalVector; - refractiveIndex = indexInside / indexOutside; - } - - // Using the notation from the lecture - float cosineTheta = dotProduct(normalVector, -ray.direction); - float cosinePhi = std::sqrt(1 + refractiveIndex * refractiveIndex * (cosineTheta * cosineTheta - 1)); - // Calculate t, the new ray direction - Vector3d t = refractiveIndex * ray.direction + (refractiveIndex * cosineTheta - cosinePhi) * normalVector; - - // Create the refraction ray - Ray refractionRay = ray; - // Reset the ray - refractionRay.length = INFINITY; - refractionRay.primitive = nullptr; - - // Check whether it is a refraction. - if (dotProduct(t, normalVector) <= 0.0) - { - refractionRay.origin = ray.origin + (ray.length + REFR_EPS) * ray.direction; - refractionRay.direction = normalized(t); - } else - { // Otherwise, it is a total reflection. - refractionRay.origin = ray.origin + (ray.length - REFR_EPS) * ray.direction; - // Next we get the reflection vector - Vector3d const reflectionVector = - ray.direction - 2.0f * dotProduct(normalVector, ray.direction) * normalVector; - - // Change the ray direction and origin - refractionRay.direction = normalized(reflectionVector); - } - - // Send out a new refracted ray into the scene - Color hitColor = scene.traceRay(refractionRay); - float lightRemaining = 1; - if (ray.primitive == refractionRay.primitive) lightRemaining = remainingLightIntensity(refractionRay.length); - - return hitColor * objectColor * lightRemaining; + return Color(0.0f, 0.0f, 0.0f); } - return Color(0.0f, 0.0f, 0.0f); + + // Get the normal of the primitive which was hit + Vector3d normalVector = ray.normal; + + // Calculate the index of refraction + float refractiveIndex = indexOutside / indexInside; + // What if we are already inside the object? + if (dotProduct(normalVector, ray.direction) > 0) + { + normalVector = -normalVector; + refractiveIndex = indexInside / indexOutside; + } + + // Using the notation from the lecture + float cosineTheta = dotProduct(normalVector, -ray.direction); + float cosinePhi = std::sqrt(1 + refractiveIndex * refractiveIndex * (cosineTheta * cosineTheta - 1)); + // Calculate t, the new ray direction + Vector3d t = refractiveIndex * ray.direction + (refractiveIndex * cosineTheta - cosinePhi) * normalVector; + + // Create the refraction ray + Ray refractionRay = ray; + // Reset the ray + refractionRay.length = INFINITY; + refractionRay.primitive = nullptr; + + // Check whether it is a refraction. + if (dotProduct(t, normalVector) <= 0.0) + { + refractionRay.origin = ray.origin + (ray.length + REFR_EPS) * ray.direction; + refractionRay.direction = normalized(t); + } else + { // Otherwise, it is a total reflection. + refractionRay.origin = ray.origin + (ray.length - REFR_EPS) * ray.direction; + // Next we get the reflection vector + Vector3d const reflectionVector = + ray.direction - 2.0f * dotProduct(normalVector, ray.direction) * normalVector; + + // Change the ray direction and origin + refractionRay.direction = normalized(reflectionVector); + } + + // Send out a new refracted ray into the scene + Color hitColor = scene.traceRay(refractionRay); + float lightRemaining = 1; + if (ray.primitive == refractionRay.primitive) lightRemaining = remainingLightIntensity(refractionRay.length); + + return hitColor * objectColor * lightRemaining; } float RefractionShader::remainingLightIntensity(float distanceThroughObject) const From cc5b66e33862de3381c7389dbc5c7c9ef9a93f62 Mon Sep 17 00:00:00 2001 From: Maximilian Giller Date: Thu, 26 Jan 2023 20:14:45 +0100 Subject: [PATCH 08/14] Fixed shadow not being fully transparent --- fancy1.cpp | 2 +- shader/cloudshader.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fancy1.cpp b/fancy1.cpp index 9600c1b..2936386 100644 --- a/fancy1.cpp +++ b/fancy1.cpp @@ -28,7 +28,7 @@ int main() { FastScene scene; - scene.setBackgroundColor(Color(0.529f, 0.808f, 0.922f)); + scene.setBackgroundColor(Color(0.529f, 0.808f, 0.922f) * 0.2f); // Add lights auto mainLight = std::make_shared(Vector3d(-1.0f, -0.5f, -1.0f), 10.0f, Color(1, 0.79f, 0.62f)); diff --git a/shader/cloudshader.h b/shader/cloudshader.h index 9b89ca7..c0773ea 100644 --- a/shader/cloudshader.h +++ b/shader/cloudshader.h @@ -15,11 +15,11 @@ struct CloudSettings int densitySamples = 100; int lightSamples = 100; float scale = 10; - float densityOffset = -0.55f; + float densityOffset = -0.57f; float densityIntensity = 7.0f; float darknessThreshold = 0.07f; - float shadowLightAbsorption = 2; float shadowIntensity = 0.8f; + float shadowLightAbsorption = 1; float lightAbsorptionTowardsLight = 0.94f; float lightAbsorptionThroughCloud = 0.85f; }; From ec59f251c7a4b06de44c0f8ebf186e109d49f580 Mon Sep 17 00:00:00 2001 From: Maximilian Giller Date: Thu, 26 Jan 2023 20:30:59 +0100 Subject: [PATCH 09/14] Removes wrong intial value for density --- fancy1.cpp | 4 ++-- shader/cloudshader.cpp | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/fancy1.cpp b/fancy1.cpp index 2936386..04271ee 100644 --- a/fancy1.cpp +++ b/fancy1.cpp @@ -28,10 +28,10 @@ int main() { FastScene scene; - scene.setBackgroundColor(Color(0.529f, 0.808f, 0.922f) * 0.2f); + scene.setBackgroundColor(Color(0.529f, 0.808f, 0.922f) * 1.0f); // Add lights - auto mainLight = std::make_shared(Vector3d(-1.0f, -0.5f, -1.0f), 10.0f, Color(1, 0.79f, 0.62f)); + auto mainLight = std::make_shared(Vector3d(-1.0f, -0.5f, -1.0f), 5.0f, Color(1, 0.79f, 0.62f)); scene.add(mainLight); // scene.add(std::make_shared(0.3f)); // auto light = std::make_shared(Vector3d(25.0f, 10.0f, 25.0f), 100.0f); diff --git a/shader/cloudshader.cpp b/shader/cloudshader.cpp index 1a88fac..6df0fd1 100644 --- a/shader/cloudshader.cpp +++ b/shader/cloudshader.cpp @@ -137,8 +137,6 @@ float CloudShader::rayDensity(const Ray &ray, float maxLength) const Vector3d startPoint = ray.origin + ray.direction * (ray.length + 0.0001f); // Determine length of cloud - float cloudLength = 0.0f; - Ray cloudRay = ray; cloudRay.origin = startPoint; cloudRay.length = INFINITY; @@ -151,14 +149,14 @@ float CloudShader::rayDensity(const Ray &ray, float maxLength) const // Something went wrong => No density return 0; } - cloudLength = std::min(cloudRay.length, maxLength - ray.length); + float cloudLength = std::min(cloudRay.length, maxLength - ray.length); // Calculate step length int noiseSamples = settings.lightSamples; float stepLength = cloudLength / (float) noiseSamples; // Step through cloud - float density = 1.0f; + float density = 0.0f; for (int i = 0; i < noiseSamples; ++i) { // Get sample point From 3f3c89b0d014ef863acd666f3ee25de35ed4566f Mon Sep 17 00:00:00 2001 From: Maximilian Giller Date: Thu, 26 Jan 2023 20:32:40 +0100 Subject: [PATCH 10/14] Performance improvement --- common/noise/cloudnoise.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/common/noise/cloudnoise.cpp b/common/noise/cloudnoise.cpp index f52a1a6..4b5a345 100644 --- a/common/noise/cloudnoise.cpp +++ b/common/noise/cloudnoise.cpp @@ -4,12 +4,14 @@ CloudNoise::CloudNoise(int size) : Noise(size) { + int minSize = std::min(32, size); + // Some worley noises - WorleyNoise worleyNoise1(size, 3); + WorleyNoise worleyNoise1(minSize, 3); WorleyNoise worleyNoise3(size, 15); // Some perlin noises - PerlinNoise perlinNoise1(size, 3); + PerlinNoise perlinNoise1(minSize, 3); PerlinNoise perlinNoise2(size, 15); // Generate the noise From d7c093944d712fc835f9c9eba2092f294373287d Mon Sep 17 00:00:00 2001 From: Maximilian Giller Date: Fri, 27 Jan 2023 05:22:43 +0100 Subject: [PATCH 11/14] Fixed sunlight not interacting with clouds --- light/sunlight.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/light/sunlight.cpp b/light/sunlight.cpp index 6c6b569..66c676b 100644 --- a/light/sunlight.cpp +++ b/light/sunlight.cpp @@ -14,7 +14,7 @@ Light::Illumination SunLight::illuminate(Scene const &scene, Ray const &ray) con illum.direction = this->direction; illum.distance = INFINITY; - // Define a secondary ray from the surface point to the light source. + // Define a secondary ray from the surfa ce point to the light source. Ray lightRay; lightRay.origin = target; lightRay.direction = -illum.direction; @@ -24,8 +24,10 @@ Light::Illumination SunLight::illuminate(Scene const &scene, Ray const &ray) con if (!scene.findOcclusion(lightRay)) { // 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 + float angleIntensity = 1; + // Only if normal is relevant, calculate angleIntensity + if (ray.normal != Vector3d(0, 0, 0)) + angleIntensity = dotProduct(-ray.normal, this->direction); // 0 if light is behind surface, 1 if light is in front of surface Color rayTransparency = scene.getTransparency(lightRay, INFINITY); illum.color = this->color * this->intensity * angleIntensity * rayTransparency; From 79446c289b28b005e750447fc02a111ca2b8157b Mon Sep 17 00:00:00 2001 From: Maximilian Giller Date: Fri, 27 Jan 2023 05:22:54 +0100 Subject: [PATCH 12/14] Clouds still bad --- fancy1.cpp | 11 ++++++----- shader/cloudshader.cpp | 38 +++++++++++++++++++++++--------------- shader/cloudshader.h | 15 +++++++++------ 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/fancy1.cpp b/fancy1.cpp index 04271ee..86b4830 100644 --- a/fancy1.cpp +++ b/fancy1.cpp @@ -29,9 +29,10 @@ int main() { FastScene scene; scene.setBackgroundColor(Color(0.529f, 0.808f, 0.922f) * 1.0f); +// scene.setBackgroundColor(Color(1, 0.79f, 0.62f) * 0.8f); // Add lights - auto mainLight = std::make_shared(Vector3d(-1.0f, -0.5f, -1.0f), 5.0f, Color(1, 0.79f, 0.62f)); + auto mainLight = std::make_shared(Vector3d(-1.0f, -0.5f, -1.0f), 2.0f, Color(1,1,1));//Color(1, 0.79f, 0.62f)); scene.add(mainLight); // scene.add(std::make_shared(0.3f)); // auto light = std::make_shared(Vector3d(25.0f, 10.0f, 25.0f), 100.0f); @@ -43,10 +44,10 @@ int main() // busShader); // Refraction boxes - auto boxShader = std::make_shared(1.05f, 1, Color(1,1,0), 0.7f); - scene.add(std::make_shared(Vector3d(5.0f, 3.0f, 10.0f), Vector3d(3.0f, 3.0f, 3.0f), boxShader)); - auto boxShader1 = std::make_shared(1.05f, 1, Color(0,1,1), 0.7f); - scene.add(std::make_shared(Vector3d(9.0f, 3.0f, 12.0f), Vector3d(3.0f, 3.0f, 3.0f), boxShader1)); +// auto boxShader = std::make_shared(1.05f, 1, Color(1,1,0), 0.7f); +// scene.add(std::make_shared(Vector3d(5.0f, 3.0f, 10.0f), Vector3d(3.0f, 3.0f, 3.0f), boxShader)); +// auto boxShader1 = std::make_shared(1.05f, 1, Color(0,1,1), 0.7f); +// scene.add(std::make_shared(Vector3d(9.0f, 3.0f, 12.0f), Vector3d(3.0f, 3.0f, 3.0f), boxShader1)); // Add floor auto floorShader = std::make_shared(Color(0.9f, 0.9f, 0.9f)); diff --git a/shader/cloudshader.cpp b/shader/cloudshader.cpp index 6df0fd1..830fde3 100644 --- a/shader/cloudshader.cpp +++ b/shader/cloudshader.cpp @@ -33,10 +33,10 @@ Color CloudShader::shade(const Scene &scene, const Ray &ray) const // Calculate step length int noiseSamples = settings.densitySamples; - float stepLength = cloudLength / noiseSamples; + float stepLength = cloudLength / (float) noiseSamples; // Step through cloud - float transmittance = 1; + float accumulatedDensity = 0.0f; Color cloudColor = Color(0, 0, 0); for (int i = 0; i < noiseSamples; ++i) { @@ -49,14 +49,14 @@ Color CloudShader::shade(const Scene &scene, const Ray &ray) const if (sampleDensity > 0) { - cloudColor += lightMarch(scene, samplePoint, lengthDirection) * stepLength * sampleDensity; + cloudColor += lightMarch(scene, samplePoint, lengthDirection, ray.primitive) * sampleDensity; } - transmittance *= exp(-sampleDensity * stepLength * settings.lightAbsorptionThroughCloud); - - if (transmittance < TRANSMITTANCE_BREAK) break; // Cloud is effectively opaque + accumulatedDensity += sampleDensity; } + float transmittance = exp(-accumulatedDensity * settings.lightAbsorptionThroughCloud); + return background * transmittance + cloudColor; } @@ -84,15 +84,17 @@ float CloudShader::getCloudDensity(Vector3d point) const return density; } -Color CloudShader::lightMarch(const Scene &scene, Vector3d currentInCloudPosition, Vector3d lengthDistance) const +Color CloudShader::lightMarch(const Scene &scene, Vector3d currentInCloudPosition, Vector3d lengthDistance, + const Primitive *cloudObject) const { - Color cloudColor; + Color cloudColor = Color(0, 0, 0); // For alle lights for (const auto &light: scene.lights()) { Ray ray = Ray(currentInCloudPosition - lengthDistance, normalized(lengthDistance)); ray.length = length(lengthDistance); + ray.primitive = cloudObject; auto illumination = light->illuminate(scene, ray); // Handle ambient lights @@ -106,22 +108,30 @@ Color CloudShader::lightMarch(const Scene &scene, Vector3d currentInCloudPositio Ray lightRay; lightRay.origin = currentInCloudPosition; lightRay.direction = illumination.direction; + lightRay.primitive = cloudObject; lightRay.length = 0; // Starting in cloud itself float density = this->rayDensity(lightRay, illumination.distance); density *= settings.lightAbsorptionTowardsLight; // Proper light calculation - float transmittance = exp(-density) * (1 - exp(-density * 2)); + float transmittance = getDensityTransmittance(density); float scatter = scatterFactor(normalized(lengthDistance), illumination.direction); - float factor = settings.darknessThreshold + (scatter * transmittance) * (1 - settings.darknessThreshold); // (transmittance * scatter) + // TODO: Back to default + float factor = settings.darknessThreshold + + (1.0f - settings.darknessThreshold) * (1); // (transmittance * scatter) cloudColor += factor * illumination.color; } return cloudColor; } +float CloudShader::getDensityTransmittance(float density) const +{ + return exp(-density) * (1 - exp(-density * 2)) / 0.4f; +} + Color CloudShader::transparency(const Scene &scene, const Ray &ray, float maxLength) const { float density = rayDensity(ray, maxLength); @@ -163,9 +173,7 @@ float CloudShader::rayDensity(const Ray &ray, float maxLength) const Vector3d samplePoint = startPoint + i * stepLength * ray.direction; // Get data at point - float sampleDensity = getCloudDensity(samplePoint) * stepLength; - - density += sampleDensity * stepLength; + density += getCloudDensity(samplePoint) * stepLength; } // If there is length left, check if it is in the cloud recursively @@ -189,14 +197,14 @@ float CloudShader::rayDensity(const Ray &ray, float maxLength) const float CloudShader::scatterFactor(Vector3d visualRay, Vector3d illuminationRay) const { // The asymmetry parameter - float g = 0.7f; + float g = settings.scatterWeight; // The angle between the visual and illumination rays float cosTheta = dotProduct(visualRay, illuminationRay); // The Dual-Lob Henyey-Greenstein function float blend = .5; - float scatter = HenyeyGreenstein(cosTheta,g) * (1-blend) + HenyeyGreenstein(cosTheta,-g) * blend; + float scatter = HenyeyGreenstein(cosTheta, g) * (1 - blend) + HenyeyGreenstein(cosTheta, -g) * blend; // Clamp the result to the range [0, 1] scatter = std::max(std::min(scatter, 1.0f), 0.0f); diff --git a/shader/cloudshader.h b/shader/cloudshader.h index c0773ea..cb4fc7a 100644 --- a/shader/cloudshader.h +++ b/shader/cloudshader.h @@ -16,12 +16,13 @@ struct CloudSettings int lightSamples = 100; float scale = 10; float densityOffset = -0.57f; - float densityIntensity = 7.0f; - float darknessThreshold = 0.07f; - float shadowIntensity = 0.8f; + float densityIntensity = 7.0f; // 7.0f + float darknessThreshold = 0.2f; // 0.07f + float shadowIntensity = 0.6f; float shadowLightAbsorption = 1; - float lightAbsorptionTowardsLight = 0.94f; - float lightAbsorptionThroughCloud = 0.85f; + float lightAbsorptionTowardsLight = 1.0f; + float lightAbsorptionThroughCloud = 0.5f; + float scatterWeight = 0.5f; }; class CloudShader : public Shader @@ -43,13 +44,15 @@ private: float getCloudDensity(Vector3d point) const; - Color lightMarch(const Scene &scene, Vector3d currentInCloudPosition, Vector3d lengthDistance) const; + Color lightMarch(const Scene &scene, Vector3d currentInCloudPosition, Vector3d lengthDistance, const Primitive *cloudObject) const; float rayDensity(const Ray &ray, float maxLength) const; float scatterFactor(Vector3d visualRay, Vector3d illuminationRay) const; float HenyeyGreenstein(float cosTheta, float g) const; + + float getDensityTransmittance(float density) const; }; From 81e9a162be9390ce00f54525fd3cb3be8e5f32cc Mon Sep 17 00:00:00 2001 From: Maximilian Giller Date: Fri, 27 Jan 2023 05:27:42 +0100 Subject: [PATCH 13/14] Reset some values and a somewhat decent result --- shader/cloudshader.cpp | 6 ++++-- shader/cloudshader.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/shader/cloudshader.cpp b/shader/cloudshader.cpp index 830fde3..5c4379c 100644 --- a/shader/cloudshader.cpp +++ b/shader/cloudshader.cpp @@ -55,6 +55,9 @@ Color CloudShader::shade(const Scene &scene, const Ray &ray) const accumulatedDensity += sampleDensity; } + if (accumulatedDensity > 1) + cloudColor /= accumulatedDensity; + float transmittance = exp(-accumulatedDensity * settings.lightAbsorptionThroughCloud); return background * transmittance + cloudColor; @@ -118,9 +121,8 @@ Color CloudShader::lightMarch(const Scene &scene, Vector3d currentInCloudPositio float transmittance = getDensityTransmittance(density); float scatter = scatterFactor(normalized(lengthDistance), illumination.direction); - // TODO: Back to default float factor = settings.darknessThreshold + - (1.0f - settings.darknessThreshold) * (1); // (transmittance * scatter) + (1.0f - settings.darknessThreshold) * (transmittance * scatter); // (transmittance * scatter) cloudColor += factor * illumination.color; } diff --git a/shader/cloudshader.h b/shader/cloudshader.h index cb4fc7a..d00f79c 100644 --- a/shader/cloudshader.h +++ b/shader/cloudshader.h @@ -17,7 +17,7 @@ struct CloudSettings float scale = 10; float densityOffset = -0.57f; float densityIntensity = 7.0f; // 7.0f - float darknessThreshold = 0.2f; // 0.07f + float darknessThreshold = 0.07f; // 0.07f float shadowIntensity = 0.6f; float shadowLightAbsorption = 1; float lightAbsorptionTowardsLight = 1.0f; From 9b228bff61f592760f9e1442afd898e255dd0a0c Mon Sep 17 00:00:00 2001 From: Maximilian Giller Date: Fri, 27 Jan 2023 05:36:27 +0100 Subject: [PATCH 14/14] SOme adjustments --- shader/cloudshader.cpp | 9 +++++++-- shader/cloudshader.h | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/shader/cloudshader.cpp b/shader/cloudshader.cpp index 5c4379c..5c9ae60 100644 --- a/shader/cloudshader.cpp +++ b/shader/cloudshader.cpp @@ -121,8 +121,13 @@ Color CloudShader::lightMarch(const Scene &scene, Vector3d currentInCloudPositio float transmittance = getDensityTransmittance(density); float scatter = scatterFactor(normalized(lengthDistance), illumination.direction); - float factor = settings.darknessThreshold + - (1.0f - settings.darknessThreshold) * (transmittance * scatter); // (transmittance * scatter) + float factor = transmittance; + if (density > 0) + { + factor = settings.darknessThreshold + + (1.0f - settings.darknessThreshold) * factor * scatter; + } + cloudColor += factor * illumination.color; } diff --git a/shader/cloudshader.h b/shader/cloudshader.h index d00f79c..a7c1cce 100644 --- a/shader/cloudshader.h +++ b/shader/cloudshader.h @@ -17,7 +17,7 @@ struct CloudSettings float scale = 10; float densityOffset = -0.57f; float densityIntensity = 7.0f; // 7.0f - float darknessThreshold = 0.07f; // 0.07f + float darknessThreshold = 0.1f; // 0.07f float shadowIntensity = 0.6f; float shadowLightAbsorption = 1; float lightAbsorptionTowardsLight = 1.0f;