cloudy-raytracer/shader/materialshader.cpp

116 lines
4.2 KiB
C++
Raw Normal View History

2022-12-10 18:26:31 +01:00
#include "light/light.h"
#include "scene/scene.h"
#include "shader/materialshader.h"
#include <cmath>
2022-12-12 20:24:21 +01:00
#include <iostream>
2022-12-10 18:26:31 +01:00
2022-12-12 20:24:21 +01:00
Vector3d
tangentToWorldSpace(const Vector3d &surfaceNormal, const Vector3d &surfaceTangent, const Vector3d &surfaceBitangent,
const Vector3d &textureNormal)
{
2022-12-12 20:24:21 +01:00
return textureNormal.x * surfaceTangent + textureNormal.y * surfaceBitangent + textureNormal.z * surfaceNormal;
2022-12-10 18:26:31 +01:00
}
2022-12-12 20:24:21 +01:00
MaterialShader::MaterialShader() : opacity(1.0f), normalCoefficient(1.0f), diffuseCoefficient(0.5f), reflectance(0.0f),
specularCoefficient(0.5f), shininessExponent(8)
{}
2022-12-10 18:26:31 +01:00
Color MaterialShader::shade(Scene const &scene, Ray const &ray) const
{
2022-12-12 20:24:21 +01:00
Color fragmentColor;
// IMPLEMENT ME
2022-12-10 18:26:31 +01:00
2022-12-12 20:24:21 +01:00
// (Normal Map) Calculate the new normal vector
Vector3d surfaceNormal = ray.normal;
if (this->normalMap != nullptr)
{
2022-12-12 20:24:21 +01:00
auto surfaceNormalMapColor = this->normalMap->color(ray.surface, true);
Vector3d textureNormal = {surfaceNormalMapColor.r, surfaceNormalMapColor.g, surfaceNormalMapColor.b};
textureNormal = textureNormal * 2.0f - Vector3d{1, 1, 1};
surfaceNormal = ray.normal * normalCoefficient +
(1 - normalCoefficient) *
tangentToWorldSpace(ray.normal, ray.tangent, ray.bitangent, textureNormal);
}
2022-12-10 18:26:31 +01:00
2022-12-12 20:24:21 +01:00
// (Diffuse-/Specular Map) Accumulate the light over all light sources
Color surfaceDiffuseColor(0, 0, 0);
if (this->diffuseMap != nullptr)
{
2022-12-12 20:24:21 +01:00
surfaceDiffuseColor = this->diffuseMap->color(ray.surface, true);
}
2022-12-10 18:26:31 +01:00
2022-12-12 20:24:21 +01:00
Color surfaceSpecularColor(0, 0, 0);
if (this->specularMap != nullptr)
{
2022-12-13 00:16:19 +01:00
surfaceSpecularColor = this->specularMap->color(ray.surface, true);
2022-12-12 20:24:21 +01:00
}
2022-12-10 18:26:31 +01:00
2022-12-12 20:24:21 +01:00
// (Reflection Map) Calculate the reflectance, create a reflection ray
Vector3d const reflection = ray.direction - 2 * dotProduct(surfaceNormal, ray.direction) * ray.normal;
float surfaceReflectanceCoefficient = this->reflectance;
if (this->reflectionMap != nullptr)
{
2022-12-12 20:24:21 +01:00
auto surfaceReflectiveMapColor = this->reflectionMap->color(ray.surface, true);
surfaceReflectanceCoefficient = surfaceReflectiveMapColor.r;
}
Ray reflectionRay = ray;
reflectionRay.origin = ray.origin + (ray.length - REFR_EPS) * ray.direction;
reflectionRay.direction = normalized(reflection);
reflectionRay.length = INFINITY;
reflectionRay.primitive = nullptr;
2022-12-10 18:26:31 +01:00
2022-12-12 20:24:21 +01:00
// (Alpha Map) Calculate the opacity, create a background ray
float surfaceAlphaCoefficient(1);
if (this->alphaMap != nullptr)
{
2022-12-12 20:24:21 +01:00
auto surfaceAlphaMapColor = this->alphaMap->color(ray.surface, true);
surfaceAlphaCoefficient = surfaceAlphaMapColor.r;
}
Ray propagatedRay = ray;
propagatedRay.origin = ray.origin + (ray.length + REFR_EPS) * ray.direction;
propagatedRay.length = INFINITY;
propagatedRay.primitive = nullptr;
// Iterate over light sources
for (const auto &light: scene.lights())
{
2022-12-12 20:24:21 +01:00
Light::Illumination const illum = light->illuminate(scene, ray);
// Diffuse term (lambertian)
Color const diffuse = this->diffuseCoefficient * surfaceDiffuseColor *
std::max(dotProduct(-illum.direction, ray.normal), 0.0f);
fragmentColor += diffuse * illum.color;
// Specular term (phong)
float const cosine = dotProduct(-illum.direction, reflection);
if (cosine > 0)
{
2022-12-12 20:24:21 +01:00
Color const specular = this->specularCoefficient * surfaceSpecularColor // highlight
* powf(cosine, this->shininessExponent); // shininess factor
fragmentColor += specular * illum.color;
}
}
2022-12-13 00:16:19 +01:00
// Reflected ray
if (surfaceReflectanceCoefficient > 0)
{
Color const reflectionColor = scene.traceRay(reflectionRay);
fragmentColor += surfaceReflectanceCoefficient * reflectionColor * reflectance;
}
2022-12-13 00:16:19 +01:00
2022-12-12 20:24:21 +01:00
// Opacity
if (surfaceAlphaCoefficient < 1)
{
Color const background = scene.traceRay(propagatedRay);
fragmentColor = (1 - surfaceAlphaCoefficient) * background + surfaceAlphaCoefficient * fragmentColor;
}
return fragmentColor;
2022-12-10 18:26:31 +01:00
}
bool MaterialShader::isTransparent() const
{ return this->opacity < 1.0f || this->alphaMap; }