153 lines
4.6 KiB
C++
153 lines
4.6 KiB
C++
#include "cloudshader.h"
|
|
#include "common/noise/cloudnoise.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;
|
|
|
|
if (sampleDensity > REFR_EPS) {
|
|
cloudColor += lightMarch(scene, samplePoint, ray);
|
|
}
|
|
|
|
transmittance *= exp(-sampleDensity * stepLength * settings.densityAbsorption);
|
|
if (transmittance <= TRANSMITTANCE_BREAK) break; // No need to continue
|
|
}
|
|
|
|
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);
|
|
|
|
// Threshold
|
|
// TODO: Smooth out!
|
|
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);
|
|
|
|
// Handle ambient lights
|
|
if (illumination.distance == 0.0f) {
|
|
cloudColor += illumination.color;
|
|
continue;
|
|
}
|
|
|
|
// Light ray
|
|
Ray lightRay;
|
|
lightRay.origin = position;
|
|
lightRay.direction = illumination.direction;
|
|
lightRay.length = 0; // Starting in cloud itself
|
|
|
|
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;
|
|
}
|