Merge branch 'partial-occlusion-lighting'

This commit is contained in:
Maximilian Giller 2023-01-27 05:37:17 +01:00
commit c1bae8a43c
18 changed files with 353 additions and 128 deletions

View file

@ -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

View file

@ -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;

View file

@ -22,21 +22,32 @@
#include "shader/noiseshader.h"
#include "common/noise/cloudnoise.h"
#include "light/sunlight.h"
#include "scene/simplescene.h"
#include "shader/refractionshader.h"
int main()
{
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
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(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
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),
busShader);
// auto busShader = std::make_shared<ToneShader>(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<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
auto floorShader = std::make_shared<SimpleShadowShader>(Color(0.9f, 0.9f, 0.9f));
@ -46,11 +57,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<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
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);

View file

@ -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};
}

View file

@ -13,6 +13,7 @@ public:
struct Illumination {
Color color;
Vector3d direction;
float distance;
};
// Constructor / Destructor

View file

@ -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;
@ -20,8 +21,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;
}

View file

@ -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;
@ -28,7 +29,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);

View file

@ -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 surfa ce point to the light source.
Ray lightRay;
@ -23,9 +24,13 @@ 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
illum.color = this->color * this->intensity * angleIntensity;
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;
}
return illum;
}

View file

@ -221,3 +221,25 @@ std::unique_ptr<Node> 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())
{
Color t = i->shader()->transparency(*this, r, maxDistance);
transparency.r *= t.r;
transparency.g *= t.g;
transparency.b *= t.b;
}
}
return transparency;
}

View file

@ -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

View file

@ -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;

View file

@ -2,16 +2,38 @@
#include "primitive/primitive.h"
#include "shader/shader.h"
bool SimpleScene::findIntersection(Ray &ray) const {
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 {
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())
{
Color t = i->shader()->transparency(*this, r, maxDistance);
transparency.r *= t.r;
transparency.g *= t.g;
transparency.b *= t.b;
}
}
return transparency;
}

View file

@ -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

View file

@ -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
@ -34,35 +33,34 @@ 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.0f;
Color cloudColor = Color(1, 1, 1);
float accumulatedDensity = 0.0f;
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;
// cloudColor += lightMarch(scene, samplePoint, ray);
transmittance *= exp(-sampleDensity * stepLength * settings.densityAbsorption);
if (transmittance <= TRANSMITTANCE_BREAK) break; // No need to continue
if (sampleDensity > 0)
{
cloudColor += lightMarch(scene, samplePoint, lengthDirection, ray.primitive) * sampleDensity;
}
// 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;
// }
accumulatedDensity += sampleDensity;
}
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
@ -82,60 +80,147 @@ float CloudShader::getCloudDensity(Vector3d point) const
float density = cloudNoise.getNoise(point);
// Treshold
density = std::max(0.0f, density - settings.densityTreshold) * settings.densityIntensity;
// Threshold
// TODO: Smooth out!
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 Primitive *cloudObject) const
{
Color cloudColor;
Color cloudColor = Color(0, 0, 0);
// 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);
ray.primitive = cloudObject;
auto illumination = light->illuminate(scene, ray);
// 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)
// Handle ambient lights
if (illumination.distance == 0.0f)
{
// No cloud or at edge
cloudColor += illumination.color;
continue;
}
cloudLength = cloudRay.length;
// Calculate step length
int lightSamples = settings.lightSamples;
float stepLength = cloudLength / lightSamples;
// Light ray
Ray lightRay;
lightRay.origin = currentInCloudPosition;
lightRay.direction = illumination.direction;
lightRay.primitive = cloudObject;
lightRay.length = 0; // Starting in cloud itself
// Step through cloud
float transmittance = 0.0f;
for (int i = 0; i < lightSamples; ++i)
float density = this->rayDensity(lightRay, illumination.distance);
density *= settings.lightAbsorptionTowardsLight;
// 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;
float density = getCloudDensity(samplePoint) * stepLength;
transmittance *= exp(-density * settings.densityAbsorption);
if (transmittance <= TRANSMITTANCE_BREAK) break; // No need to continue
factor = settings.darknessThreshold +
(1.0f - settings.darknessThreshold) * factor * scatter;
}
// float lightAbsorption = dotProduct(lightDirection, ray.direction); // Approaches 1 when light is parallel to ray
cloudColor += transmittance * illumination.color;
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);
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));
}

View file

@ -8,18 +8,21 @@
#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
float 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 = 100;
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 densityOffset = -0.57f;
float densityIntensity = 7.0f; // 7.0f
float darknessThreshold = 0.1f; // 0.07f
float shadowIntensity = 0.6f;
float shadowLightAbsorption = 1;
float lightAbsorptionTowardsLight = 1.0f;
float lightAbsorptionThroughCloud = 0.5f;
float scatterWeight = 0.5f;
};
class CloudShader : public Shader
@ -29,6 +32,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;
@ -40,7 +44,15 @@ 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 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;
};

View file

@ -1,18 +1,27 @@
#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, 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
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
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) {
if (dotProduct(normalVector, ray.direction) > 0)
{
normalVector = -normalVector;
refractiveIndex = indexInside / indexOutside;
}
@ -30,22 +39,51 @@ Color RefractionShader::shade(Scene const &scene, Ray const &ray) const {
refractionRay.primitive = nullptr;
// 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.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;
// 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
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);
Color hitColor = scene.traceRay(refractionRay);
float lightRemaining = 1;
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;
}

View file

@ -3,19 +3,26 @@
#include "shader/shader.h"
class RefractionShader : public Shader {
class RefractionShader : public Shader
{
public:
// Constructor
RefractionShader(float indexInside, float indexOutside);
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;
bool isTransparent() const override;
Color transparency(const Scene &scene, const Ray &ray, float maxLength) const override;
private:
float indexInside;
float indexOutside;
float lightLoss;
Color objectColor;
float remainingLightIntensity(float distanceThroughObject) const;
};
#endif

View file

@ -7,14 +7,27 @@
// Forward declarations
class Scene;
class Shader {
class Shader
{
public:
// Constructor / Desctructor
Shader() = default;
virtual ~Shader() = default;
// 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
virtual Color shade(Scene const &scene, Ray const &ray) const = 0;