cloudy-raytracer/primitive/box.cpp

97 lines
3 KiB
C++

#include "common/ray.h"
#include "primitive/box.h"
#include <cmath>
#include <utility>
// Constructor /////////////////////////////////////////////////////////////////
Box::Box(std::shared_ptr<Shader> const &shader) : Primitive(shader), size(Vector3d(1, 1, 1)) {}
Box::Box(Vector3d const &center, Vector3d const &size, std::shared_ptr<Shader> const &shader)
: Primitive(shader), center(center), size(size) {}
// Helper functions /////////////////////////////////////////////////////////
float intersectionParameterDimension(Vector3d bounds, Ray &ray, int dimension)
{
return (bounds[dimension] - ray.origin[dimension]) / ray.direction[dimension];
}
Vector3d intersectionParameter(Vector3d bounds, Ray &ray)
{
return Vector3d(intersectionParameterDimension(bounds, ray, 0),
intersectionParameterDimension(bounds, ray, 1),
intersectionParameterDimension(bounds, ray, 2));
}
// Primitive functions /////////////////////////////////////////////////////////
bool Box::intersect(Ray &ray) const
{
// IMPLEMENT ME!
// Determine whether the ray intersects the box
Vector3d minBounds(this->minimumBounds(0), this->minimumBounds(1), this->minimumBounds(2));
Vector3d maxBounds(this->maximumBounds(0), this->maximumBounds(1), this->maximumBounds(2));
Vector3d tMin = intersectionParameter(minBounds, ray);
Vector3d tMax = intersectionParameter(maxBounds, ray);
Vector3d tInAll = Vector3d(std::min(tMin[0], tMax[0]), std::min(tMin[1], tMax[1]), std::min(tMin[2], tMax[2]));
Vector3d tOutAll = Vector3d(std::max(tMin[0], tMax[0]), std::max(tMin[1], tMax[1]), std::max(tMin[2], tMax[2]));
float tIn = std::max(std::max(tInAll[0], tInAll[1]), tInAll[2]);
float tOut = std::min(std::min(tOutAll[0], tOutAll[1]), tOutAll[2]);
if (tIn > tOut || tOut < 0)
{
return false;
}
float t = tIn;
// Test whether this is the foremost primitive in front of the camera
if (t >= ray.length)
{
return false;
}
// (Optional for now) Calculate the normal
// On what side of the box did the ray hit?
Vector3d normal;
if (tInAll[0] == tIn)
{
normal = Vector3d(1, 0, 0);
}
else if (tInAll[1] == tIn)
{
normal = Vector3d(0, 1, 0);
}
else // tInAll[2] == tIn
{
normal = Vector3d(0, 0, 1);
}
// Make sure sign is correct
if (dotProduct(ray.direction, normal) > 0)
{
normal = -normal;
}
ray.normal = normalized(normal);
// (Optional for now) Calculate the surface position
// Set the new length and the current primitive
ray.length = t;
ray.primitive = this;
return false;
}
// Bounding box ////////////////////////////////////////////////////////////////
float Box::minimumBounds(int dimension) const { return this->center[dimension] - this->size[dimension] / 2; }
float Box::maximumBounds(int dimension) const { return this->center[dimension] + this->size[dimension] / 2; }