Merge branch 'partial-occlusion-lighting'
This commit is contained in:
commit
c1bae8a43c
18 changed files with 353 additions and 128 deletions
|
@ -4,12 +4,14 @@
|
||||||
|
|
||||||
CloudNoise::CloudNoise(int size) : Noise(size)
|
CloudNoise::CloudNoise(int size) : Noise(size)
|
||||||
{
|
{
|
||||||
|
int minSize = std::min(32, size);
|
||||||
|
|
||||||
// Some worley noises
|
// Some worley noises
|
||||||
WorleyNoise worleyNoise1(size, 3);
|
WorleyNoise worleyNoise1(minSize, 3);
|
||||||
WorleyNoise worleyNoise3(size, 15);
|
WorleyNoise worleyNoise3(size, 15);
|
||||||
|
|
||||||
// Some perlin noises
|
// Some perlin noises
|
||||||
PerlinNoise perlinNoise1(size, 3);
|
PerlinNoise perlinNoise1(minSize, 3);
|
||||||
PerlinNoise perlinNoise2(size, 15);
|
PerlinNoise perlinNoise2(size, 15);
|
||||||
|
|
||||||
// Generate the noise
|
// Generate the noise
|
||||||
|
|
|
@ -87,6 +87,8 @@ Noise::Noise(int size)
|
||||||
*/
|
*/
|
||||||
float Noise::interpolate(float a0, float a1, float w) const
|
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:
|
/* // You may want clamping by inserting:
|
||||||
* if (0.0 > w) return a0;
|
* if (0.0 > w) return a0;
|
||||||
* if (1.0 < w) return a1;
|
* if (1.0 < w) return a1;
|
||||||
|
|
30
fancy1.cpp
30
fancy1.cpp
|
@ -22,21 +22,32 @@
|
||||||
#include "shader/noiseshader.h"
|
#include "shader/noiseshader.h"
|
||||||
#include "common/noise/cloudnoise.h"
|
#include "common/noise/cloudnoise.h"
|
||||||
#include "light/sunlight.h"
|
#include "light/sunlight.h"
|
||||||
|
#include "scene/simplescene.h"
|
||||||
|
#include "shader/refractionshader.h"
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
FastScene scene;
|
FastScene scene;
|
||||||
scene.setBackgroundColor(Color(0.529f, 0.808f, 0.922f));
|
scene.setBackgroundColor(Color(0.529f, 0.808f, 0.922f) * 1.0f);
|
||||||
|
// scene.setBackgroundColor(Color(1, 0.79f, 0.62f) * 0.8f);
|
||||||
|
|
||||||
// Add lights
|
// Add lights
|
||||||
auto mainLight = std::make_shared<SunLight>(Vector3d(-1.0f, -0.5f, -1.0f), 10.0f);
|
auto mainLight = std::make_shared<SunLight>(Vector3d(-1.0f, -0.5f, -1.0f), 2.0f, Color(1,1,1));//Color(1, 0.79f, 0.62f));
|
||||||
scene.add(mainLight);
|
scene.add(mainLight);
|
||||||
scene.add(std::make_shared<AmbientLight>(0.3f));
|
// scene.add(std::make_shared<AmbientLight>(0.3f));
|
||||||
|
// auto light = std::make_shared<PointLight>(Vector3d(25.0f, 10.0f, 25.0f), 100.0f);
|
||||||
|
// scene.add(light);
|
||||||
|
|
||||||
// Add the bus
|
// Add the bus
|
||||||
auto busShader = std::make_shared<ToneShader>(mainLight);
|
// auto busShader = std::make_shared<ToneShader>(mainLight);
|
||||||
scene.addObj("data/Bus/source/T07-11M.obj", Vector3d(1.0f, 1.0f, 1.0f), Vector3d(10.0f, 0.0f, 10.0f),
|
// scene.addObj("data/Bus/source/T07-11M.obj", Vector3d(1.0f, 1.0f, 1.0f), Vector3d(15.0f, 0.0f, 10.0f),
|
||||||
busShader);
|
// busShader);
|
||||||
|
|
||||||
|
// Refraction boxes
|
||||||
|
// auto boxShader = std::make_shared<RefractionShader>(1.05f, 1, Color(1,1,0), 0.7f);
|
||||||
|
// scene.add(std::make_shared<Box>(Vector3d(5.0f, 3.0f, 10.0f), Vector3d(3.0f, 3.0f, 3.0f), boxShader));
|
||||||
|
// auto boxShader1 = std::make_shared<RefractionShader>(1.05f, 1, Color(0,1,1), 0.7f);
|
||||||
|
// scene.add(std::make_shared<Box>(Vector3d(9.0f, 3.0f, 12.0f), Vector3d(3.0f, 3.0f, 3.0f), boxShader1));
|
||||||
|
|
||||||
// Add floor
|
// Add floor
|
||||||
auto floorShader = std::make_shared<SimpleShadowShader>(Color(0.9f, 0.9f, 0.9f));
|
auto floorShader = std::make_shared<SimpleShadowShader>(Color(0.9f, 0.9f, 0.9f));
|
||||||
|
@ -46,11 +57,8 @@ int main()
|
||||||
// Add box for volume shader
|
// Add box for volume shader
|
||||||
auto cloudSettings = CloudSettings();
|
auto cloudSettings = CloudSettings();
|
||||||
cloudSettings.scale = 15.0f;
|
cloudSettings.scale = 15.0f;
|
||||||
cloudSettings.densityIntensity = 10.0f;
|
|
||||||
cloudSettings.densityTreshold = 0.49f;
|
|
||||||
cloudSettings.densityAbsorption = 0.9f;
|
|
||||||
auto cloudShader = std::make_shared<CloudShader>(cloudSettings);
|
auto cloudShader = std::make_shared<CloudShader>(cloudSettings);
|
||||||
scene.add(std::make_shared<Box>(Vector3d(5.0f, 8.0f, 5.0f), Vector3d(50.0f, 5.0f, 50.0f), cloudShader));
|
scene.add(std::make_shared<Box>(Vector3d(20.0f, 10.0f, 20.0f), Vector3d(50.0f, 10.0f, 50.0f), cloudShader));
|
||||||
|
|
||||||
// build the tree
|
// build the tree
|
||||||
scene.buildTree();
|
scene.buildTree();
|
||||||
|
@ -58,7 +66,7 @@ int main()
|
||||||
// Set up the camera
|
// Set up the camera
|
||||||
PerspectiveCamera camera;
|
PerspectiveCamera camera;
|
||||||
camera.setPosition(Vector3d(0.0f, 3.0f, 0.0f));
|
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.setUpDirection(normalized(Vector3d(0.0f, 1.0f, 0.0f)));
|
||||||
camera.setFovAngle(90.0f);
|
camera.setFovAngle(90.0f);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#include "light/ambientlight.h"
|
#include "light/ambientlight.h"
|
||||||
|
|
||||||
Light::Illumination AmbientLight::illuminate(Scene const &scene, Ray const &ray) const {
|
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};
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ public:
|
||||||
struct Illumination {
|
struct Illumination {
|
||||||
Color color;
|
Color color;
|
||||||
Vector3d direction;
|
Vector3d direction;
|
||||||
|
float distance;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constructor / Destructor
|
// Constructor / Destructor
|
||||||
|
|
|
@ -12,6 +12,7 @@ Light::Illumination PointLight::illuminate(Scene const &scene, Ray const &ray) c
|
||||||
|
|
||||||
// Precompute the distance from the light source
|
// Precompute the distance from the light source
|
||||||
float const distance = length(target - this->position);
|
float const distance = length(target - this->position);
|
||||||
|
illum.distance = distance;
|
||||||
|
|
||||||
// Define a secondary ray from the surface point to the light source.
|
// Define a secondary ray from the surface point to the light source.
|
||||||
Ray lightRay;
|
Ray lightRay;
|
||||||
|
@ -20,8 +21,10 @@ Light::Illumination PointLight::illuminate(Scene const &scene, Ray const &ray) c
|
||||||
lightRay.length = distance - LGT_EPS;
|
lightRay.length = distance - LGT_EPS;
|
||||||
|
|
||||||
// If the target is not in shadow...
|
// If the target is not in shadow...
|
||||||
if (!scene.findOcclusion(lightRay))
|
if (!scene.findOcclusion(lightRay)) {
|
||||||
// ... compute the attenuation and light color
|
// ... 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;
|
return illum;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ Light::Illumination SpotLight::illuminate(Scene const &scene, Ray const &ray) co
|
||||||
|
|
||||||
// Precompute the distance from the light source
|
// Precompute the distance from the light source
|
||||||
float const distance = length(target - this->position);
|
float const distance = length(target - this->position);
|
||||||
|
illum.distance = distance;
|
||||||
|
|
||||||
// Define a secondary ray from the surface point to the light source
|
// Define a secondary ray from the surface point to the light source
|
||||||
Ray lightRay;
|
Ray lightRay;
|
||||||
|
@ -28,7 +29,8 @@ Light::Illumination SpotLight::illuminate(Scene const &scene, Ray const &ray) co
|
||||||
// ... and not in shadow ...
|
// ... and not in shadow ...
|
||||||
if (!scene.findOcclusion(lightRay)) {
|
if (!scene.findOcclusion(lightRay)) {
|
||||||
// ... compute the attenuation and light color ...
|
// ... 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
|
// ... then compute the falloff towards the edge of the cone
|
||||||
if (this->alphaMin < alpha)
|
if (this->alphaMin < alpha)
|
||||||
illum.color *= 1.0f - (alpha - this->alphaMin) / (this->alphaMax - this->alphaMin);
|
illum.color *= 1.0f - (alpha - this->alphaMin) / (this->alphaMax - this->alphaMin);
|
||||||
|
|
|
@ -12,8 +12,9 @@ Light::Illumination SunLight::illuminate(Scene const &scene, Ray const &ray) con
|
||||||
// Illumination object
|
// Illumination object
|
||||||
Illumination illum;
|
Illumination illum;
|
||||||
illum.direction = this->direction;
|
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;
|
Ray lightRay;
|
||||||
lightRay.origin = target;
|
lightRay.origin = target;
|
||||||
lightRay.direction = -illum.direction;
|
lightRay.direction = -illum.direction;
|
||||||
|
@ -23,9 +24,13 @@ Light::Illumination SunLight::illuminate(Scene const &scene, Ray const &ray) con
|
||||||
if (!scene.findOcclusion(lightRay))
|
if (!scene.findOcclusion(lightRay))
|
||||||
{
|
{
|
||||||
// Look at angleIntensity of light
|
// Look at angleIntensity of light
|
||||||
float angleIntensity = dotProduct(-ray.normal,
|
float angleIntensity = 1;
|
||||||
this->direction); // 0 if light is behind surface, 1 if light is in front of surface
|
// Only if normal is relevant, calculate angleIntensity
|
||||||
illum.color = this->color * this->intensity * 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;
|
||||||
}
|
}
|
||||||
return illum;
|
return illum;
|
||||||
}
|
}
|
|
@ -221,3 +221,25 @@ std::unique_ptr<Node> FastScene::build(Vector3d const &minimumBounds, Vector3d c
|
||||||
node->child[1] = this->build(minimumSplit, maximumBounds, rightPrimitives, depth);
|
node->child[1] = this->build(minimumSplit, maximumBounds, rightPrimitives, depth);
|
||||||
return node;
|
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())
|
||||||
|
{
|
||||||
|
Color t = i->shader()->transparency(*this, r, maxDistance);
|
||||||
|
transparency.r *= t.r;
|
||||||
|
transparency.g *= t.g;
|
||||||
|
transparency.b *= t.b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return transparency;
|
||||||
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ public:
|
||||||
// Raytracing functions
|
// Raytracing functions
|
||||||
bool findIntersection(Ray &ray) const override;
|
bool findIntersection(Ray &ray) const override;
|
||||||
bool findOcclusion(Ray &ray) const override;
|
bool findOcclusion(Ray &ray) const override;
|
||||||
|
Color getTransparency(Ray &ray, float maxDistance) const override;
|
||||||
int countNodeIntersections(const Ray &ray) const;
|
int countNodeIntersections(const Ray &ray) const;
|
||||||
|
|
||||||
// Setup functions
|
// Setup functions
|
||||||
|
|
|
@ -41,6 +41,7 @@ public:
|
||||||
Color traceRay(Ray &ray) const;
|
Color traceRay(Ray &ray) const;
|
||||||
virtual bool findIntersection(Ray &ray) const = 0;
|
virtual bool findIntersection(Ray &ray) const = 0;
|
||||||
virtual bool findOcclusion(Ray &ray) const = 0;
|
virtual bool findOcclusion(Ray &ray) const = 0;
|
||||||
|
virtual Color getTransparency(Ray &ray, float maxDistance) const = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Color backgroundColor;
|
Color backgroundColor;
|
||||||
|
|
|
@ -2,16 +2,38 @@
|
||||||
#include "primitive/primitive.h"
|
#include "primitive/primitive.h"
|
||||||
#include "shader/shader.h"
|
#include "shader/shader.h"
|
||||||
|
|
||||||
bool SimpleScene::findIntersection(Ray &ray) const {
|
bool SimpleScene::findIntersection(Ray &ray) const
|
||||||
|
{
|
||||||
bool hit = false;
|
bool hit = false;
|
||||||
for (auto i : this->primitives())
|
for (auto i: this->primitives())
|
||||||
hit |= i->intersect(ray);
|
hit |= i->intersect(ray);
|
||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SimpleScene::findOcclusion(Ray &ray) const {
|
bool SimpleScene::findOcclusion(Ray &ray) const
|
||||||
for (auto i : this->primitives())
|
{
|
||||||
|
for (auto i: this->primitives())
|
||||||
if (i->intersect(ray) && !i->shader()->isTransparent())
|
if (i->intersect(ray) && !i->shader()->isTransparent())
|
||||||
return true;
|
return true;
|
||||||
return false;
|
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())
|
||||||
|
{
|
||||||
|
Color t = i->shader()->transparency(*this, r, maxDistance);
|
||||||
|
transparency.r *= t.r;
|
||||||
|
transparency.g *= t.g;
|
||||||
|
transparency.b *= t.b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return transparency;
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ public:
|
||||||
// Raytracing functions
|
// Raytracing functions
|
||||||
bool findIntersection(Ray &ray) const override;
|
bool findIntersection(Ray &ray) const override;
|
||||||
bool findOcclusion(Ray &ray) const override;
|
bool findOcclusion(Ray &ray) const override;
|
||||||
|
Color getTransparency(Ray &ray, float maxDistance) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#include "cloudshader.h"
|
#include "cloudshader.h"
|
||||||
#include "common/noise/cloudnoise.h"
|
#include "common/noise/cloudnoise.h"
|
||||||
#include "common/noise/perlinnoise.h"
|
|
||||||
|
|
||||||
|
|
||||||
Color CloudShader::shade(const Scene &scene, const Ray &ray) const
|
Color CloudShader::shade(const Scene &scene, const Ray &ray) const
|
||||||
|
@ -34,35 +33,34 @@ Color CloudShader::shade(const Scene &scene, const Ray &ray) const
|
||||||
|
|
||||||
// Calculate step length
|
// Calculate step length
|
||||||
int noiseSamples = settings.densitySamples;
|
int noiseSamples = settings.densitySamples;
|
||||||
float stepLength = cloudLength / noiseSamples;
|
float stepLength = cloudLength / (float) noiseSamples;
|
||||||
|
|
||||||
// Step through cloud
|
// Step through cloud
|
||||||
float transmittance = 1.0f;
|
float accumulatedDensity = 0.0f;
|
||||||
Color cloudColor = Color(1, 1, 1);
|
Color cloudColor = Color(0, 0, 0);
|
||||||
for (int i = 0; i < noiseSamples; ++i)
|
for (int i = 0; i < noiseSamples; ++i)
|
||||||
{
|
{
|
||||||
// Get sample point
|
// Get sample point
|
||||||
Vector3d samplePoint = hitPoint + i * stepLength * ray.direction;
|
Vector3d lengthDirection = i * stepLength * ray.direction;
|
||||||
|
Vector3d samplePoint = hitPoint + lengthDirection;
|
||||||
|
|
||||||
// Get data at point
|
// Get data at point
|
||||||
float sampleDensity = getCloudDensity(samplePoint) * stepLength;
|
float sampleDensity = getCloudDensity(samplePoint) * stepLength;
|
||||||
// cloudColor += lightMarch(scene, samplePoint, ray);
|
|
||||||
|
|
||||||
transmittance *= exp(-sampleDensity * stepLength * settings.densityAbsorption);
|
if (sampleDensity > 0)
|
||||||
if (transmittance <= TRANSMITTANCE_BREAK) break; // No need to continue
|
{
|
||||||
|
cloudColor += lightMarch(scene, samplePoint, lengthDirection, ray.primitive) * sampleDensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add some ambient and diffuse lighting
|
accumulatedDensity += sampleDensity;
|
||||||
// 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;
|
if (accumulatedDensity > 1)
|
||||||
|
cloudColor /= accumulatedDensity;
|
||||||
|
|
||||||
|
float transmittance = exp(-accumulatedDensity * settings.lightAbsorptionThroughCloud);
|
||||||
|
|
||||||
|
return background * transmittance + cloudColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CloudShader::isTransparent() const
|
bool CloudShader::isTransparent() const
|
||||||
|
@ -82,60 +80,147 @@ float CloudShader::getCloudDensity(Vector3d point) const
|
||||||
|
|
||||||
float density = cloudNoise.getNoise(point);
|
float density = cloudNoise.getNoise(point);
|
||||||
|
|
||||||
// Treshold
|
// Threshold
|
||||||
density = std::max(0.0f, density - settings.densityTreshold) * settings.densityIntensity;
|
// TODO: Smooth out!
|
||||||
|
density = std::max(0.0f, density + settings.densityOffset) * settings.densityIntensity;
|
||||||
|
|
||||||
return density;
|
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 Primitive *cloudObject) const
|
||||||
{
|
{
|
||||||
Color cloudColor;
|
Color cloudColor = Color(0, 0, 0);
|
||||||
|
|
||||||
// For alle lights
|
// For alle lights
|
||||||
for (const auto &light: scene.lights())
|
for (const auto &light: scene.lights())
|
||||||
{
|
{
|
||||||
auto illumination = light->illuminate(scene, position);
|
Ray ray = Ray(currentInCloudPosition - lengthDistance, normalized(lengthDistance));
|
||||||
|
ray.length = length(lengthDistance);
|
||||||
|
ray.primitive = cloudObject;
|
||||||
|
auto illumination = light->illuminate(scene, ray);
|
||||||
|
|
||||||
// Get light direction
|
// Handle ambient lights
|
||||||
Vector3d lightDirection = normalized(illumination.direction); // Points from surface to light
|
if (illumination.distance == 0.0f)
|
||||||
|
|
||||||
// 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
|
cloudColor += illumination.color;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
cloudLength = cloudRay.length;
|
|
||||||
|
|
||||||
// Calculate step length
|
// Light ray
|
||||||
int lightSamples = settings.lightSamples;
|
Ray lightRay;
|
||||||
float stepLength = cloudLength / lightSamples;
|
lightRay.origin = currentInCloudPosition;
|
||||||
|
lightRay.direction = illumination.direction;
|
||||||
|
lightRay.primitive = cloudObject;
|
||||||
|
lightRay.length = 0; // Starting in cloud itself
|
||||||
|
|
||||||
// Step through cloud
|
float density = this->rayDensity(lightRay, illumination.distance);
|
||||||
float transmittance = 0.0f;
|
density *= settings.lightAbsorptionTowardsLight;
|
||||||
for (int i = 0; i < lightSamples; ++i)
|
|
||||||
|
// Proper light calculation
|
||||||
|
float transmittance = getDensityTransmittance(density);
|
||||||
|
float scatter = scatterFactor(normalized(lengthDistance), illumination.direction);
|
||||||
|
|
||||||
|
float factor = transmittance;
|
||||||
|
if (density > 0)
|
||||||
{
|
{
|
||||||
Vector3d samplePoint = position + i * stepLength * lightDirection;
|
factor = settings.darknessThreshold +
|
||||||
float density = getCloudDensity(samplePoint) * stepLength;
|
(1.0f - settings.darknessThreshold) * factor * scatter;
|
||||||
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 += factor * illumination.color;
|
||||||
|
|
||||||
cloudColor += transmittance * illumination.color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cloudColor;
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Determine length of cloud
|
||||||
|
Ray cloudRay = ray;
|
||||||
|
cloudRay.origin = startPoint;
|
||||||
|
cloudRay.length = INFINITY;
|
||||||
|
cloudRay.primitive = nullptr;
|
||||||
|
|
||||||
|
// Get out of cloud primitive first
|
||||||
|
if (ray.primitive != nullptr && !ray.primitive->intersect(cloudRay) || cloudRay.length == INFINITY ||
|
||||||
|
cloudRay.length <= 0)
|
||||||
|
{
|
||||||
|
// Something went wrong => No density
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
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 = 0.0f;
|
||||||
|
for (int i = 0; i < noiseSamples; ++i)
|
||||||
|
{
|
||||||
|
// Get sample point
|
||||||
|
Vector3d samplePoint = startPoint + i * stepLength * ray.direction;
|
||||||
|
|
||||||
|
// Get data at point
|
||||||
|
density += getCloudDensity(samplePoint) * stepLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
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 = 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;
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
|
|
@ -8,18 +8,21 @@
|
||||||
#include "common/noise/worleynoise.h"
|
#include "common/noise/worleynoise.h"
|
||||||
|
|
||||||
int const NOISE_SIZE = 128;
|
int const NOISE_SIZE = 128;
|
||||||
int const TRANSMITTANCE_BREAK = 0.01f; // 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
|
struct CloudSettings
|
||||||
{
|
{
|
||||||
int densitySamples = 100;
|
int densitySamples = 100;
|
||||||
int lightSamples = 25;
|
int lightSamples = 100;
|
||||||
float scale = 10;
|
float scale = 10;
|
||||||
float densityTreshold = 0.55f;
|
float densityOffset = -0.57f;
|
||||||
float densityIntensity = 2.5f;
|
float densityIntensity = 7.0f; // 7.0f
|
||||||
float densityAbsorption = 2;
|
float darknessThreshold = 0.1f; // 0.07f
|
||||||
Color cloudColor = Color(1, 1, 1);
|
float shadowIntensity = 0.6f;
|
||||||
float darknessThreshold = 0.1f;
|
float shadowLightAbsorption = 1;
|
||||||
|
float lightAbsorptionTowardsLight = 1.0f;
|
||||||
|
float lightAbsorptionThroughCloud = 0.5f;
|
||||||
|
float scatterWeight = 0.5f;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CloudShader : public Shader
|
class CloudShader : public Shader
|
||||||
|
@ -29,6 +32,7 @@ public:
|
||||||
|
|
||||||
// Shader functions
|
// Shader functions
|
||||||
Color shade(Scene const &scene, Ray const &ray) const;
|
Color shade(Scene const &scene, Ray const &ray) const;
|
||||||
|
Color transparency(const Scene &scene, const Ray &ray, float maxLength) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CloudSettings settings;
|
CloudSettings settings;
|
||||||
|
@ -40,7 +44,15 @@ private:
|
||||||
|
|
||||||
float getCloudDensity(Vector3d point) const;
|
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 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,27 @@
|
||||||
#include "scene/scene.h"
|
#include "scene/scene.h"
|
||||||
#include "shader/refractionshader.h"
|
#include "shader/refractionshader.h"
|
||||||
|
|
||||||
RefractionShader::RefractionShader(float indexInside, float indexOutside) : indexInside(indexInside), indexOutside(indexOutside) {}
|
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 {
|
Color RefractionShader::shade(Scene const &scene, Ray const &ray) const
|
||||||
|
{
|
||||||
// Circumvent getting environment map color into the mix
|
// Circumvent getting environment map color into the mix
|
||||||
if (ray.getRemainingBounces() > 0) {
|
if (ray.getRemainingBounces() <= 0)
|
||||||
|
{
|
||||||
|
return Color(0.0f, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
// Get the normal of the primitive which was hit
|
// Get the normal of the primitive which was hit
|
||||||
Vector3d normalVector = ray.normal;
|
Vector3d normalVector = ray.normal;
|
||||||
|
|
||||||
// Calculate the index of refraction
|
// Calculate the index of refraction
|
||||||
float refractiveIndex = indexOutside / indexInside;
|
float refractiveIndex = indexOutside / indexInside;
|
||||||
// What if we are already inside the object?
|
// What if we are already inside the object?
|
||||||
if (dotProduct(normalVector, ray.direction) > 0) {
|
if (dotProduct(normalVector, ray.direction) > 0)
|
||||||
|
{
|
||||||
normalVector = -normalVector;
|
normalVector = -normalVector;
|
||||||
refractiveIndex = indexInside / indexOutside;
|
refractiveIndex = indexInside / indexOutside;
|
||||||
}
|
}
|
||||||
|
@ -30,22 +39,51 @@ Color RefractionShader::shade(Scene const &scene, Ray const &ray) const {
|
||||||
refractionRay.primitive = nullptr;
|
refractionRay.primitive = nullptr;
|
||||||
|
|
||||||
// Check whether it is a refraction.
|
// Check whether it is a refraction.
|
||||||
if (dotProduct(t, normalVector) <= 0.0) {
|
if (dotProduct(t, normalVector) <= 0.0)
|
||||||
|
{
|
||||||
refractionRay.origin = ray.origin + (ray.length + REFR_EPS) * ray.direction;
|
refractionRay.origin = ray.origin + (ray.length + REFR_EPS) * ray.direction;
|
||||||
refractionRay.direction = normalized(t);
|
refractionRay.direction = normalized(t);
|
||||||
} else { // Otherwise, it is a total reflection.
|
} else
|
||||||
|
{ // Otherwise, it is a total reflection.
|
||||||
refractionRay.origin = ray.origin + (ray.length - REFR_EPS) * ray.direction;
|
refractionRay.origin = ray.origin + (ray.length - REFR_EPS) * ray.direction;
|
||||||
// Next we get the reflection vector
|
// Next we get the reflection vector
|
||||||
Vector3d const reflectionVector = ray.direction - 2.0f * dotProduct(normalVector, ray.direction) * normalVector;
|
Vector3d const reflectionVector =
|
||||||
|
ray.direction - 2.0f * dotProduct(normalVector, ray.direction) * normalVector;
|
||||||
|
|
||||||
// Change the ray direction and origin
|
// Change the ray direction and origin
|
||||||
refractionRay.direction = normalized(reflectionVector);
|
refractionRay.direction = normalized(reflectionVector);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send out a new refracted ray into the scene
|
// Send out a new refracted ray into the scene
|
||||||
return scene.traceRay(refractionRay);
|
Color hitColor = scene.traceRay(refractionRay);
|
||||||
}
|
float lightRemaining = 1;
|
||||||
return Color(0.0f, 0.0f, 0.0f);
|
if (ray.primitive == refractionRay.primitive) lightRemaining = remainingLightIntensity(refractionRay.length);
|
||||||
|
|
||||||
|
return hitColor * objectColor * lightRemaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RefractionShader::isTransparent() const { return true; }
|
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, float maxLength) 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(maxLength - ray.length, lengthRay.length);
|
||||||
|
float lightRemaining = 1;
|
||||||
|
if (ray.primitive == lengthRay.primitive) lightRemaining = remainingLightIntensity(transparencyDistance);
|
||||||
|
|
||||||
|
return objectColor * lightRemaining;
|
||||||
|
}
|
||||||
|
|
|
@ -3,19 +3,26 @@
|
||||||
|
|
||||||
#include "shader/shader.h"
|
#include "shader/shader.h"
|
||||||
|
|
||||||
class RefractionShader : public Shader {
|
class RefractionShader : public Shader
|
||||||
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructor
|
||||||
RefractionShader(float indexInside, float indexOutside);
|
RefractionShader(float indexInside, float indexOutside, Color const &objectColor = Color(1, 1, 1), float lightLoss = 0);
|
||||||
|
|
||||||
// Shader functions
|
// Shader functions
|
||||||
Color shade(Scene const &scene, Ray const &ray) const override;
|
Color shade(Scene const &scene, Ray const &ray) const override;
|
||||||
|
|
||||||
bool isTransparent() const override;
|
bool isTransparent() const override;
|
||||||
|
Color transparency(const Scene &scene, const Ray &ray, float maxLength) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float indexInside;
|
float indexInside;
|
||||||
float indexOutside;
|
float indexOutside;
|
||||||
|
float lightLoss;
|
||||||
|
Color objectColor;
|
||||||
|
|
||||||
|
float remainingLightIntensity(float distanceThroughObject) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -7,14 +7,27 @@
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
class Scene;
|
class Scene;
|
||||||
|
|
||||||
class Shader {
|
class Shader
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
// Constructor / Desctructor
|
// Constructor / Desctructor
|
||||||
Shader() = default;
|
Shader() = default;
|
||||||
|
|
||||||
virtual ~Shader() = default;
|
virtual ~Shader() = default;
|
||||||
|
|
||||||
// Get
|
// Get
|
||||||
virtual bool isTransparent() const { return false; }
|
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 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(const Scene &scene, const Ray &ray, float maxLength) const
|
||||||
|
{ return isTransparent() ? Color(1, 1, 1) : Color(0, 0, 0); }
|
||||||
|
|
||||||
// Shader functions
|
// Shader functions
|
||||||
virtual Color shade(Scene const &scene, Ray const &ray) const = 0;
|
virtual Color shade(Scene const &scene, Ray const &ray) const = 0;
|
||||||
|
|
Loading…
Reference in a new issue