calculate nearest hit

This commit is contained in:
Shuo Feng 2024-02-28 00:58:29 -05:00
parent a8a00d386e
commit 184812c533
Signed by: sfeng
GPG key ID: 1E83AE6CD1C037B1
6 changed files with 92 additions and 17 deletions

View file

@ -1,6 +1,7 @@
#include "Geometry.h" #include "Geometry.h"
#include <Eigen/Dense> #include <Eigen/Dense>
#include <cmath>
Vector3f Geometry::diffuse() const { return cd; } Vector3f Geometry::diffuse() const { return cd; }
Vector3f Geometry::specular() const { return cs; } Vector3f Geometry::specular() const { return cs; }
@ -10,13 +11,24 @@ void Geometry::setTransform(const Matrix4f &transform) {
this->transform = transform; this->transform = transform;
} }
bool Sphere::intersect(const Ray &r) const { Optional<float> Sphere::intersect(const Ray &r) const {
Vector3f originCenter = r.getOrigin() - center; Vector3f originCenter = r.getOrigin() - center;
float a = r.getDirection().dot(r.getDirection()); float a = r.getDirection().dot(r.getDirection());
float b = 2.0f * originCenter.dot(r.getDirection()); float b = 2.0f * originCenter.dot(r.getDirection());
float c = originCenter.dot(originCenter) - radius * radius; 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<float>::nullopt
: (mint < 0 ? Optional<float>(maxt) : Optional<float>(mint));
}
return Optional<float>::nullopt;
} }
bool isInRectangle(const Vector3f &p, const Vector3f &a, const Vector3f &b, 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); (s1 <= 0 && s2 <= 0 && s3 <= 0 && s4 <= 0);
} }
bool Rectangle::intersect(const Ray &r) const { Optional<float> Rectangle::intersect(const Ray &r) const {
Vector3f normal = (p2 - p1).cross(p3 - p1).normalized(); Vector3f normal = (p2 - p1).cross(p3 - p1).normalized();
float denom = normal.dot(r.getDirection()); float denom = normal.dot(r.getDirection());
if (abs(denom) < 1e-6f) if (abs(denom) < 1e-6f)
return false; return Optional<float>::nullopt;
float t = -normal.dot(r.getOrigin() - p1) / denom; float t = -normal.dot(r.getOrigin() - p1) / denom;
if (t <= 0) if (t <= 0)
return false; return Optional<float>::nullopt;
Vector3f p = r.getOrigin() + t * r.getDirection(); Vector3f p = r.getOrigin() + t * r.getDirection();
return isInRectangle(p, p1, p2, p3, p4, normal); return isInRectangle(p, p1, p2, p3, p4, normal) ? Optional<float>(t)
: Optional<float>::nullopt;
} }

View file

@ -1,6 +1,7 @@
#ifndef GEOMETRY_H_ #ifndef GEOMETRY_H_
#define GEOMETRY_H_ #define GEOMETRY_H_
#include "Optional.h"
#include "Ray.h" #include "Ray.h"
#include <Eigen/Core> #include <Eigen/Core>
@ -8,6 +9,7 @@
using Eigen::Matrix; using Eigen::Matrix;
using Eigen::Matrix4f; using Eigen::Matrix4f;
using Eigen::Vector3f; using Eigen::Vector3f;
using utils::Optional;
// Abstract class for Geometries // Abstract class for Geometries
class Geometry { class Geometry {
@ -15,7 +17,7 @@ public:
enum class Type { SPHERE, RECTANGLE }; enum class Type { SPHERE, RECTANGLE };
virtual ~Geometry() = default; virtual ~Geometry() = default;
virtual bool intersect(const Ray &) const = 0; virtual Optional<float> intersect(const Ray &) const = 0;
protected: protected:
Geometry(Type type, float ka, float kd, float ks, const Vector3f &ca, 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), : Geometry(Type::SPHERE, ka, kd, ks, ca, cd, cs, pc), radius(radius),
center(center) {} center(center) {}
bool intersect(const Ray &) const override; Optional<float> intersect(const Ray &) const override;
private: private:
float radius; float radius;
@ -57,7 +59,7 @@ public:
: Geometry(Type::RECTANGLE, ka, kd, ks, ca, cd, cs, pc), p1(p1), p2(p2), : Geometry(Type::RECTANGLE, ka, kd, ks, ca, cd, cs, pc), p1(p1), p2(p2),
p3(p3), p4(p4) {} p3(p3), p4(p4) {}
bool intersect(const Ray &) const override; Optional<float> intersect(const Ray &) const override;
private: private:
Vector3f p1, p2, p3, p4; Vector3f p1, p2, p3, p4;

7
src/HitRecord.cc Normal file
View file

@ -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; }

19
src/HitRecord.h Normal file
View file

@ -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_

25
src/Optional.h Normal file
View file

@ -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 <typename T> 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 <typename T> const Optional<T> Optional<T>::nullopt = Optional<T>();
} // namespace utils
#endif // !OPTIONAL_H_

View file

@ -1,11 +1,15 @@
#include "RayTracer.h" #include "RayTracer.h"
#include "../external/simpleppm.h" #include "../external/simpleppm.h"
#include "HitRecord.h"
#include "Output.h" #include "Output.h"
#include "Parser.h" #include "Parser.h"
#include "Ray.h" #include "Ray.h"
#include <Eigen/Core> #include <Eigen/Core>
#include <cmath> #include <cmath>
#include <queue>
using std::priority_queue;
void RayTracer::parse() { void RayTracer::parse() {
for (auto i = json["output"].begin(); i != json["output"].end(); ++i) for (auto i = json["output"].begin(); i != json["output"].end(); ++i)
@ -46,14 +50,19 @@ void RayTracer::render(Scene *scene) {
for (int y = 0; y < height; ++y) for (int y = 0; y < height; ++y)
for (int x = 0; x < width; ++x) { for (int x = 0; x < width; ++x) {
Ray ray = getRay(x, y, cameraPos, pxUpperLeft, du, dv); Ray ray = getRay(x, y, cameraPos, pxUpperLeft, du, dv);
priority_queue<HitRecord> records;
for (auto g : geometries) {
Optional<float> t = g->intersect(ray);
if (t.hasValue())
records.push(HitRecord(t.value(), g));
}
for (auto g : geometries) if (!records.empty()) {
if (g->intersect(ray)) { HitRecord hit = records.top();
Vector3f diffuse = g->diffuse(); Vector3f diffuse = hit.geometry()->diffuse();
buffer->r(y * width + x, diffuse.x()); buffer->r(y * width + x, diffuse.x());
buffer->g(y * width + x, diffuse.y()); buffer->g(y * width + x, diffuse.y());
buffer->b(y * width + x, diffuse.z()); buffer->b(y * width + x, diffuse.z());
break;
} }
} }