diff --git a/RayTracer/shape/RectanglePlane.cpp b/RayTracer/shape/RectanglePlane.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fe8fb65afeefd3df60b382a343905174afe3b8fd --- /dev/null +++ b/RayTracer/shape/RectanglePlane.cpp @@ -0,0 +1,63 @@ +#define _USE_MATH_DEFINES + +#include "RectanglePlane.h" + +#include "../tools/Random.h" +#include "math.h" + +namespace shapes { +RectanglePlane::RectanglePlane( + float width, float depth, bool twofaced, + const std::shared_ptr<material::Material>& material) + : width(width), depth(depth), twofaced(twofaced), material(material) { +} +std::optional<cam::Hit> RectanglePlane::intersect(const cam::Ray& r) const { + util::Vec3 n(0, 1, 0); + util::Vec3 x0 = r.x0; + util::Vec3 d = r.d; + + float a = util::dot(d, n); + if (a == 0) { + return std::nullopt; + } else if (a > 0) { + if (twofaced) + n = -n; + else + return std::nullopt; + } + float t = -x0[1] / d[1]; + util::Vec3 t_hitpoint = r(t); + + if (r.in_range(t) && std::abs(t_hitpoint.x()) <= width / 2 && + std::abs(t_hitpoint.z()) <= depth / 2) { + return std::optional<cam::Hit>({t_hitpoint, n, t, material}); + } else { + return std::nullopt; + } +} +util::AxisAlignedBoundingBox RectanglePlane::bounds() const { + return util::AxisAlignedBoundingBox(util::Vec3(-width / 2, 0, -depth / 2), + util::Vec3(width / 2, 0, depth / 2)); +} +util::SurfacePoint RectanglePlane::sampleLight() const { + // X coord of the sampled point. + float x = util::disMinus1To1(util::gen) * width / 2; + // Z coord of the sampled point. + float z = util::disMinus1To1(util::gen) * width / 2; + return util::SurfacePoint(util::Vec3(x, 0, z), util::Vec3(0, 1, 0), + material); + // The sampled point will be in local coordinates. +} +util::Vec3 RectanglePlane::calculateLightEmission(const util::SurfacePoint& p, + const util::Vec3& d) const { + // Basically this is just the emission at a surface point. And the pdf dimms + // the light in regard to the angle. + // Uniform pdf of shape is 1/area, converting to pdf over solid angle is + // pdf/(dot/length^2). + auto emission = p.emission(); + auto dot = std::max<float>(util::dot(p.normal(), d.normalize()), 0); + auto area = width * depth; + auto pdf = std::pow(d.length(), 2) / (dot * area); + return emission / pdf; +} +} // namespace shapes diff --git a/RayTracer/shape/RectanglePlane.h b/RayTracer/shape/RectanglePlane.h new file mode 100644 index 0000000000000000000000000000000000000000..900c2ff78b224e23d6a3daec2a05c8d33f6218e6 --- /dev/null +++ b/RayTracer/shape/RectanglePlane.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Light.h" +#include "Shape.h" + +namespace shapes { +class RectanglePlane : public Light, public Shape { + public: + RectanglePlane(float width, float depth, bool twofaced, + const std::shared_ptr<material::Material>& material); + std::optional<cam::Hit> intersect(const cam::Ray& r) const override; + util::AxisAlignedBoundingBox bounds() const override; + + util::SurfacePoint sampleLight() const override; + util::Vec3 calculateLightEmission(const util::SurfacePoint& p, + const util::Vec3& d) const override; + + private: + std::shared_ptr<material::Material> material; + const float width, depth; + const bool twofaced; +}; +} // namespace shapes