diff --git a/RayTracer/tools/EmissionProfile.cpp b/RayTracer/tools/EmissionProfile.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6ce0d7bc4c740f040fe4e44635f4755d3c63a1ec --- /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 0000000000000000000000000000000000000000..bdd74a1a2ba2a110ead6a0ea390701c2f78f8a8f --- /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