diff --git a/fancy1.cpp b/fancy1.cpp index ffd0801..83169a3 100644 --- a/fancy1.cpp +++ b/fancy1.cpp @@ -29,7 +29,7 @@ int main() scene.setBackgroundColor(Color(0.529f, 0.808f, 0.922f)); // Add lights - auto mainLight = std::make_shared(Vector3d(1.0f, -1.0f, -1.0f), 10.0f); + auto mainLight = std::make_shared(Vector3d(-1.0f, -0.5f, -1.0f), 10.0f); scene.add(mainLight); scene.add(std::make_shared(0.3f)); diff --git a/shader/cloudshader.cpp b/shader/cloudshader.cpp index 23a51d4..b01ac27 100644 --- a/shader/cloudshader.cpp +++ b/shader/cloudshader.cpp @@ -38,6 +38,7 @@ Color CloudShader::shade(const Scene &scene, const Ray &ray) const // Step through cloud float transmittance = 1.0f; + Color cloudColor = Color(1, 1, 1); for (int i = 0; i < noiseSamples; ++i) { // Get sample point @@ -45,8 +46,10 @@ Color CloudShader::shade(const Scene &scene, const Ray &ray) const // Get data at point float sampleDensity = getCloudDensity(samplePoint) * stepLength; +// 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 @@ -59,7 +62,7 @@ Color CloudShader::shade(const Scene &scene, const Ray &ray) const // cloud += material.cloud() * illumination.cloud * diffuse; // } - return background * transmittance + (1.0f - transmittance) * settings.cloudColor; + return background * transmittance + (1.0f - transmittance) * cloudColor; } bool CloudShader::isTransparent() const @@ -84,3 +87,55 @@ float CloudShader::getCloudDensity(Vector3d point) const return density; } + +Color CloudShader::lightMarch(const Scene &scene, Vector3d position, const Ray &ray) const +{ + Color cloudColor; + + // For alle lights + for (const auto &light: scene.lights()) + { + 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 + continue; + } + cloudLength = cloudRay.length; + + // Calculate step length + int lightSamples = settings.lightSamples; + float stepLength = cloudLength / lightSamples; + + // 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; + } + + return cloudColor; +} diff --git a/shader/cloudshader.h b/shader/cloudshader.h index b170d24..e2c6148 100644 --- a/shader/cloudshader.h +++ b/shader/cloudshader.h @@ -3,19 +3,23 @@ #include "scene/scene.h" #include "shader.h" +#include "light/light.h" #include "primitive/primitive.h" #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 struct CloudSettings { int densitySamples = 100; + int lightSamples = 25; float scale = 10; float densityTreshold = 0.55f; float densityIntensity = 2.5f; float densityAbsorption = 2; Color cloudColor = Color(1, 1, 1); + float darknessThreshold = 0.1f; }; class CloudShader : public Shader @@ -35,6 +39,8 @@ private: Noise cloudNoise; float getCloudDensity(Vector3d point) const; + + Color lightMarch(const Scene &scene, Vector3d position, const Ray &ray) const; };