#include "light/light.h" #include "scene/scene.h" #include "shader/materialshader.h" #include #include Vector3d tangentToWorldSpace(const Vector3d &surfaceNormal, const Vector3d &surfaceTangent, const Vector3d &surfaceBitangent, const Vector3d &textureNormal) { return textureNormal.x * surfaceTangent + textureNormal.y * surfaceBitangent + textureNormal.z * surfaceNormal; } MaterialShader::MaterialShader() : opacity(1.0f), normalCoefficient(1.0f), diffuseCoefficient(0.5f), reflectance(0.0f), specularCoefficient(0.5f), shininessExponent(8) {} Color MaterialShader::shade(Scene const &scene, Ray const &ray) const { Color fragmentColor; // IMPLEMENT ME // (Normal Map) Calculate the new normal vector Vector3d surfaceNormal = ray.normal; if (this->normalMap != nullptr) { 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); } // (Diffuse-/Specular Map) Accumulate the light over all light sources Color surfaceDiffuseColor(0, 0, 0); if (this->diffuseMap != nullptr) { surfaceDiffuseColor = this->diffuseMap->color(ray.surface, true); } Color surfaceSpecularColor(0, 0, 0); if (this->specularMap != nullptr) { surfaceSpecularColor = this->specularMap->color(ray.surface, true); } // (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) { 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; // (Alpha Map) Calculate the opacity, create a background ray float surfaceAlphaCoefficient(1); if (this->alphaMap != nullptr) { 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()) { 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) { Color const specular = this->specularCoefficient * surfaceSpecularColor // highlight * powf(cosine, this->shininessExponent); // shininess factor fragmentColor += specular * illum.color; } } // Reflected ray if (surfaceReflectanceCoefficient > 0) { Color const reflectionColor = scene.traceRay(reflectionRay); fragmentColor += surfaceReflectanceCoefficient * reflectionColor * reflectance; } // Opacity if (surfaceAlphaCoefficient < 1) { Color const background = scene.traceRay(propagatedRay); fragmentColor = (1 - surfaceAlphaCoefficient) * background + surfaceAlphaCoefficient * fragmentColor; } return fragmentColor; } bool MaterialShader::isTransparent() const { return this->opacity < 1.0f || this->alphaMap; }