From 184812c53334f266d941c6f3984918440c67e12c Mon Sep 17 00:00:00 2001 From: vonhyou Date: Wed, 28 Feb 2024 00:58:29 -0500 Subject: [PATCH] calculate nearest hit --- src/Geometry.cc | 25 +++++++++++++++++++------ src/Geometry.h | 8 +++++--- src/HitRecord.cc | 7 +++++++ src/HitRecord.h | 19 +++++++++++++++++++ src/Optional.h | 25 +++++++++++++++++++++++++ src/RayTracer.cc | 25 +++++++++++++++++-------- 6 files changed, 92 insertions(+), 17 deletions(-) create mode 100644 src/HitRecord.cc create mode 100644 src/HitRecord.h create mode 100644 src/Optional.h diff --git a/src/Geometry.cc b/src/Geometry.cc index 0822d14..1072868 100644 --- a/src/Geometry.cc +++ b/src/Geometry.cc @@ -1,6 +1,7 @@ #include "Geometry.h" #include +#include Vector3f Geometry::diffuse() const { return cd; } Vector3f Geometry::specular() const { return cs; } @@ -10,13 +11,24 @@ void Geometry::setTransform(const Matrix4f &transform) { this->transform = transform; } -bool Sphere::intersect(const Ray &r) const { +Optional Sphere::intersect(const Ray &r) const { Vector3f originCenter = r.getOrigin() - center; float a = r.getDirection().dot(r.getDirection()); float b = 2.0f * originCenter.dot(r.getDirection()); float c = originCenter.dot(originCenter) - radius * radius; - return b * b - 4 * a * c >= 0; + float delta = b * b - 4 * a * c; + if (delta >= 0) { + float t1 = (-b + sqrt(delta)) / 2.0f / a; + float t2 = (-b - sqrt(delta)) / 2.0f / a; + float mint = std::min(t1, t2); + float maxt = std::max(t1, t2); + return maxt <= 0 + ? Optional::nullopt + : (mint < 0 ? Optional(maxt) : Optional(mint)); + } + + return Optional::nullopt; } bool isInRectangle(const Vector3f &p, const Vector3f &a, const Vector3f &b, @@ -30,18 +42,19 @@ bool isInRectangle(const Vector3f &p, const Vector3f &a, const Vector3f &b, (s1 <= 0 && s2 <= 0 && s3 <= 0 && s4 <= 0); } -bool Rectangle::intersect(const Ray &r) const { +Optional Rectangle::intersect(const Ray &r) const { Vector3f normal = (p2 - p1).cross(p3 - p1).normalized(); float denom = normal.dot(r.getDirection()); if (abs(denom) < 1e-6f) - return false; + return Optional::nullopt; float t = -normal.dot(r.getOrigin() - p1) / denom; if (t <= 0) - return false; + return Optional::nullopt; Vector3f p = r.getOrigin() + t * r.getDirection(); - return isInRectangle(p, p1, p2, p3, p4, normal); + return isInRectangle(p, p1, p2, p3, p4, normal) ? Optional(t) + : Optional::nullopt; } diff --git a/src/Geometry.h b/src/Geometry.h index f2df2ee..09c97c0 100644 --- a/src/Geometry.h +++ b/src/Geometry.h @@ -1,6 +1,7 @@ #ifndef GEOMETRY_H_ #define GEOMETRY_H_ +#include "Optional.h" #include "Ray.h" #include @@ -8,6 +9,7 @@ using Eigen::Matrix; using Eigen::Matrix4f; using Eigen::Vector3f; +using utils::Optional; // Abstract class for Geometries class Geometry { @@ -15,7 +17,7 @@ public: enum class Type { SPHERE, RECTANGLE }; virtual ~Geometry() = default; - virtual bool intersect(const Ray &) const = 0; + virtual Optional intersect(const Ray &) const = 0; protected: Geometry(Type type, float ka, float kd, float ks, const Vector3f &ca, @@ -42,7 +44,7 @@ public: : Geometry(Type::SPHERE, ka, kd, ks, ca, cd, cs, pc), radius(radius), center(center) {} - bool intersect(const Ray &) const override; + Optional intersect(const Ray &) const override; private: float radius; @@ -57,7 +59,7 @@ public: : Geometry(Type::RECTANGLE, ka, kd, ks, ca, cd, cs, pc), p1(p1), p2(p2), p3(p3), p4(p4) {} - bool intersect(const Ray &) const override; + Optional intersect(const Ray &) const override; private: Vector3f p1, p2, p3, p4; diff --git a/src/HitRecord.cc b/src/HitRecord.cc new file mode 100644 index 0000000..bb18770 --- /dev/null +++ b/src/HitRecord.cc @@ -0,0 +1,7 @@ +#include "HitRecord.h" + +bool HitRecord::operator<(const HitRecord &other) const { + return this->t > other.t; // to get the nearest t +} + +Geometry *HitRecord::geometry() const { return g; } diff --git a/src/HitRecord.h b/src/HitRecord.h new file mode 100644 index 0000000..705d2a6 --- /dev/null +++ b/src/HitRecord.h @@ -0,0 +1,19 @@ +#ifndef HITRECORD_H_ +#define HITRECORD_H_ + +#include "Geometry.h" + +class HitRecord { +public: + HitRecord(float t, Geometry *g) : t(t), g(g) {} + bool operator<(const HitRecord &) const; + +private: + float t; + Geometry *g; + +public: + Geometry *geometry() const; +}; + +#endif // !HITRECORD_H_ diff --git a/src/Optional.h b/src/Optional.h new file mode 100644 index 0000000..3b3a7d4 --- /dev/null +++ b/src/Optional.h @@ -0,0 +1,25 @@ +#ifndef OPTIONAL_H_ + +namespace utils { + +// I write this class because we are using C++14 +// so std::optional is not available +template class Optional { +public: + Optional() : hasValue_(false) {} + Optional(T value) : value_(value), hasValue_(true) {} + + bool hasValue() const { return hasValue_; } + T value() const { return value_; } + + static const Optional nullopt; + +private: + T value_; + bool hasValue_; +}; + +template const Optional Optional::nullopt = Optional(); +} // namespace utils + +#endif // !OPTIONAL_H_ diff --git a/src/RayTracer.cc b/src/RayTracer.cc index 27d9d5d..cd51d50 100644 --- a/src/RayTracer.cc +++ b/src/RayTracer.cc @@ -1,11 +1,15 @@ #include "RayTracer.h" #include "../external/simpleppm.h" +#include "HitRecord.h" #include "Output.h" #include "Parser.h" #include "Ray.h" #include #include +#include + +using std::priority_queue; void RayTracer::parse() { for (auto i = json["output"].begin(); i != json["output"].end(); ++i) @@ -46,15 +50,20 @@ void RayTracer::render(Scene *scene) { for (int y = 0; y < height; ++y) for (int x = 0; x < width; ++x) { Ray ray = getRay(x, y, cameraPos, pxUpperLeft, du, dv); + priority_queue records; + for (auto g : geometries) { + Optional t = g->intersect(ray); + if (t.hasValue()) + records.push(HitRecord(t.value(), g)); + } - for (auto g : geometries) - if (g->intersect(ray)) { - Vector3f diffuse = g->diffuse(); - buffer->r(y * width + x, diffuse.x()); - buffer->g(y * width + x, diffuse.y()); - buffer->b(y * width + x, diffuse.z()); - break; - } + if (!records.empty()) { + HitRecord hit = records.top(); + Vector3f diffuse = hit.geometry()->diffuse(); + buffer->r(y * width + x, diffuse.x()); + buffer->g(y * width + x, diffuse.y()); + buffer->b(y * width + x, diffuse.z()); + } } outputs.push_back(buffer);