diff --git a/src/Geometry.cc b/src/Geometry.cc index 9eb69f5..9a98549 100644 --- a/src/Geometry.cc +++ b/src/Geometry.cc @@ -1,9 +1,43 @@ #include "Geometry.h" +#include + void Geometry::setTransform(const Matrix4f &transform) { this->transform = transform; } -bool Sphere::intersect() const { return false; } +bool 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; -bool Rectangle::intersect() const { return false; } + return b * b - 4 * a * c >= 0; +} + +bool isInRectangle(const Vector3f &p, const Vector3f &a, const Vector3f &b, + const Vector3f &c, const Vector3f &d, const Vector3f &n) { + float s1 = (b - a).cross(p - a).dot(n); + float s2 = (c - b).cross(p - b).dot(n); + float s3 = (d - c).cross(p - c).dot(n); + float s4 = (a - d).cross(p - d).dot(n); + + return (s1 >= 0 && s2 >= 0 && s3 >= 0 && s4 >= 0) || + (s1 <= 0 && s2 <= 0 && s3 <= 0 && s4 <= 0); +} + +bool 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; + + float t = -normal.dot(r.getOrigin() - p1) / denom; + if (t <= 0) + return false; + + Vector3f p = r.getOrigin() + t * r.getDirection(); + + return isInRectangle(p, p1, p2, p3, p4, normal); +} diff --git a/src/Geometry.h b/src/Geometry.h index b262b11..d92709f 100644 --- a/src/Geometry.h +++ b/src/Geometry.h @@ -1,6 +1,8 @@ #ifndef GEOMETRY_H_ #define GEOMETRY_H_ +#include "Ray.h" + #include using Eigen::Matrix; @@ -13,7 +15,7 @@ public: enum class Type { SPHERE, RECTANGLE }; virtual ~Geometry() = default; - virtual bool intersect() const = 0; + virtual bool intersect(const Ray &) const = 0; protected: Geometry(Type type, float ka, float kd, float ks, const Vector3f &ca, @@ -37,7 +39,7 @@ public: : Geometry(Type::SPHERE, ka, kd, ks, ca, cd, cs, pc), radius(radius), center(center) {} - bool intersect() const override; + bool intersect(const Ray &) const override; private: float radius; @@ -47,14 +49,15 @@ private: class Rectangle : public Geometry { public: Rectangle(float ka, float kd, float ks, const Vector3f &ca, const Vector3f cd, - const Vector3f &cs, float pc, const Matrix &corners) - : Geometry(Type::RECTANGLE, ka, kd, ks, ca, cd, cs, pc), - corners(corners) {} + const Vector3f &cs, float pc, const Vector3f &p1, + const Vector3f &p2, const Vector3f &p3, const Vector3f &p4) + : Geometry(Type::RECTANGLE, ka, kd, ks, ca, cd, cs, pc), p1(p1), p2(p2), + p3(p3), p4(p4) {} - bool intersect() const override; + bool intersect(const Ray &) const override; private: - Matrix corners; + Vector3f p1, p2, p3, p4; }; #endif // !GEOMETRY_H_ diff --git a/src/Light.h b/src/Light.h index 3b41c7b..c708467 100644 --- a/src/Light.h +++ b/src/Light.h @@ -46,14 +46,14 @@ private: class AreaLight : public Light { public: - AreaLight(const Vector3f &id, const Vector3f &is, - const Matrix &corners) - : Light(Type::Area, id, is), corners(corners) {} + AreaLight(const Vector3f &id, const Vector3f &is, const Vector3f &p1, + const Vector3f &p2, const Vector3f &p3, const Vector3f &p4) + : Light(Type::Area, id, is), p1(p1), p2(p2), p3(p3), p4(p4) {} virtual void illumination() const override; private: - Matrix corners; // stores `p1`, `p2`, `p3` and `p4` + Vector3f p1, p2, p3, p4; }; #endif // !LIGHT_H_ diff --git a/src/Parser.cc b/src/Parser.cc index 69fabb3..4d15d76 100644 --- a/src/Parser.cc +++ b/src/Parser.cc @@ -72,15 +72,6 @@ Geometry *Parser::getGeometry(const nlohmann::json &j) { return g; } -// helper function to get four corners of a rectangle -const Matrix getCorners(const nlohmann::json &j) { - Matrix corners; - for (int i = 0; i < 4; ++i) { - corners.col(i) = getVector3f(j["p" + std::to_string(i + 1)]); - } - return corners; -} - Sphere *Parser::getSphere(const nlohmann::json &j, float ka, float kd, float ks, const Vector3f &ca, const Vector3f &cd, const Vector3f &cs, float pc) { @@ -94,9 +85,12 @@ Rectangle *Parser::getRectangle(const nlohmann::json &j, float ka, float kd, float ks, const Vector3f &ca, const Vector3f &cd, const Vector3f &cs, float pc) { - Matrix corners = getCorners(j); + Vector3f p1 = getVector3f(j["p1"]); + Vector3f p2 = getVector3f(j["p2"]); + Vector3f p3 = getVector3f(j["p3"]); + Vector3f p4 = getVector3f(j["p4"]); - return new Rectangle(ka, kd, ks, ca, cd, cs, pc, corners); + return new Rectangle(ka, kd, ks, ca, cd, cs, pc, p1, p2, p3, p4); } Light *Parser::getLight(const nlohmann::json &j) { @@ -121,9 +115,12 @@ Light *Parser::getLight(const nlohmann::json &j) { AreaLight *Parser::getAreaLight(const nlohmann::json &j, const Vector3f &id, const Vector3f &is) { - Matrix corners = getCorners(j); - return new AreaLight(id, is, corners); + Vector3f p1 = getVector3f(j["p1"]); + Vector3f p2 = getVector3f(j["p2"]); + Vector3f p3 = getVector3f(j["p3"]); + Vector3f p4 = getVector3f(j["p4"]); + return new AreaLight(id, is, p1, p2, p3, p4); } PointLight *Parser::getPointLight(const nlohmann::json &j, const Vector3f &id, diff --git a/src/Ray.cc b/src/Ray.cc new file mode 100644 index 0000000..4cafd1d --- /dev/null +++ b/src/Ray.cc @@ -0,0 +1,5 @@ +#include "Ray.h" + +Vector3f Ray::getOrigin() const { return origin; } + +Vector3f Ray::getDirection() const { return direction; } diff --git a/src/Ray.h b/src/Ray.h new file mode 100644 index 0000000..9fe5b37 --- /dev/null +++ b/src/Ray.h @@ -0,0 +1,21 @@ +#ifndef RAY_H_ +#define RAY_H_ + +#include + +using Eigen::Vector3f; + +class Ray { +public: + Ray(const Vector3f &o, const Vector3f &d) : origin(o), direction(d) {} + +private: + Vector3f origin; + Vector3f direction; + +public: + Vector3f getOrigin() const; + Vector3f getDirection() const; +}; + +#endif // !RAY_H_ diff --git a/src/RayTracer.cc b/src/RayTracer.cc index 2cb39a1..e9c93a1 100644 --- a/src/RayTracer.cc +++ b/src/RayTracer.cc @@ -1,10 +1,10 @@ #include "RayTracer.h" #include "../external/simpleppm.h" #include "Parser.h" +#include "Ray.h" -#include - -using std::vector; +#include +#include void RayTracer::parse() { for (auto i = json["output"].begin(); i != json["output"].end(); ++i) @@ -17,19 +17,60 @@ void RayTracer::parse() { lights.push_back(Parser::getLight(*i)); } -void RayTracer::render() {} +Ray getRay(int x, int y, const Vector3f &camPos, const Vector3f &pxUpperLeft, + const Vector3f &du, const Vector3f &dv) { + return Ray(camPos, pxUpperLeft + x * du + y * dv - camPos); +} -void RayTracer::output() { - for (auto scene : scenes) { - int width = scene->getWidth(); - int height = scene->getHeight(); - vector buffer(3 * width * height); - save_ppm(scene->getName(), buffer, width, height); - } +void RayTracer::render(Scene *scene) { + int width = scene->getWidth(); + int height = scene->getHeight(); + float fov = scene->getFov(); + Vector3f cameraPos = scene->getCenter(); + Vector3f lookAt = scene->getLookAt(); + Vector3f up = scene->getUpVector(); + float vpHeight = 2 * tan(fov / 180 * M_PI / 2) * lookAt.norm(); + float vpWidth = vpHeight * width / height; + Vector3f vpU = Vector3f(vpWidth, 0, 0); + Vector3f vpV = Vector3f(0, -vpHeight, 0); + Vector3f du = vpU / width; + Vector3f dv = vpV / height; + + Vector3f vpUpperLeft = cameraPos + lookAt - vpU / 2.0 - vpV / 2.0; + Vector3f pxUpperLeft = vpUpperLeft + (du + dv) / 2.0; + + Buffer buffer(width * height * 3); + for (int y = 0; y < height; ++y) + for (int x = 0; x < width; ++x) { + Ray ray = getRay(x, y, cameraPos, pxUpperLeft, du, dv); + + for (auto geometry : geometries) + if (geometry->intersect(ray)) { + buffer[3 * y * width + 3 * x + 0] = 1; + buffer[3 * y * width + 3 * x + 1] = 1; + buffer[3 * y * width + 3 * x + 2] = 1; + break; + } + } + + Task *task = new Task(scene, buffer); + tasks.push_back(task); +} + +void RayTracer::output(Task *task) { + string path = task->first->getName(); + int width = task->first->getWidth(); + int height = task->first->getHeight(); + + save_ppm(path, task->second, width, height); } void RayTracer::run() { parse(); - render(); - output(); + + for (auto scene : scenes) + render(scene); + + for (auto task : tasks) + output(task); } diff --git a/src/RayTracer.h b/src/RayTracer.h index 1c18f19..95c917c 100644 --- a/src/RayTracer.h +++ b/src/RayTracer.h @@ -6,8 +6,12 @@ #include "Light.h" #include "Scene.h" +#include #include +using Buffer = std::vector; +using Task = std::pair; + class RayTracer { public: RayTracer(const nlohmann::json &j) : json(j) {} @@ -19,9 +23,11 @@ private: std::vector lights; std::vector geometries; + std::vector tasks; + void parse(); - void render(); - void output(); + void render(Scene *); + void output(Task *); }; #endif // !RAY_TRACER_H_ diff --git a/src/Scene.cc b/src/Scene.cc index ea22c48..8533ff3 100644 --- a/src/Scene.cc +++ b/src/Scene.cc @@ -1,11 +1,19 @@ #include "Scene.h" -string Scene::getName() { return name; } +string Scene::getName() const { return name; } int Scene::getWidth() { return width; } int Scene::getHeight() { return height; } +float Scene::getFov() { return fov; } + +Vector3f Scene::getCenter() const { return center; } + +Vector3f Scene::getUpVector() const { return up; } + +Vector3f Scene::getLookAt() const { return lookAt; } + void Scene::setRaysPerPixel(const Eigen::VectorXi &raysPerPixel) { this->raysPerPixel = raysPerPixel; } diff --git a/src/Scene.h b/src/Scene.h index 74ec55f..24a697e 100644 --- a/src/Scene.h +++ b/src/Scene.h @@ -32,9 +32,13 @@ private: bool globalIllum = false; public: - string getName(); + string getName() const; int getWidth(); int getHeight(); + float getFov(); + Vector3f getCenter() const; + Vector3f getUpVector() const; + Vector3f getLookAt() const; void setRaysPerPixel(const Eigen::VectorXi &); void setAntialiasing(bool); void setTwoSideRender(bool);