From 8b8a53dde67851ba4c6f15bd04cb8ba6ecc2c3f1 Mon Sep 17 00:00:00 2001 From: jp Date: Fri, 18 Nov 2022 11:43:53 +0100 Subject: [PATCH] added ex02 solution --- light/pointlight.cpp | 26 +++++++------ primitive/box.cpp | 69 ++++++++++++++++++++++++++++++++--- primitive/infiniteplane.cpp | 2 +- primitive/sphere.cpp | 3 +- primitive/triangle.cpp | 2 +- shader/mirrorshader.cpp | 11 +++++- shader/refractionshader.cpp | 48 +++++++++++++++++++++--- shader/simpleshadowshader.cpp | 11 ++++-- 8 files changed, 141 insertions(+), 31 deletions(-) diff --git a/light/pointlight.cpp b/light/pointlight.cpp index b443f85..95c6549 100644 --- a/light/pointlight.cpp +++ b/light/pointlight.cpp @@ -4,20 +4,24 @@ PointLight::PointLight(Vector3d const &position, float intensity, Color const &color) : Light(intensity, color), position(position) {} Light::Illumination PointLight::illuminate(Scene const &scene, Ray const &ray) const { - // IMPLEMENT ME - // Get the point on the surface + Vector3d const target = ray.origin + (ray.length - LGT_EPS) * ray.direction; - // Create an instance of the Illumination object, fill in the direction (from - // surface point to light source) - Light::Illumination illum; + // Illumination object + Illumination illum; + illum.direction = normalized(target - this->position); + + // Precompute the distance from the light source + float const distance = length(target - this->position); // Define a secondary ray from the surface point to the light source. + Ray lightRay; + lightRay.origin = target; + lightRay.direction = -illum.direction; + lightRay.length = distance - LGT_EPS; - // If the target is not in shadow... (use scene.findOcclusion()) - - // Compute the brightness-color of this light using inverse squared distance - // to light source (i.e. 1/(d^2)), the color of this light source and the - // intensity - + // If the target is not in shadow... + if (!scene.findOcclusion(lightRay)) + // ... compute the attenuation and light color + illum.color = 1.0f / (distance * distance) * this->color * this->intensity; return illum; } diff --git a/primitive/box.cpp b/primitive/box.cpp index 83a2c75..53e626f 100644 --- a/primitive/box.cpp +++ b/primitive/box.cpp @@ -13,19 +13,78 @@ Box::Box(Vector3d const ¢er, Vector3d const &size, std::shared_ptr c // Primitive functions ///////////////////////////////////////////////////////// bool Box::intersect(Ray &ray) const { - // IMPLEMENT ME! + // Project the ray onto the box + Vector3d const minBounds = this->center - this->size / 2; + Vector3d const maxBounds = this->center + this->size / 2; + Vector3d t1 = componentQuotient(minBounds - ray.origin, ray.direction); + Vector3d t2 = componentQuotient(maxBounds - ray.origin, ray.direction); - // Determine whether the ray intersects the box + // Determine the intersection points (tNear, tFar) + // We also have to remember the intersection axes (tNearIndex, tFarIndex) + float tNear = -INFINITY; + float tFar = +INFINITY; + int tNearIndex = 0; + int tFarIndex = 0; + for (int d = 0; d < 3; ++d) { + + // Test the trivial case (and to avoid division by zero errors) + if (ray.direction[d] == 0 && (ray.origin[d] < minBounds[d] || ray.origin[d] > maxBounds[d])) + return false; + + // Swap the bounds if necessary + if (t1[d] > t2[d]) + std::swap(t1[d], t2[d]); + + // Check for the near intersection + if (t1[d] > tNear) { + tNear = t1[d]; + tNearIndex = d; + } + + // Check for the far intersection + if (t2[d] < tFar) { + tFar = t2[d]; + tFarIndex = d; + } + + // Check whether we missed the box completely + if (tFar < 0 || tNear > tFar) + return false; + } + + // Check whether we are on the outside or on the inside of the box + float const t = (tNear >= 0 ? tNear : tFar); + int const tIndex = tNear >= 0 ? tNearIndex : tFarIndex; // Test whether this is the foremost primitive in front of the camera + if (ray.length < t) + return false; - // (Optional for now) Calculate the normal + // Calculate the normal + ray.normal = Vector3d(0, 0, 0); + // Flip the normal if we are on the inside + ray.normal[tIndex] = std::copysignf(1.0f, ray.direction[tIndex]) * (tNear < 0.0f ? +1.0f : -1.0f); - // (Optional for now) Calculate the surface position + // Calculate the surface position and tangent vector + Vector3d const target = ray.origin + t * ray.direction; + Vector3d const surface = componentQuotient(target - minBounds, maxBounds - minBounds); + if (tIndex == 0) { + ray.surface = Vector2d(surface[2], surface[1]); + ray.tangent = Vector3d(0, 0, 1); + } else if (tIndex == 1) { + ray.surface = Vector2d(surface[0], surface[2]); + ray.tangent = Vector3d(1, 0, 0); + } else { + ray.surface = Vector2d(surface[0], surface[1]); + ray.tangent = Vector3d(1, 0, 0); + } // Set the new length and the current primitive + ray.length = t; + ray.primitive = this; - return false; + // True, because the primitive was hit + return true; } // Bounding box //////////////////////////////////////////////////////////////// diff --git a/primitive/infiniteplane.cpp b/primitive/infiniteplane.cpp index cc02f31..7a9bb42 100644 --- a/primitive/infiniteplane.cpp +++ b/primitive/infiniteplane.cpp @@ -27,7 +27,7 @@ bool InfinitePlane::intersect(Ray &ray) const { return false; // Set the normal - // IMPLEMENT ME + ray.normal = this->normal; // Set the new length and the current primitive ray.length = t; diff --git a/primitive/sphere.cpp b/primitive/sphere.cpp index fc4e76e..54cce80 100644 --- a/primitive/sphere.cpp +++ b/primitive/sphere.cpp @@ -36,7 +36,8 @@ bool Sphere::intersect(Ray &ray) const { return false; // Calculate the normal - // IMPLEMENT ME + Vector3d const hitPoint = ray.origin + t * ray.direction; + ray.normal = normalized(hitPoint - this->center); // Calculate the surface position and tangent vector float const phi = std::acos(ray.normal.y); diff --git a/primitive/triangle.cpp b/primitive/triangle.cpp index ae98206..b98c539 100644 --- a/primitive/triangle.cpp +++ b/primitive/triangle.cpp @@ -100,7 +100,7 @@ bool Triangle::intersect(Ray &ray) const { return false; // Calculate the normal - // IMPLEMENT ME + ray.normal = normalized(crossProduct(edge1, edge2)); // Calculate the surface position ray.surface = u * this->surface[1] + v * this->surface[2] + (1 - u - v) * this->surface[0]; diff --git a/shader/mirrorshader.cpp b/shader/mirrorshader.cpp index 201a532..83703b7 100644 --- a/shader/mirrorshader.cpp +++ b/shader/mirrorshader.cpp @@ -4,9 +4,16 @@ MirrorShader::MirrorShader() {} Color MirrorShader::shade(Scene const &scene, Ray const &ray) const { - // IMPLEMENT ME // Calculate the reflection vector + Vector3d const reflection = ray.direction - 2 * dotProduct(ray.normal, ray.direction) * ray.normal; + // Create a new reflection ray + Ray reflectionRay = ray; + reflectionRay.origin = ray.origin + (ray.length - REFR_EPS) * ray.direction; + reflectionRay.direction = normalized(reflection); + reflectionRay.length = INFINITY; + reflectionRay.primitive = nullptr; + // Send the new ray out into the scene and return the result - return Color(0, 0, 1); + return scene.traceRay(reflectionRay); } diff --git a/shader/refractionshader.cpp b/shader/refractionshader.cpp index 1dacc1b..80d2124 100644 --- a/shader/refractionshader.cpp +++ b/shader/refractionshader.cpp @@ -4,12 +4,48 @@ RefractionShader::RefractionShader(float indexInside, float indexOutside) : indexInside(indexInside), indexOutside(indexOutside) {} Color RefractionShader::shade(Scene const &scene, Ray const &ray) const { - // IMPLEMENT ME - // Calculate the refracted ray using the surface normal vector and - // indexInside, indexOutside - // Also check for total internal reflection - // Send out a new refracted ray into the scene; recursively call traceRay() - return Color(1, 0, 0); + // Circumvent getting environment map color into the mix + if (ray.getRemainingBounces() > 0) { + // 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 + return scene.traceRay(refractionRay); + } + return Color(0.0f, 0.0f, 0.0f); } bool RefractionShader::isTransparent() const { return true; } diff --git a/shader/simpleshadowshader.cpp b/shader/simpleshadowshader.cpp index ee456c5..74e5ff7 100644 --- a/shader/simpleshadowshader.cpp +++ b/shader/simpleshadowshader.cpp @@ -5,8 +5,11 @@ SimpleShadowShader::SimpleShadowShader(Color const &objectColor) : objectColor(objectColor) {} Color SimpleShadowShader::shade(Scene const &scene, Ray const &ray) const { - // IMPLEMENT ME - // loop over all light sources to check for visibility and multiply "light - // strength" with this objects albedo (color) - return Color(0, 1, 0); + Color fragmentColor; + + // Accumulate the light over all light sources + for (const auto &light : scene.lights()) + fragmentColor += light->illuminate(scene, ray).color; + + return fragmentColor * this->objectColor; }