#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "helper.hpp" #include #include #include #include #include #include #include "camera.hpp" #include "ray.hpp" #include "random_sampler.hpp" #include "random_sampler_wrapper.hpp" // https://github.com/mmp/pbrt-v3/blob/master/src/core/pbrt.h#L403 template int FindInterval(int size, const Predicate& pred) { int first = 0, len = size; while (len > 0) { int half = len >> 1, middle = first + half; // Bisect range based on value of _pred_ at _middle_ if (pred(middle)) { first = middle + 1; len -= half + 1; } else len = half; } return clamp(first - 1, 0, size - 2); } struct Distribution1D { Distribution1D(){} // Distribution1D Public Methods Distribution1D(const float* f, int n) : func(f, f + n), cdf(n + 1) { // Compute integral of step function at $x_i$ cdf[0] = 0; for (int i = 1; i < n + 1; ++i) cdf[i] = cdf[i - 1] + func[i - 1] / n; // Transform step function integral into CDF funcInt = cdf[n]; if (funcInt == 0) { for (int i = 1; i < n + 1; ++i) cdf[i] = float(i) / float(n); } else { for (int i = 1; i < n + 1; ++i) cdf[i] /= funcInt; } } int Count() const { return (int)func.size(); } float SampleContinuous(float u, float* pdf, int* off = nullptr) const { // Find surrounding CDF segments and _offset_ int offset = FindInterval((int)cdf.size(), [&](int index) { return cdf[index] <= u; }); if (off) *off = offset; // Compute offset along CDF segment float du = u - cdf[offset]; if ((cdf[offset + 1] - cdf[offset]) > 0) { du /= (cdf[offset + 1] - cdf[offset]); } assert(!std::isnan(du)); // Compute PDF for sampled offset if (pdf) *pdf = (funcInt > 0) ? func[offset] / funcInt : 0; // Return $x\in{}[0,1)$ corresponding to sample return (offset + du) / Count(); } int SampleDiscrete(float u, float* pdf = nullptr, float* uRemapped = nullptr) const { // Find surrounding CDF segments and _offset_ int offset = FindInterval((int)cdf.size(), [&](int index) { return cdf[index] <= u; }); if (pdf) *pdf = (funcInt > 0) ? func[offset] / (funcInt * Count()) : 0; if (uRemapped) *uRemapped = (u - cdf[offset]) / (cdf[offset + 1] - cdf[offset]); if (uRemapped) assert(*uRemapped >= 0.f && *uRemapped <= 1.f); return offset; } float DiscretePDF(int index) const { assert(index >= 0 && index < Count()); return func[index] / (funcInt * Count()); } // Distribution1D Public Data std::vector func, cdf; float funcInt; };