diff --git a/src/Light.cc b/src/Light.cc index a23cd0c..c2f5fe0 100644 --- a/src/Light.cc +++ b/src/Light.cc @@ -20,6 +20,8 @@ Vector3f Light::is() const { return is_; } bool Light::isUse() const { return use; } +Vector3f PointLight::getCenter() const { return center; } + Vector3f PointLight::illumination(const HitRecord &hit, const vector &geometries) const { Vector3f shadingPoint = hit.point(); @@ -46,6 +48,10 @@ Vector3f PointLight::illumination(const HitRecord &hit, return specular_ + ambient_ + diffuse_; } +Vector3f AreaLight::getCenter() const { + return p1 + (p4 - p1) / 2 + (p2 - p1) / 2; +} + Vector3f AreaLight::illumination(const HitRecord &hit, const vector &geometries) const { Vector3f u = p4 - p1; @@ -54,7 +60,7 @@ Vector3f AreaLight::illumination(const HitRecord &hit, Vector3f color = Vector3f::Zero(); if (useCenter) { - color += PointLight(*this, p1 + (u + v) / 2).illumination(hit, geometries); + color += PointLight(*this, getCenter()).illumination(hit, geometries); } else { for (int y = 0; y < gridSize; ++y) for (int x = 0; x < gridSize; ++x) { diff --git a/src/Light.h b/src/Light.h index 7f21867..b64773b 100644 --- a/src/Light.h +++ b/src/Light.h @@ -18,6 +18,7 @@ public: virtual ~Light() = default; virtual Vector3f illumination(const HitRecord &, const vector &) const = 0; + virtual Vector3f getCenter() const = 0; protected: Light(Type type, const Vector3f &id, const Vector3f &is) @@ -36,6 +37,7 @@ public: void setGridSize(unsigned int); void setUseCenter(bool); void setIsUse(bool); + Type type() const; Vector3f id() const; Vector3f is() const; bool isUse() const; @@ -52,6 +54,9 @@ public: private: Vector3f p1, p2, p3, p4; + +public: + Vector3f getCenter() const override; }; class PointLight : public Light { @@ -67,6 +72,9 @@ public: private: Vector3f center; + +public: + Vector3f getCenter() const override; }; #endif // !LIGHT_H_ diff --git a/src/RayTracer.cc b/src/RayTracer.cc index 2953b37..f4c1835 100644 --- a/src/RayTracer.cc +++ b/src/RayTracer.cc @@ -1,5 +1,6 @@ #include "RayTracer.h" #include "HitRecord.h" +#include "Light.h" #include "Output.h" #include "Parser.h" #include "Progress.h" @@ -7,6 +8,7 @@ #include "Ray.h" #include +#include #include #include #include @@ -89,26 +91,22 @@ void RayTracer::render() { for (int j = 0; j < gridHeight; ++j) for (int i = 0; i < gridWidth; ++i) { Ray ray = getRay(x, y, i, j); - utils::Optional result = trace(ray); - if (result.hasValue()) { - accumulate += result.value() * raysPerPixel; - success += raysPerPixel; + for (int rayNum = 0; rayNum < raysPerPixel; ++rayNum) { + utils::Optional result = trace(ray); + if (result.hasValue()) { + accumulate += result.value(); + success++; + } } } if (!success) color = accumulate / success; } else { Ray ray = getRay(x, y); - priority_queue records; - for (auto g : geometries) { - Optional t = g->intersect(ray); - if (t.hasValue()) - records.push(HitRecord(t.value(), ray, g)); - } + Optional hitRecord = getHitRecord(ray); - if (!records.empty()) { - HitRecord hit = records.top(); - hit.calcNormal(); + if (hitRecord.hasValue()) { + HitRecord hit = hitRecord.value(); color = calculateColor(hit, y * width + x); } } @@ -130,6 +128,33 @@ Vector3f RayTracer::calculateColor(const HitRecord &hit, int i) const { return result; } +/** + * Find the nearest geometry to intersect + */ +Optional RayTracer::getHitRecord(Ray r) const { + priority_queue records; + for (auto g : geometries) { + Optional t = g->intersect(r); + if (t.hasValue()) + records.push(HitRecord(t.value(), r, g)); + } + + if (!records.empty()) { + HitRecord result = records.top(); + result.calcNormal(); + return Optional(result); + } + + return Optional::nullopt; +} + +Light *RayTracer::singleLightSource() const { + for (auto light : lights) + if (light->isUse()) + return light; + return nullptr; +} + // helper functions Ray getRay(int x, int y) { using namespace camera; @@ -151,21 +176,39 @@ void writeColor(int i, const Vector3f &color) { Output::current->b(i, color.z()); } -Vector3f trace(Ray r, int bounce, float prob) { +Vector3f RayTracer::trace(HitRecord hit, int bounce, float prob) const { float dice = utils::Random::get(); if (bounce && (dice > prob)) { - return Vector3f(1, 0, 1).array() * trace(r, bounce - 1, prob).array(); + return Vector3f(1, 0, 1).array() * trace(hit, bounce - 1, prob).array(); + } else { + Light *light = singleLightSource(); + Vector3f point = hit.point(); + Vector3f direction = (light->getCenter() - point).normalized(); + Ray shadowRay(point, direction); + + Geometry *geometry = hit.geometry(); + + for (auto g : geometries) + if (g != geometry && g->intersect(shadowRay).hasValue() && + g->type() == Geometry::Type::SPHERE) + return Vector3f::Zero(); + + return geometry->cd().array() * light->id().array() * + std::max(0.0f, hit.normal().dot(direction)); } return Vector3f(1, 1, 1); } -utils::Optional trace(Ray r) { - Vector3f color = - trace(r, Scene::current->maxBounce(), Scene::current->probTerminate()); +utils::Optional RayTracer::trace(Ray r) const { + Optional hitRecord = getHitRecord(r); + if (hitRecord.hasValue()) { + Vector3f color = trace(hitRecord.value(), Scene::current->maxBounce(), + Scene::current->probTerminate()); - if (color != Vector3f::Zero()) - return utils::Optional(color); + if (color != Vector3f::Zero()) + return utils::Optional(color); + } return utils::Optional::nullopt; } diff --git a/src/RayTracer.h b/src/RayTracer.h index e30b8d3..a92e763 100644 --- a/src/RayTracer.h +++ b/src/RayTracer.h @@ -3,6 +3,7 @@ #include "../external/json.hpp" #include "Geometry.h" +#include "HitRecord.h" #include "Light.h" #include "Output.h" #include "Scene.h" @@ -22,8 +23,12 @@ private: std::vector outputs; void parse(); - Vector3f calculateColor(const HitRecord &, int) const; void render(); + Optional getHitRecord(Ray) const; + Vector3f calculateColor(const HitRecord &, int) const; + Light *singleLightSource() const; + Optional trace(Ray) const; + Vector3f trace(HitRecord, int, float) const; }; #endif // !RAY_TRACER_H_