From 1279311c3b55c71e5f05e92c6e5929376139e781 Mon Sep 17 00:00:00 2001
From: Yoel <s73017@beuth-hochschule.de>
Date: Mon, 23 Nov 2020 14:02:28 +0100
Subject: [PATCH] RectanglePlane implemented as Shape and as Light

---
 RayTracer/shape/RectanglePlane.cpp | 63 ++++++++++++++++++++++++++++++
 RayTracer/shape/RectanglePlane.h   | 23 +++++++++++
 2 files changed, 86 insertions(+)
 create mode 100644 RayTracer/shape/RectanglePlane.cpp
 create mode 100644 RayTracer/shape/RectanglePlane.h

diff --git a/RayTracer/shape/RectanglePlane.cpp b/RayTracer/shape/RectanglePlane.cpp
new file mode 100644
index 0000000..fe8fb65
--- /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 0000000..900c2ff
--- /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
-- 
GitLab