diff options
Diffstat (limited to 'src/intersect/intersect.cpp')
-rw-r--r-- | src/intersect/intersect.cpp | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/src/intersect/intersect.cpp b/src/intersect/intersect.cpp new file mode 100644 index 0000000..3a39a87 --- /dev/null +++ b/src/intersect/intersect.cpp @@ -0,0 +1,265 @@ +#include "raytracer/raytracer.h" + +/** + * @brief This source file handles intersection calculations to be used by the ray tracer. + * The implementation for findIntersection in the RayTracer namespace is at the end of the file. + */ + +// TODO: implement mesh + +glm::vec4 intersectCircle( + glm::vec4 p, + glm::vec4 d, + const RenderShapeData& shape) +{ + // implicit: x^2 + y^2 + z^2 - r^2 = 0, all directions + float radius = 0.5f; + float a = d.x*d.x + d.y*d.y + d.z*d.z; + float b = 2.f * (p.x*d.x + p.y*d.y + p.z*d.z); + float c = p.x*p.x + p.y*p.y + p.z*p.z - radius*radius; + + float discriminant = b*b - 4*a*c; + if (discriminant < 0) // no solution + { + return glm::vec4(0.f); + } + + float t1 = (-b - std::sqrt(discriminant)) / (2.f*a); + float t2 = (-b + std::sqrt(discriminant)) / (2.f*a); + if (t1 <= 0 && t2 <= 0) // both behind camera + { + return glm::vec4(0.f); + } else if (t1 <= 0) // t2 in front of camera + { + return p + t2*d; + } else if (t2 <= 0) // t1 in front of camera + { + return p + t1*d; + } else { + float t = std::min(t1, t2); + return p + t*d; // want best intersection point + } +} + +glm::vec4 intersectCone( + glm::vec4 p, + glm::vec4 d, + const RenderShapeData& shape) +{ + float t = FINF; + + // implicit: x^2 + y^2 - z^2 = 0, conic top + float radius = 0.5f; + float a = d.x*d.x + d.z*d.z - .25f*(d.y*d.y); + float b = 2.f*(p.x*d.x + p.z*d.z) - .5f*(p.y*d.y) + .25f*d.y; + float c = p.x*p.x + p.z*p.z - .25f*(p.y*p.y) + .25f*p.y - 1/16.f; + + float discriminant = b*b - 4*a*c; + if (discriminant >= 0) + { + float t1 = (-b - std::sqrt(discriminant)) / (2.f*a); + float t2 = (-b + std::sqrt(discriminant)) / (2.f*a); + + auto p1Top = p + t1 * d; + if ( + t1 > 0 && + p1Top.y >= -.5f && p1Top.y <= .5f) + + { + t = std::min(t1, t); + } + + auto p2Top = p + t2 * d; + if ( + t2 > 0 && + p2Top.y >= -.5f && p2Top.y <= .5f) + + { + t = std::min(t2, t); + } + } + + + // implicit p_y + t*d_y = -.5f, top base + float tBase = (- .5f - p.y) / d.y; + auto pBase = p + tBase * d; + if ( + tBase > 0 && + pBase.x*pBase.x + pBase.z*pBase.z <= radius*radius + ) + { + t = std::min(t, tBase); + } + + return t == FINF ? glm::vec4(0.f) : p + t*d; +} + +glm::vec4 intersectCylinder( + glm::vec4 p, + glm::vec4 d, + const RenderShapeData& shape) +{ + float t = FINF; + + // implicit: x^2 + z^2 = 0, y between -.5, 5 rectuangular side + float radius = 0.5f; + float a = d.x*d.x + d.z*d.z; + float b = 2.f * (p.x*d.x + p.z*d.z); + float c = p.x*p.x + p.z*p.z - radius*radius; + + float discriminant = b*b - 4*a*c; + if (discriminant >= 0) + { + float t1 = (-b - std::sqrt(discriminant)) / (2.f*a); + float t2 = (-b + std::sqrt(discriminant)) / (2.f*a); + + auto p1Top = p + t1 * d; + if ( + t1 > 0 && + p1Top.y >= -.5f && p1Top.y <= .5f) + { + t = std::min(t1, t); + } + + auto p2Top = p + t2 * d; + if ( + t2 > 0 && + p2Top.y >= -.5f && p2Top.y <= .5f) + { + t = std::min(t2, t); + } + } + + + // implicit p_y + t*d_y = -.5f, top base + float tTop = (.5f - p.y) / d.y; + auto pTop = p + tTop * d; + if ( + tTop > 0 && + pTop.x*pTop.x + pTop.z*pTop.z <= radius*radius + ) + { + t = std::min(t, tTop); + } + + + // implicit p_y + t*d_y = -.5f, top base + float tBase = (- .5f - p.y) / d.y; + auto pBase = p + tBase * d; + if ( + tBase > 0 && + pBase.x*pBase.x + pBase.z*pBase.z <= radius*radius + ) + { + t = std::min(t, tBase); + } + + return t == FINF ? glm::vec4(0.f) : p + t*d; +} + +glm::vec4 intersectCube ( + glm::vec4 p, + glm::vec4 d, + const RenderShapeData& shape) +{ + // float t = FINF; + float apothem = .5f; + + // start with x-dir + float tmin = (-apothem - p.x) / d.x; + float tmax = (apothem - p.x) / d.x; + + // see if it hits top or bottom + if (tmin > tmax) + { + std::swap(tmin, tmax); + } + + // y-dir + float tymin = (-apothem - p.y) / d.y; + float tymax = (apothem - p.y) / d.y; + + if (tymin > tymax) + { + std::swap(tymin, tymax); + } + + if ((tmin > tymax) || (tymin > tmax)) + { // no hit + return glm::vec4(0.f); + } + + if (tymin > tmin) + { + tmin = tymin; + } + if (tymax < tmax) + { + tmax = tymax; + } + + // z-dir + float tzmin = (-apothem - p.z) / d.z; + float tzmax = (apothem - p.z) / d.z; + + if (tzmin > tzmax) + { + std::swap(tzmin, tzmax); + } + + if ((tmin > tzmax) || (tzmin > tmax)) + { // no hit + return glm::vec4(0.f); + } + + if (tzmin > tmin) + { + tmin = tzmin; + } + if (tzmax < tmax) + { + tmax = tzmax; + } + + if (tmin <= 0 && tmax <= 0) // both behind camera + { + return glm::vec4(0.f); + } else if (tmin > 0) // tmin in front of camera + { + return p + tmin*d; + } else if (tmin <= 0) // tmax in front of camera + { + return p + tmax*d; + } + + return glm::vec4(0.f); +} + +/** + * @brief Finds the intersection point of a ray and a shape. + * The ray and shape should be in the same space for this function to work properly. + * This function does not check if the intersection point is in front of the camera. + * @param p, the point of the ray + * @param d, the direction of the space + * @param shape, the shape to be intersected with the ray + * @return the intersection point as a vec4. If there exists no intersection, returns vec4(0.f). + */ +glm::vec4 RayTracer::findIntersection( + glm::vec4 p, + glm::vec4 d, + const RenderShapeData& shape) +{ + switch(shape.primitive.type) { + case PrimitiveType::PRIMITIVE_SPHERE: + return intersectCircle(p, d, shape); + case PrimitiveType::PRIMITIVE_CONE: + return intersectCone(p, d, shape); + case PrimitiveType::PRIMITIVE_CYLINDER: + return intersectCylinder(p, d, shape); + case PrimitiveType::PRIMITIVE_CUBE: + return intersectCube(p, d, shape); + case PrimitiveType::PRIMITIVE_MESH: + break; + } + return glm::vec4(0.f); +}
\ No newline at end of file |