2022-10-28 09:31:13 +02:00
|
|
|
#include "common/ray.h"
|
|
|
|
#include "primitive/sphere.h"
|
|
|
|
|
|
|
|
// Constructor /////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
Sphere::Sphere(std::shared_ptr<Shader> const &shader) : Primitive(shader), radius(0.5f) {}
|
|
|
|
|
|
|
|
Sphere::Sphere(Vector3d const ¢er, float radius, std::shared_ptr<Shader> const &shader)
|
|
|
|
: Primitive(shader), center(center), radius(radius) {}
|
|
|
|
|
|
|
|
// Primitive functions /////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
bool Sphere::intersect(Ray &ray) const {
|
2022-11-11 14:27:43 +01:00
|
|
|
// Use the definitions from the lecture
|
|
|
|
Vector3d const difference = ray.origin - this->center;
|
|
|
|
float const a = 1.0f;
|
|
|
|
float const b = 2.0f * dotProduct(ray.direction, difference);
|
|
|
|
float const c = dotProduct(difference, difference) - this->radius * this->radius;
|
|
|
|
float const discriminant = b * b - 4 * a * c;
|
|
|
|
|
|
|
|
// Test whether the ray could intersect at all
|
|
|
|
if (discriminant < 0)
|
|
|
|
return false;
|
|
|
|
float const root = std::sqrt(discriminant);
|
|
|
|
|
|
|
|
// Stable solution
|
|
|
|
float const q = -0.5f * (b < 0 ? (b - root) : (b + root));
|
|
|
|
float const t0 = q / a;
|
|
|
|
float const t1 = c / q;
|
|
|
|
float t = std::min(t0, t1);
|
|
|
|
if (t < EPSILON)
|
|
|
|
t = std::max(t0, t1);
|
2022-10-28 09:31:13 +02:00
|
|
|
|
|
|
|
// Test whether this is the foremost primitive in front of the camera
|
2022-11-11 14:27:43 +01:00
|
|
|
if (t < EPSILON || ray.length < t)
|
|
|
|
return false;
|
2022-10-28 09:31:13 +02:00
|
|
|
|
2022-11-11 14:27:43 +01:00
|
|
|
// Calculate the normal
|
2022-11-17 17:34:17 +01:00
|
|
|
auto intersectPoint = ray.origin + ray.direction * t;
|
|
|
|
auto normalDirection = intersectPoint - this->center; // Point of intersect - origin of sphere creates a line along the normal, pointing outwards
|
|
|
|
ray.normal = normalized(normalDirection);
|
2022-10-28 09:31:13 +02:00
|
|
|
|
2022-11-11 14:27:43 +01:00
|
|
|
// Calculate the surface position and tangent vector
|
|
|
|
float const phi = std::acos(ray.normal.y);
|
|
|
|
float const rho = std::atan2(ray.normal.z, ray.normal.x) + PI;
|
|
|
|
ray.surface = Vector2d(rho / (2 * PI), phi / PI);
|
|
|
|
ray.tangent = Vector3d(std::sin(rho), 0, std::cos(rho));
|
|
|
|
ray.bitangent = normalized(crossProduct(ray.normal, ray.tangent));
|
2022-10-28 09:31:13 +02:00
|
|
|
|
|
|
|
// Set the new length and the current primitive
|
2022-11-11 14:27:43 +01:00
|
|
|
ray.length = t;
|
|
|
|
ray.primitive = this;
|
2022-10-28 09:31:13 +02:00
|
|
|
|
2022-11-11 14:27:43 +01:00
|
|
|
// True, because the primitive was hit
|
|
|
|
return true;
|
2022-10-28 09:31:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Bounding box ////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
float Sphere::minimumBounds(int dimension) const { return this->center[dimension] - this->radius; }
|
|
|
|
|
|
|
|
float Sphere::maximumBounds(int dimension) const { return this->center[dimension] + this->radius; }
|