From 812317f2ee15434720496315832a499ea5ef4e23 Mon Sep 17 00:00:00 2001 From: vonhyou Date: Thu, 22 Feb 2024 19:49:26 -0500 Subject: [PATCH 01/13] write background color --- src/RayTracer.cc | 7 +++++++ src/Scene.cc | 2 ++ src/Scene.h | 1 + 3 files changed, 10 insertions(+) diff --git a/src/RayTracer.cc b/src/RayTracer.cc index e9c93a1..7994cf6 100644 --- a/src/RayTracer.cc +++ b/src/RayTracer.cc @@ -40,6 +40,13 @@ void RayTracer::render(Scene *scene) { Vector3f pxUpperLeft = vpUpperLeft + (du + dv) / 2.0; Buffer buffer(width * height * 3); + Vector3f bgc = scene->getBackgroundColor(); + for (int i = 0; i < width * height; ++i) { + buffer[i * 3] = bgc.x(); + buffer[i * 3 + 1] = bgc.y(); + buffer[i * 3 + 2] = bgc.z(); + } + for (int y = 0; y < height; ++y) for (int x = 0; x < width; ++x) { Ray ray = getRay(x, y, cameraPos, pxUpperLeft, du, dv); diff --git a/src/Scene.cc b/src/Scene.cc index 8533ff3..9c48128 100644 --- a/src/Scene.cc +++ b/src/Scene.cc @@ -14,6 +14,8 @@ Vector3f Scene::getUpVector() const { return up; } Vector3f Scene::getLookAt() const { return lookAt; } +Vector3f Scene::getBackgroundColor() const { return backgroundColor; } + void Scene::setRaysPerPixel(const Eigen::VectorXi &raysPerPixel) { this->raysPerPixel = raysPerPixel; } diff --git a/src/Scene.h b/src/Scene.h index 24a697e..8ac10e0 100644 --- a/src/Scene.h +++ b/src/Scene.h @@ -39,6 +39,7 @@ public: Vector3f getCenter() const; Vector3f getUpVector() const; Vector3f getLookAt() const; + Vector3f getBackgroundColor() const; void setRaysPerPixel(const Eigen::VectorXi &); void setAntialiasing(bool); void setTwoSideRender(bool); From 94f1898b5183ab7f35ff1662bea9635e8212c25f Mon Sep 17 00:00:00 2001 From: vonhyou Date: Tue, 27 Feb 2024 20:35:41 -0500 Subject: [PATCH 02/13] rewrite output --- src/Output.cc | 22 ++++++++++++++++++++++ src/Output.h | 37 +++++++++++++++++++++++++++++++++++++ src/RayTracer.cc | 32 +++++++++++--------------------- src/RayTracer.h | 8 +++----- 4 files changed, 73 insertions(+), 26 deletions(-) create mode 100644 src/Output.cc create mode 100644 src/Output.h diff --git a/src/Output.cc b/src/Output.cc new file mode 100644 index 0000000..81da024 --- /dev/null +++ b/src/Output.cc @@ -0,0 +1,22 @@ +#include "Output.h" + +#include + +void Output::write() { + std::ofstream fout(path, std::ios_base::out | std::ios_base::binary); + fout << "P6\n" << width << ' ' << height << '\n' << "255" << std::endl; + + for (unsigned int y = 0; y < height; ++y) + for (unsigned int x = 0; x < width; ++x) + fout << (char)(255.0f * red[y * width + x]) + << (char)(255.0f * green[y * width + x]) + << (char)(255.0f * blue[y * width + x]); + fout.close(); +} + +float Output::r(int index) { return red.at(index); } +float Output::g(int index) { return green.at(index); } +float Output::b(int index) { return blue.at(index); } +void Output::r(int index, float value) { red.at(index) = value; } +void Output::g(int index, float value) { green.at(index) = value; } +void Output::b(int index, float value) { blue.at(index) = value; } diff --git a/src/Output.h b/src/Output.h new file mode 100644 index 0000000..d7b9af9 --- /dev/null +++ b/src/Output.h @@ -0,0 +1,37 @@ +#ifndef OUTPUT_H_ +#define OUTPUT_H_ + +#include +#include +#include + +using Eigen::Vector3f; +using std::string; +using std::vector; + +class Output { +public: + Output(const Vector3f &bgc, string path, int w, int h) + : red(vector(w * h, bgc.x())), + green(vector(w * h, bgc.y())), + blue(vector(w * h, bgc.z())), path(path), width(w), height(h) {} + + void write(); + +private: + int width, height; + string path; + vector red; + vector green; + vector blue; + +public: + void r(int, float); + float r(int); + void g(int, float); + float g(int); + void b(int, float); + float b(int); +}; + +#endif // !OUTPUT_H_ diff --git a/src/RayTracer.cc b/src/RayTracer.cc index 7994cf6..e4a393b 100644 --- a/src/RayTracer.cc +++ b/src/RayTracer.cc @@ -1,5 +1,6 @@ #include "RayTracer.h" #include "../external/simpleppm.h" +#include "Output.h" #include "Parser.h" #include "Ray.h" @@ -39,13 +40,8 @@ void RayTracer::render(Scene *scene) { Vector3f vpUpperLeft = cameraPos + lookAt - vpU / 2.0 - vpV / 2.0; Vector3f pxUpperLeft = vpUpperLeft + (du + dv) / 2.0; - Buffer buffer(width * height * 3); - Vector3f bgc = scene->getBackgroundColor(); - for (int i = 0; i < width * height; ++i) { - buffer[i * 3] = bgc.x(); - buffer[i * 3 + 1] = bgc.y(); - buffer[i * 3 + 2] = bgc.z(); - } + Output *buffer = + new Output(scene->getBackgroundColor(), scene->getName(), width, height); for (int y = 0; y < height; ++y) for (int x = 0; x < width; ++x) { @@ -53,23 +49,18 @@ void RayTracer::render(Scene *scene) { 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; + buffer->r(y * width + x, 1); + buffer->g(y * width + x, 1); + buffer->b(y * width + x, 1); break; } } - - Task *task = new Task(scene, buffer); - tasks.push_back(task); + outputs.push_back(buffer); } -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::output() { + for (auto output : outputs) + output->write(); } void RayTracer::run() { @@ -78,6 +69,5 @@ void RayTracer::run() { for (auto scene : scenes) render(scene); - for (auto task : tasks) - output(task); + output(); } diff --git a/src/RayTracer.h b/src/RayTracer.h index 95c917c..807b370 100644 --- a/src/RayTracer.h +++ b/src/RayTracer.h @@ -4,14 +4,12 @@ #include "../external/json.hpp" #include "Geometry.h" #include "Light.h" +#include "Output.h" #include "Scene.h" #include #include -using Buffer = std::vector; -using Task = std::pair; - class RayTracer { public: RayTracer(const nlohmann::json &j) : json(j) {} @@ -23,11 +21,11 @@ private: std::vector lights; std::vector geometries; - std::vector tasks; + std::vector outputs; void parse(); void render(Scene *); - void output(Task *); + void output(); }; #endif // !RAY_TRACER_H_ From 43a8204032842b163add049b2a80e4e69d302de9 Mon Sep 17 00:00:00 2001 From: vonhyou Date: Tue, 27 Feb 2024 21:23:24 -0500 Subject: [PATCH 03/13] make getters const --- src/Output.cc | 8 ++++---- src/Output.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Output.cc b/src/Output.cc index 81da024..e89ab7c 100644 --- a/src/Output.cc +++ b/src/Output.cc @@ -10,13 +10,13 @@ void Output::write() { for (unsigned int x = 0; x < width; ++x) fout << (char)(255.0f * red[y * width + x]) << (char)(255.0f * green[y * width + x]) - << (char)(255.0f * blue[y * width + x]); + << (char)(255.0f * blue[y * height + x]); fout.close(); } -float Output::r(int index) { return red.at(index); } -float Output::g(int index) { return green.at(index); } -float Output::b(int index) { return blue.at(index); } +float Output::r(int index) const { return red.at(index); } +float Output::g(int index) const { return green.at(index); } +float Output::b(int index) const { return blue.at(index); } void Output::r(int index, float value) { red.at(index) = value; } void Output::g(int index, float value) { green.at(index) = value; } void Output::b(int index, float value) { blue.at(index) = value; } diff --git a/src/Output.h b/src/Output.h index d7b9af9..68f9350 100644 --- a/src/Output.h +++ b/src/Output.h @@ -27,11 +27,11 @@ private: public: void r(int, float); - float r(int); + float r(int) const; void g(int, float); - float g(int); + float g(int) const; void b(int, float); - float b(int); + float b(int) const; }; #endif // !OUTPUT_H_ From a8a00d386e686997eb2fe15c651eddb753d0904a Mon Sep 17 00:00:00 2001 From: vonhyou Date: Tue, 27 Feb 2024 23:36:12 -0500 Subject: [PATCH 04/13] bugfix: color shadow --- src/Geometry.cc | 4 ++++ src/Geometry.h | 3 +++ src/Output.cc | 2 +- src/Output.h | 7 ++++--- src/RayTracer.cc | 12 +++++++----- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/Geometry.cc b/src/Geometry.cc index 9a98549..0822d14 100644 --- a/src/Geometry.cc +++ b/src/Geometry.cc @@ -2,6 +2,10 @@ #include +Vector3f Geometry::diffuse() const { return cd; } +Vector3f Geometry::specular() const { return cs; } +Vector3f Geometry::ambient() const { return ca; } + void Geometry::setTransform(const Matrix4f &transform) { this->transform = transform; } diff --git a/src/Geometry.h b/src/Geometry.h index d92709f..f2df2ee 100644 --- a/src/Geometry.h +++ b/src/Geometry.h @@ -29,6 +29,9 @@ protected: Matrix4f transform = Matrix4f::Identity(); public: + Vector3f diffuse() const; + Vector3f specular() const; + Vector3f ambient() const; void setTransform(const Matrix4f &); }; diff --git a/src/Output.cc b/src/Output.cc index e89ab7c..840212b 100644 --- a/src/Output.cc +++ b/src/Output.cc @@ -10,7 +10,7 @@ void Output::write() { for (unsigned int x = 0; x < width; ++x) fout << (char)(255.0f * red[y * width + x]) << (char)(255.0f * green[y * width + x]) - << (char)(255.0f * blue[y * height + x]); + << (char)(255.0f * blue[y * width + x]); fout.close(); } diff --git a/src/Output.h b/src/Output.h index 68f9350..d7a065d 100644 --- a/src/Output.h +++ b/src/Output.h @@ -12,9 +12,10 @@ using std::vector; class Output { public: Output(const Vector3f &bgc, string path, int w, int h) - : red(vector(w * h, bgc.x())), - green(vector(w * h, bgc.y())), - blue(vector(w * h, bgc.z())), path(path), width(w), height(h) {} + : red(vector(w * h + 1, bgc.x())), + green(vector(w * h + 1, bgc.y())), + blue(vector(w * h + 1, bgc.z())), path(path), width(w), + height(h) {} void write(); diff --git a/src/RayTracer.cc b/src/RayTracer.cc index e4a393b..27d9d5d 100644 --- a/src/RayTracer.cc +++ b/src/RayTracer.cc @@ -47,14 +47,16 @@ void RayTracer::render(Scene *scene) { 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->r(y * width + x, 1); - buffer->g(y * width + x, 1); - buffer->b(y * width + x, 1); + 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; } } + outputs.push_back(buffer); } From 184812c53334f266d941c6f3984918440c67e12c Mon Sep 17 00:00:00 2001 From: vonhyou Date: Wed, 28 Feb 2024 00:58:29 -0500 Subject: [PATCH 05/13] 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); From bb8819013ccc40bd45c4347b96431aa6d0309f49 Mon Sep 17 00:00:00 2001 From: vonhyou Date: Thu, 29 Feb 2024 00:15:00 -0500 Subject: [PATCH 06/13] work on direct illumination --- src/Geometry.cc | 11 +++++++++-- src/Geometry.h | 10 +++++++++- src/HitRecord.cc | 8 ++++++++ src/HitRecord.h | 11 ++++++++++- src/Light.cc | 19 +++++++++++++++++-- src/Light.h | 12 +++++++++--- src/RayTracer.cc | 25 +++++++++++++++++-------- src/RayTracer.h | 2 +- 8 files changed, 80 insertions(+), 18 deletions(-) diff --git a/src/Geometry.cc b/src/Geometry.cc index 1072868..1969397 100644 --- a/src/Geometry.cc +++ b/src/Geometry.cc @@ -6,6 +6,9 @@ Vector3f Geometry::diffuse() const { return cd; } Vector3f Geometry::specular() const { return cs; } Vector3f Geometry::ambient() const { return ca; } +float Geometry::coefDiffuse() const { return kd; } +float Geometry::coefSpecular() const { return ks; } +float Geometry::coefAmbient() const { return ka; } void Geometry::setTransform(const Matrix4f &transform) { this->transform = transform; @@ -31,6 +34,10 @@ Optional Sphere::intersect(const Ray &r) const { return Optional::nullopt; } +Vector3f Sphere::getNormal(const Vector3f &p) const { + return (p - center).normalized(); +} + 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); @@ -43,8 +50,6 @@ bool isInRectangle(const Vector3f &p, const Vector3f &a, const Vector3f &b, } 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 Optional::nullopt; @@ -58,3 +63,5 @@ Optional Rectangle::intersect(const Ray &r) const { return isInRectangle(p, p1, p2, p3, p4, normal) ? Optional(t) : Optional::nullopt; } + +Vector3f Rectangle::getNormal(const Vector3f &p) const { return normal; } diff --git a/src/Geometry.h b/src/Geometry.h index 09c97c0..dc3be95 100644 --- a/src/Geometry.h +++ b/src/Geometry.h @@ -5,6 +5,7 @@ #include "Ray.h" #include +#include using Eigen::Matrix; using Eigen::Matrix4f; @@ -18,6 +19,7 @@ public: virtual ~Geometry() = default; virtual Optional intersect(const Ray &) const = 0; + virtual Vector3f getNormal(const Vector3f &) const = 0; protected: Geometry(Type type, float ka, float kd, float ks, const Vector3f &ca, @@ -34,6 +36,9 @@ public: Vector3f diffuse() const; Vector3f specular() const; Vector3f ambient() const; + float coefDiffuse() const; + float coefSpecular() const; + float coefAmbient() const; void setTransform(const Matrix4f &); }; @@ -45,6 +50,7 @@ public: center(center) {} Optional intersect(const Ray &) const override; + Vector3f getNormal(const Vector3f &) const override; private: float radius; @@ -57,12 +63,14 @@ public: 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) {} + p3(p3), p4(p4), normal((p2 - p1).cross(p3 - p1).normalized()) {} Optional intersect(const Ray &) const override; + Vector3f getNormal(const Vector3f &) const override; private: Vector3f p1, p2, p3, p4; + Vector3f normal; }; #endif // !GEOMETRY_H_ diff --git a/src/HitRecord.cc b/src/HitRecord.cc index bb18770..9f50540 100644 --- a/src/HitRecord.cc +++ b/src/HitRecord.cc @@ -5,3 +5,11 @@ bool HitRecord::operator<(const HitRecord &other) const { } Geometry *HitRecord::geometry() const { return g; } + +Vector3f HitRecord::getPoint() const { + return r.getOrigin() + t * r.getDirection(); +} + +Vector3f HitRecord::normal() const { return n; } + +void HitRecord::calcNormal() { n = g->getNormal(getPoint()); } diff --git a/src/HitRecord.h b/src/HitRecord.h index 705d2a6..87290fc 100644 --- a/src/HitRecord.h +++ b/src/HitRecord.h @@ -2,18 +2,27 @@ #define HITRECORD_H_ #include "Geometry.h" +#include "Ray.h" +#include + +using Eigen::Vector3f; class HitRecord { public: - HitRecord(float t, Geometry *g) : t(t), g(g) {} + HitRecord(float t, const Ray &r, Geometry *g) : t(t), r(r), g(g) {} bool operator<(const HitRecord &) const; private: float t; + Ray r; + Vector3f n; Geometry *g; public: Geometry *geometry() const; + Vector3f getPoint() const; + Vector3f normal() const; + void calcNormal(); }; #endif // !HITRECORD_H_ diff --git a/src/Light.cc b/src/Light.cc index 07d7c27..68fb168 100644 --- a/src/Light.cc +++ b/src/Light.cc @@ -8,6 +8,21 @@ void Light::setGridSize(unsigned int gridSize) { this->gridSize = gridSize; } void Light::setUseCenter(bool useCenter) { this->useCenter = useCenter; } -void PointLight::illumination() const {} +Vector3f PointLight::illumination(const HitRecord &hit, + const vector &geometries) const { + Vector3f shadingPoint = hit.getPoint(); + Vector3f rayDirection = (center - shadingPoint).normalized(); + Geometry *geometry = hit.geometry(); + Ray shadowRay(shadingPoint, rayDirection); -void AreaLight::illumination() const {} + for (auto g : geometries) + if (g != geometry && g->intersect(shadowRay).hasValue()) + return Vector3f::Zero(); + + return Vector3f::Zero(); +} + +Vector3f AreaLight::illumination(const HitRecord &hit, + const vector &geometries) const { + return Vector3f::Zero(); +} diff --git a/src/Light.h b/src/Light.h index c708467..84163ed 100644 --- a/src/Light.h +++ b/src/Light.h @@ -1,11 +1,14 @@ #ifndef LIGHT_H_ #define LIGHT_H_ +#include "HitRecord.h" #include +#include using Eigen::Matrix; using Eigen::Matrix4f; using Eigen::Vector3f; +using std::vector; // Abstract base class for Lights class Light { @@ -13,7 +16,8 @@ public: enum class Type { Point, Area }; virtual ~Light() = default; - virtual void illumination() const = 0; + virtual Vector3f illumination(const HitRecord &, + const vector &) const = 0; protected: Light(Type type, const Vector3f &id, const Vector3f &is) @@ -38,7 +42,8 @@ public: PointLight(const Vector3f &id, const Vector3f &is, Vector3f ¢er) : Light(Type::Point, id, is), center(center) {} - virtual void illumination() const override; + virtual Vector3f illumination(const HitRecord &, + const vector &) const override; private: Vector3f center; @@ -50,7 +55,8 @@ public: 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; + virtual Vector3f illumination(const HitRecord &, + const vector &) const override; private: Vector3f p1, p2, p3, p4; diff --git a/src/RayTracer.cc b/src/RayTracer.cc index cd51d50..6ae028b 100644 --- a/src/RayTracer.cc +++ b/src/RayTracer.cc @@ -27,14 +27,25 @@ Ray getRay(int x, int y, const Vector3f &camPos, const Vector3f &pxUpperLeft, return Ray(camPos, pxUpperLeft + x * du + y * dv - camPos); } +void RayTracer::calculateColor(const HitRecord &hit, Output *buffer, int i) { + buffer->r(i, 0); + buffer->g(i, 0); + buffer->b(i, 0); + + for (auto light : lights) { + Vector3f contribution = light->illumination(hit, geometries); + buffer->r(i, buffer->r(i) + contribution.x()); + buffer->g(i, buffer->g(i) + contribution.y()); + buffer->b(i, buffer->b(i) + contribution.z()); + } +} + 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 vpHeight = 2 * tan(scene->getFov() / 180 * M_PI / 2) * lookAt.norm(); float vpWidth = vpHeight * width / height; Vector3f vpU = Vector3f(vpWidth, 0, 0); Vector3f vpV = Vector3f(0, -vpHeight, 0); @@ -54,15 +65,13 @@ void RayTracer::render(Scene *scene) { for (auto g : geometries) { Optional t = g->intersect(ray); if (t.hasValue()) - records.push(HitRecord(t.value(), g)); + records.push(HitRecord(t.value(), ray, g)); } 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()); + hit.calcNormal(); + calculateColor(hit, buffer, y * width + x); } } diff --git a/src/RayTracer.h b/src/RayTracer.h index 807b370..6ca8254 100644 --- a/src/RayTracer.h +++ b/src/RayTracer.h @@ -20,10 +20,10 @@ private: std::vector scenes; std::vector lights; std::vector geometries; - std::vector outputs; void parse(); + void calculateColor(const HitRecord &, Output *, int); void render(Scene *); void output(); }; From 27bdd95fa6df5c52cdb3fa43e9444773dd771da0 Mon Sep 17 00:00:00 2001 From: vonhyou Date: Thu, 29 Feb 2024 01:16:49 -0500 Subject: [PATCH 07/13] calculate ambient, diffuse and specular --- src/Geometry.cc | 1 + src/Geometry.h | 1 + src/HitRecord.cc | 4 ++++ src/HitRecord.h | 1 + src/Light.cc | 15 ++++++++++++++- 5 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Geometry.cc b/src/Geometry.cc index 1969397..0a29ab8 100644 --- a/src/Geometry.cc +++ b/src/Geometry.cc @@ -9,6 +9,7 @@ Vector3f Geometry::ambient() const { return ca; } float Geometry::coefDiffuse() const { return kd; } float Geometry::coefSpecular() const { return ks; } float Geometry::coefAmbient() const { return ka; } +float Geometry::getPhong() const { return phong; } void Geometry::setTransform(const Matrix4f &transform) { this->transform = transform; diff --git a/src/Geometry.h b/src/Geometry.h index dc3be95..b3d1eee 100644 --- a/src/Geometry.h +++ b/src/Geometry.h @@ -39,6 +39,7 @@ public: float coefDiffuse() const; float coefSpecular() const; float coefAmbient() const; + float getPhong() const; void setTransform(const Matrix4f &); }; diff --git a/src/HitRecord.cc b/src/HitRecord.cc index 9f50540..6a07ee7 100644 --- a/src/HitRecord.cc +++ b/src/HitRecord.cc @@ -10,6 +10,10 @@ Vector3f HitRecord::getPoint() const { return r.getOrigin() + t * r.getDirection(); } +Vector3f HitRecord::viewDirection() const { + return -r.getDirection().normalized(); +} + Vector3f HitRecord::normal() const { return n; } void HitRecord::calcNormal() { n = g->getNormal(getPoint()); } diff --git a/src/HitRecord.h b/src/HitRecord.h index 87290fc..c7fad17 100644 --- a/src/HitRecord.h +++ b/src/HitRecord.h @@ -21,6 +21,7 @@ private: public: Geometry *geometry() const; Vector3f getPoint() const; + Vector3f viewDirection() const; Vector3f normal() const; void calcNormal(); }; diff --git a/src/Light.cc b/src/Light.cc index 68fb168..724dade 100644 --- a/src/Light.cc +++ b/src/Light.cc @@ -1,4 +1,6 @@ #include "Light.h" +#include +#include void Light::setTransform(const Matrix4f &transform) { this->transform = transform; @@ -19,7 +21,18 @@ Vector3f PointLight::illumination(const HitRecord &hit, if (g != geometry && g->intersect(shadowRay).hasValue()) return Vector3f::Zero(); - return Vector3f::Zero(); + float distance = (center - shadingPoint).norm(); + float att = 1.0f / distance / distance; + + Vector3f ambient_ = geometry->coefAmbient() * geometry->ambient(); + Vector3f diffuse_ = att * geometry->coefDiffuse() * diffuse * + std::max(0.0f, hit.normal().dot(rayDirection)); + + Vector3f halfWay = (hit.viewDirection() + rayDirection).normalized(); + Vector3f specular_ = + att * geometry->coefSpecular() * specular * + pow(std::max(0.0f, hit.normal().dot(halfWay)), geometry->getPhong()); + return ambient_ + diffuse_ + specular_; } Vector3f AreaLight::illumination(const HitRecord &hit, From 6b1f0264273f44158abd5787fe35a1b7af0de19b Mon Sep 17 00:00:00 2001 From: vonhyou Date: Sun, 3 Mar 2024 21:52:38 -0500 Subject: [PATCH 08/13] make area light --- CMakeLists.txt | 4 ++-- src/Light.cc | 32 +++++++++++++++++++++++++------- src/Light.h | 30 +++++++++++++++++------------- 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 801846f..dfefc30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,8 +21,8 @@ add_compile_options(-DSTUDENT_SOLUTION) # When testing large scenes the debug mode will be very slow # so switch to release -set(CMAKE_BUILD_TYPE Debug) -#set(CMAKE_BUILD_TYPE Release) +#set(CMAKE_BUILD_TYPE Debug) +set(CMAKE_BUILD_TYPE Release) set(CMAKE_CXX_STANDARD 14) diff --git a/src/Light.cc b/src/Light.cc index 724dade..83c5cc8 100644 --- a/src/Light.cc +++ b/src/Light.cc @@ -1,6 +1,7 @@ #include "Light.h" #include #include +#include void Light::setTransform(const Matrix4f &transform) { this->transform = transform; @@ -10,6 +11,10 @@ void Light::setGridSize(unsigned int gridSize) { this->gridSize = gridSize; } void Light::setUseCenter(bool useCenter) { this->useCenter = useCenter; } +Vector3f Light::getDiffuse() const { return diffuse; } + +Vector3f Light::getSpecular() const { return specular; } + Vector3f PointLight::illumination(const HitRecord &hit, const vector &geometries) const { Vector3f shadingPoint = hit.getPoint(); @@ -21,21 +26,34 @@ Vector3f PointLight::illumination(const HitRecord &hit, if (g != geometry && g->intersect(shadowRay).hasValue()) return Vector3f::Zero(); - float distance = (center - shadingPoint).norm(); - float att = 1.0f / distance / distance; - Vector3f ambient_ = geometry->coefAmbient() * geometry->ambient(); - Vector3f diffuse_ = att * geometry->coefDiffuse() * diffuse * + Vector3f diffuse_ = geometry->coefDiffuse() * diffuse.array() * + geometry->diffuse().array() * std::max(0.0f, hit.normal().dot(rayDirection)); Vector3f halfWay = (hit.viewDirection() + rayDirection).normalized(); Vector3f specular_ = - att * geometry->coefSpecular() * specular * + geometry->coefSpecular() * specular.array() * + geometry->specular().array() * pow(std::max(0.0f, hit.normal().dot(halfWay)), geometry->getPhong()); - return ambient_ + diffuse_ + specular_; + return diffuse_ + specular_ + ambient_; } Vector3f AreaLight::illumination(const HitRecord &hit, const vector &geometries) const { - return Vector3f::Zero(); + Vector3f u = p4 - p1; + Vector3f v = p2 - p1; + + Vector3f color = Vector3f::Zero(); + + if (useCenter) { + color += PointLight(*this, (u + v) / 2).illumination(hit, geometries); + } else { + for (int y = 0; y < gridSize; ++y) + for (int x = 0; x < gridSize; ++x) + color += PointLight(*this, (u * x + v * y) / gridSize) + .illumination(hit, geometries); + } + + return color / gridSize / gridSize; } diff --git a/src/Light.h b/src/Light.h index 84163ed..be87209 100644 --- a/src/Light.h +++ b/src/Light.h @@ -31,22 +31,11 @@ protected: bool useCenter = false; // optional member `usecenter` public: - // setters for optional members void setTransform(const Matrix4f &); void setGridSize(unsigned int); void setUseCenter(bool); -}; - -class PointLight : public Light { -public: - PointLight(const Vector3f &id, const Vector3f &is, Vector3f ¢er) - : Light(Type::Point, id, is), center(center) {} - - virtual Vector3f illumination(const HitRecord &, - const vector &) const override; - -private: - Vector3f center; + Vector3f getDiffuse() const; + Vector3f getSpecular() const; }; class AreaLight : public Light { @@ -62,4 +51,19 @@ private: Vector3f p1, p2, p3, p4; }; +class PointLight : public Light { +public: + PointLight(const Vector3f &id, const Vector3f &is, const Vector3f ¢er) + : Light(Type::Point, id, is), center(center) {} + + PointLight(const AreaLight &al, const Vector3f ¢er) + : PointLight(al.getDiffuse(), al.getSpecular(), center) {} + + virtual Vector3f illumination(const HitRecord &, + const vector &) const override; + +private: + Vector3f center; +}; + #endif // !LIGHT_H_ From f98939c2067c35f5d118741661f9a32611f356bf Mon Sep 17 00:00:00 2001 From: vonhyou Date: Sun, 3 Mar 2024 22:43:27 -0500 Subject: [PATCH 09/13] update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c65ce39..ec45768 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build/ *.ppm *.zip +.vscode/ From efab58561ae693fd6c14691d36c0adb19b99b142 Mon Sep 17 00:00:00 2001 From: vonhyou Date: Sun, 3 Mar 2024 23:22:47 -0500 Subject: [PATCH 10/13] commit --- .gitignore | 1 + src/Light.cc | 5 ++++- src/Light.h | 3 +++ src/Output.cc | 7 ++++--- src/Parser.cc | 1 + src/RayTracer.cc | 13 +++++++------ 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index ec45768..bf7cd72 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ build/ *.ppm *.zip .vscode/ +.cache/ diff --git a/src/Light.cc b/src/Light.cc index 83c5cc8..aae27b6 100644 --- a/src/Light.cc +++ b/src/Light.cc @@ -1,7 +1,6 @@ #include "Light.h" #include #include -#include void Light::setTransform(const Matrix4f &transform) { this->transform = transform; @@ -11,10 +10,14 @@ void Light::setGridSize(unsigned int gridSize) { this->gridSize = gridSize; } void Light::setUseCenter(bool useCenter) { this->useCenter = useCenter; } +void Light::setIsUse(bool isUse) { this->use = isUse; } + Vector3f Light::getDiffuse() const { return diffuse; } Vector3f Light::getSpecular() const { return specular; } +bool Light::isUse() const { return use; } + Vector3f PointLight::illumination(const HitRecord &hit, const vector &geometries) const { Vector3f shadingPoint = hit.getPoint(); diff --git a/src/Light.h b/src/Light.h index be87209..60afbce 100644 --- a/src/Light.h +++ b/src/Light.h @@ -29,13 +29,16 @@ protected: Matrix4f transform = Matrix4f::Identity(); // optional member `transform` unsigned int gridSize = 0; // optional member `n` bool useCenter = false; // optional member `usecenter` + bool use = true; // this appears in a json file public: void setTransform(const Matrix4f &); void setGridSize(unsigned int); void setUseCenter(bool); + void setIsUse(bool); Vector3f getDiffuse() const; Vector3f getSpecular() const; + bool isUse() const; }; class AreaLight : public Light { diff --git a/src/Output.cc b/src/Output.cc index 840212b..c72c918 100644 --- a/src/Output.cc +++ b/src/Output.cc @@ -1,5 +1,6 @@ #include "Output.h" +#include #include void Output::write() { @@ -8,9 +9,9 @@ void Output::write() { for (unsigned int y = 0; y < height; ++y) for (unsigned int x = 0; x < width; ++x) - fout << (char)(255.0f * red[y * width + x]) - << (char)(255.0f * green[y * width + x]) - << (char)(255.0f * blue[y * width + x]); + fout << (char)std::min(255.0f, 255.0f * red[y * width + x]) + << (char)std::min(255.0f, 255.0f * green[y * width + x]) + << (char)std::min(255.0f, 255.0f * blue[y * width + x]); fout.close(); } diff --git a/src/Parser.cc b/src/Parser.cc index 4d15d76..126c6b3 100644 --- a/src/Parser.cc +++ b/src/Parser.cc @@ -109,6 +109,7 @@ Light *Parser::getLight(const nlohmann::json &j) { l->setGridSize(j.value("n", 0)); l->setUseCenter(j.value("usecenter", false)); + l->setIsUse(j.value("use", true)); return l; } diff --git a/src/RayTracer.cc b/src/RayTracer.cc index 6ae028b..09b7113 100644 --- a/src/RayTracer.cc +++ b/src/RayTracer.cc @@ -32,12 +32,13 @@ void RayTracer::calculateColor(const HitRecord &hit, Output *buffer, int i) { buffer->g(i, 0); buffer->b(i, 0); - for (auto light : lights) { - Vector3f contribution = light->illumination(hit, geometries); - buffer->r(i, buffer->r(i) + contribution.x()); - buffer->g(i, buffer->g(i) + contribution.y()); - buffer->b(i, buffer->b(i) + contribution.z()); - } + for (auto light : lights) + if (light->isUse()) { + Vector3f contribution = light->illumination(hit, geometries); + buffer->r(i, buffer->r(i) + contribution.x()); + buffer->g(i, buffer->g(i) + contribution.y()); + buffer->b(i, buffer->b(i) + contribution.z()); + } } void RayTracer::render(Scene *scene) { From b5878e06c90e8089482ae4e9c3eb4a20aa069a0c Mon Sep 17 00:00:00 2001 From: vonhyou Date: Mon, 4 Mar 2024 02:25:59 -0500 Subject: [PATCH 11/13] bugfix: clamp --- src/Light.cc | 12 +++++++----- src/Output.cc | 7 +++---- src/RayTracer.cc | 5 +++-- src/RayTracer.h | 1 - 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Light.cc b/src/Light.cc index aae27b6..e9f1075 100644 --- a/src/Light.cc +++ b/src/Light.cc @@ -30,16 +30,18 @@ Vector3f PointLight::illumination(const HitRecord &hit, return Vector3f::Zero(); Vector3f ambient_ = geometry->coefAmbient() * geometry->ambient(); - Vector3f diffuse_ = geometry->coefDiffuse() * diffuse.array() * - geometry->diffuse().array() * + + Vector3f diffuse_ = geometry->coefDiffuse() * geometry->diffuse().array() * + diffuse.array() * std::max(0.0f, hit.normal().dot(rayDirection)); Vector3f halfWay = (hit.viewDirection() + rayDirection).normalized(); Vector3f specular_ = - geometry->coefSpecular() * specular.array() * - geometry->specular().array() * + geometry->coefSpecular() * geometry->specular().array() * + specular.array() * pow(std::max(0.0f, hit.normal().dot(halfWay)), geometry->getPhong()); - return diffuse_ + specular_ + ambient_; + + return specular_ + ambient_ + diffuse_; } Vector3f AreaLight::illumination(const HitRecord &hit, diff --git a/src/Output.cc b/src/Output.cc index c72c918..840212b 100644 --- a/src/Output.cc +++ b/src/Output.cc @@ -1,6 +1,5 @@ #include "Output.h" -#include #include void Output::write() { @@ -9,9 +8,9 @@ void Output::write() { for (unsigned int y = 0; y < height; ++y) for (unsigned int x = 0; x < width; ++x) - fout << (char)std::min(255.0f, 255.0f * red[y * width + x]) - << (char)std::min(255.0f, 255.0f * green[y * width + x]) - << (char)std::min(255.0f, 255.0f * blue[y * width + x]); + fout << (char)(255.0f * red[y * width + x]) + << (char)(255.0f * green[y * width + x]) + << (char)(255.0f * blue[y * width + x]); fout.close(); } diff --git a/src/RayTracer.cc b/src/RayTracer.cc index 09b7113..c79add8 100644 --- a/src/RayTracer.cc +++ b/src/RayTracer.cc @@ -31,10 +31,11 @@ void RayTracer::calculateColor(const HitRecord &hit, Output *buffer, int i) { buffer->r(i, 0); buffer->g(i, 0); buffer->b(i, 0); - for (auto light : lights) if (light->isUse()) { - Vector3f contribution = light->illumination(hit, geometries); + Vector3f contribution = + light->illumination(hit, geometries).cwiseMax(0.0f).cwiseMin(1.0f) / + lights.size(); buffer->r(i, buffer->r(i) + contribution.x()); buffer->g(i, buffer->g(i) + contribution.y()); buffer->b(i, buffer->b(i) + contribution.z()); diff --git a/src/RayTracer.h b/src/RayTracer.h index 6ca8254..43a9075 100644 --- a/src/RayTracer.h +++ b/src/RayTracer.h @@ -7,7 +7,6 @@ #include "Output.h" #include "Scene.h" -#include #include class RayTracer { From 6d81bd45303db17496ab48be9de305724f474933 Mon Sep 17 00:00:00 2001 From: vonhyou Date: Mon, 4 Mar 2024 02:43:10 -0500 Subject: [PATCH 12/13] calc correct light --- src/Light.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Light.cc b/src/Light.cc index e9f1075..4298adf 100644 --- a/src/Light.cc +++ b/src/Light.cc @@ -1,6 +1,7 @@ #include "Light.h" #include #include +#include void Light::setTransform(const Matrix4f &transform) { this->transform = transform; @@ -52,11 +53,11 @@ Vector3f AreaLight::illumination(const HitRecord &hit, Vector3f color = Vector3f::Zero(); if (useCenter) { - color += PointLight(*this, (u + v) / 2).illumination(hit, geometries); + color += PointLight(*this, p1 + (u + v) / 2).illumination(hit, geometries); } else { for (int y = 0; y < gridSize; ++y) for (int x = 0; x < gridSize; ++x) - color += PointLight(*this, (u * x + v * y) / gridSize) + color += PointLight(*this, p1 + (u * x + v * y) / gridSize) .illumination(hit, geometries); } From 0aa8696cf482ff721898baef10d4c4bbdc4a5988 Mon Sep 17 00:00:00 2001 From: vonhyou Date: Mon, 4 Mar 2024 02:48:33 -0500 Subject: [PATCH 13/13] delete debug info --- src/Light.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Light.cc b/src/Light.cc index 4298adf..2b0030a 100644 --- a/src/Light.cc +++ b/src/Light.cc @@ -1,7 +1,6 @@ #include "Light.h" #include #include -#include void Light::setTransform(const Matrix4f &transform) { this->transform = transform;