#include "cloudshader.h" #include "common/noise/cloudnoise.h" #include "common/noise/perlinnoise.h" Color CloudShader::shade(const Scene &scene, const Ray &ray) const { Vector3d hitPoint = ray.origin + ray.direction * ray.length; // Potentially add epsilon // Collect getNoise through the cloud float cloudLength = 0.0f; // Length of cloud in ray direction // Get background color behind cloud and information about the clouds length Ray cloudRay = ray; cloudRay.origin = ray.origin + (ray.length + REFR_EPS) * ray.direction; cloudRay.length = INFINITY; cloudRay.primitive = nullptr; // Get out of cloud primitive first if (ray.primitive->intersect(cloudRay)) { // Get length cloudLength = cloudRay.length; // Prepare ray for background color cloudRay.setRemainingBounces(cloudRay.getRemainingBounces() + 1); cloudRay.origin = cloudRay.origin + (cloudRay.length + REFR_EPS) * cloudRay.direction; cloudRay.length = INFINITY; cloudRay.primitive = nullptr; } Color background = scene.traceRay(cloudRay); if (cloudLength == 0.0f) return background; // No cloud or at edge // Calculate step length int noiseSamples = settings.densitySamples; float stepLength = cloudLength / noiseSamples; // Step through cloud float transmittance = 1.0f; Color cloudColor = Color(1, 1, 1); for (int i = 0; i < noiseSamples; ++i) { // Get sample point Vector3d samplePoint = hitPoint + i * stepLength * ray.direction; // 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 // 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; } bool CloudShader::isTransparent() const { return true; } CloudShader::CloudShader(const CloudSettings &settings) : settings(settings), cloudNoise(CloudNoise(NOISE_SIZE)) { cloudNoise.invert = true; } float CloudShader::getCloudDensity(Vector3d point) const { point /= settings.scale; float density = cloudNoise.getNoise(point); // Treshold density = std::max(0.0f, density - settings.densityTreshold) * settings.densityIntensity; 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; }