From 15feef3a9784ab337eb8a6942160cefc0fa1f24b Mon Sep 17 00:00:00 2001
From: Yoel <s73017@beuth-hochschule.de>
Date: Mon, 29 Mar 2021 17:42:14 +0200
Subject: [PATCH] EmissionProfile implemented

---
 RayTracer/tools/EmissionProfile.cpp | 101 ++++++++++++++++++++++++++++
 RayTracer/tools/EmissionProfile.h   |  36 ++++++++++
 2 files changed, 137 insertions(+)
 create mode 100644 RayTracer/tools/EmissionProfile.cpp
 create mode 100644 RayTracer/tools/EmissionProfile.h

diff --git a/RayTracer/tools/EmissionProfile.cpp b/RayTracer/tools/EmissionProfile.cpp
new file mode 100644
index 0000000..6ce0d7b
--- /dev/null
+++ b/RayTracer/tools/EmissionProfile.cpp
@@ -0,0 +1,101 @@
+#include "EmissionProfile.h"
+
+#include <math.h>
+
+#include <algorithm>
+#include <numeric>
+
+#include "../tools/Random.h"
+
+namespace util {
+
+EmissionProfile::EmissionProfile(const std::shared_ptr<util::Sampler>& emission,
+                                 float intensity)
+    : emission(emission),
+      uniform(true),
+      cdf(),
+      intensity(intensity),
+      normalizer(0.0f),
+      distribution(std::nullopt){};
+EmissionProfile::EmissionProfile(const std::shared_ptr<util::Sampler>& emission,
+                                 const Image& distribution, float intensity)
+    : emission(emission),
+      uniform(false),
+      intensity(intensity),
+      distribution(std::make_optional(distribution)) {
+	// vec: a vector with texel and grey value
+	std::vector<std::pair<std::pair<float, float>, float>> vec;
+	for (int w = 0; w <= distribution.width; w++) {
+		for (int h = 0; h <= distribution.height; h++) {
+			auto xyz = distribution[{w, h}];
+			float grey = 0.299f * xyz[0] + 0.587f * xyz[1] + 0.114f * xyz[2];
+			vec.push_back({{(float)w / (float)distribution.width,
+			                (float)h / (float)distribution.height},
+			               grey});
+		}
+	}
+	// Sort the value from biggest to smallest v
+	std::sort(vec.begin(), vec.end(),
+	          [](std::pair<std::pair<float, float>, float> a,
+	             std::pair<std::pair<float, float>, float> b) {
+		          return a.second > b.second;
+	          });
+	// Build cdf
+	cdf = {};
+	float cumm = 0;
+	for (auto x : vec) {
+		cumm += x.second;
+		// std::cout << cumm << std::endl;
+		cdf.push_back({x.first, cumm});
+	}
+	// Transform cdf to cap at 1
+	for (int i = 0; i < cdf.size(); i++) {
+		cdf[i].second = cdf[i].second / cumm;
+	}
+	// Normalizer
+	normalizer = (distribution.width * distribution.height) / cumm;
+};
+
+std::pair<float, float> EmissionProfile::sample() const {
+	if (uniform) {
+		return {(float)util::dis0to1(util::gen),
+		        (float)util::dis0to1(util::gen)};
+	} else {
+		auto s = dis0to1(gen);
+		auto res = binary_search(s);
+		return res.first;
+	}
+};
+util::Vec3 EmissionProfile::color(float x, float y) const {
+	return emission->color(x, y);
+}
+std::optional<float> EmissionProfile::pdf(float x, float y) const {
+	if (uniform) return std::nullopt;
+	auto xyz = distribution->color(x, y);
+
+	return std::make_optional<float>(
+	    (0.299f * xyz[0] + 0.587f * xyz[1] + 0.114f * xyz[2]) * normalizer);
+}
+
+std::pair<std::pair<float, float>, float> EmissionProfile::binary_search(
+    float target, int start, int end) const {
+	int mid;
+	if (end == -1) end = cdf.size();
+	while (start <= end) {
+		mid = (start + end) / 2;
+		if (cdf[mid].second <= target)
+			start = mid + 1;
+		else
+			end = mid - 1;
+	}
+	return cdf[mid];
+}
+// Not used
+std::pair<std::pair<float, float>, float> EmissionProfile::exponential_search(
+    float target) const {
+	if (cdf[0].second >= target) return cdf[0];
+	int start = 1;
+	while (start < cdf.size() && cdf[start].second <= target) start = 2 * start;
+	return binary_search(target, start / 2, std::min<int>(start, cdf.size()));
+}
+}  // namespace util
\ No newline at end of file
diff --git a/RayTracer/tools/EmissionProfile.h b/RayTracer/tools/EmissionProfile.h
new file mode 100644
index 0000000..bdd74a1
--- /dev/null
+++ b/RayTracer/tools/EmissionProfile.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <optional>
+
+#include "../sampling/Image.h"
+#include "../sampling/Sampler.h"
+
+namespace util {
+class EmissionProfile : public Sampler {
+   public:
+	// Uniform distribution constructor
+	EmissionProfile(const std::shared_ptr<util::Sampler>& emission,
+	                float intensity = 1);
+	// Image distribution constructor
+	EmissionProfile(const std::shared_ptr<util::Sampler>& emission,
+	                const Image& distribution, float intensity = 1);
+	// Returns a texels based on distribution
+	std::pair<float, float> sample() const;
+	util::Vec3 color(float x, float y) const override;
+	std::optional<float> pdf(float x, float y) const;
+
+   private:
+	std::shared_ptr<util::Sampler> emission;
+	std::optional<Image> distribution;
+	std::vector<std::pair<std::pair<float, float>, float>> cdf;
+	std::pair<std::pair<float, float>, float> binary_search(float target,
+	                                                        int start = 0,
+	                                                        int end = -1) const;
+	std::pair<std::pair<float, float>, float> exponential_search(
+	    float target) const;
+	bool uniform;
+	float intensity;
+	float normalizer;
+};
+
+}  // namespace util
\ No newline at end of file
-- 
GitLab