#include "scene/scene.h" #include "shader/refractionshader.h" 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 { // Circumvent getting environment map color into the mix 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) { normalVector = -normalVector; refractiveIndex = indexInside / indexOutside; } // Using the notation from the lecture float cosineTheta = dotProduct(normalVector, -ray.direction); float cosinePhi = std::sqrt(1 + refractiveIndex * refractiveIndex * (cosineTheta * cosineTheta - 1)); // Calculate t, the new ray direction Vector3d t = refractiveIndex * ray.direction + (refractiveIndex * cosineTheta - cosinePhi) * normalVector; // Create the refraction ray Ray refractionRay = ray; // Reset the ray refractionRay.length = INFINITY; refractionRay.primitive = nullptr; // Check whether it is a refraction. 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. 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; // Change the ray direction and origin refractionRay.direction = normalized(reflectionVector); } // Send out a new refracted ray into the scene Color hitColor = scene.traceRay(refractionRay); float lightRemaining = 1; if (ray.primitive == refractionRay.primitive) lightRemaining = remainingLightIntensity(refractionRay.length); return hitColor * objectColor * lightRemaining; } 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; }