Photon Engine 2.0.0-beta
A physically based renderer.
Loading...
Searching...
No Matches
TPwcDistribution1D.ipp
Go to the documentation of this file.
1#pragma once
2
4#include "Math/math.h"
5
6#include <Common/assertion.h>
7
8#include <algorithm>
9#include <cmath>
10
11namespace ph::math
12{
13
14template<typename T>
16 const T min, const T max,
17 const std::vector<T>& weights) :
18
20 min, max,
21 weights.data(), weights.size())
22{}
23
24template<typename T>
26 const T min,
27 const T max,
28 const T* const weights,
29 const std::size_t numWeights) :
30
31 m_min(min), m_max(max),
32
33 m_delta(0),
34
35 m_firstNonZeroPdfColumn(0),
36
37 // One more entry since we are storing values on endpoints
38 m_cdf(numWeights + 1, 0)
39{
40 PH_ASSERT(max > min && weights && numWeights > 0);
41 m_delta = (max - min) / static_cast<T>(numWeights);
43 // Construct CDF by first integrating the weights
44 m_cdf.front() = 0;
45 for(std::size_t i = 1; i <= numWeights; ++i)
46 {
47 const T wi = weights[i - 1];
48 PH_ASSERT_GE(wi, 0);
50 m_cdf[i] = m_cdf[i - 1] + wi * m_delta;
51 }
52
53 const T rcpSum = static_cast<T>(1) / m_cdf.back();
54
55 // Ensure first and last CDF entry is 0 and 1, respectively
56 m_cdf.front() = 0;
57 m_cdf.back() = 1;
58
59 if(std::isfinite(rcpSum))
60 {
61 // Normalize the CDF
62 for(std::size_t i = 1; i < numWeights; ++i)
63 {
64 m_cdf[i] *= rcpSum;
65 }
66 }
67 else
68 {
69 // If the sum is zero or non-finite, make a simple linear CDF.
70 for(std::size_t i = 1; i < numWeights; ++i)
71 {
72 m_cdf[i] = static_cast<T>(i) / static_cast<T>(numWeights);
73 }
74 }
76 // Find first column with non-zero PDF
77 for(std::size_t i = 0; i < numColumns(); ++i)
78 {
79 if(pdfContinuous(i) > 0)
80 {
81 m_firstNonZeroPdfColumn = i;
82 break;
83 }
84 }
85
86 PH_ASSERT_EQ(m_cdf.front(), 0);
87 PH_ASSERT_EQ(m_cdf.back(), 1);
88}
89
90template<typename T>
91inline TPwcDistribution1D<T>::TPwcDistribution1D(const std::vector<T>& weights) :
92 TPwcDistribution1D(0, 1, weights)
93{}
94
95template<typename T>
97
98template<typename T>
99inline std::size_t TPwcDistribution1D<T>::sampleDiscrete(const T sample) const
100{
101 const auto& result = std::lower_bound(m_cdf.begin(), m_cdf.end(), sample);
102 PH_ASSERT_MSG(result != m_cdf.end(),
103 "sample = " + std::to_string(sample) + ", "
104 "last CDF value = " + (m_cdf.empty() ? "(empty CDF)" : std::to_string(m_cdf.back())));
105
106 return result != m_cdf.begin() ? result - m_cdf.begin() - 1 : m_firstNonZeroPdfColumn;
107}
108
109template<typename T>
110inline T TPwcDistribution1D<T>::sampleContinuous(const T sample) const
111{
112 const std::size_t sampledColumn = sampleDiscrete(sample);
113 return continuouslySampleValue(sample, sampledColumn);
114}
115
116template<typename T>
117inline T TPwcDistribution1D<T>::sampleContinuous(const T sample, T* const out_pdf) const
118{
119 PH_ASSERT(out_pdf);
120
121 const std::size_t sampledColumn = sampleDiscrete(sample);
122
123 *out_pdf = pdfContinuous(sampledColumn);
124 return continuouslySampleValue(sample, sampledColumn);
125}
126
127template<typename T>
129 const T sample,
130 T* const out_pdf,
131 std::size_t* const out_straddledColumn) const
132{
133 PH_ASSERT(out_pdf);
134 PH_ASSERT(out_straddledColumn);
135
136 *out_straddledColumn = sampleDiscrete(sample);
137 *out_pdf = pdfContinuous(*out_straddledColumn);
138 return continuouslySampleValue(sample, *out_straddledColumn);
139}
140
141template<typename T>
142inline std::size_t TPwcDistribution1D<T>::numColumns() const
143{
144 PH_ASSERT(m_cdf.size() >= 2);
145
146 return m_cdf.size() - 1;
147}
148
149template<typename T>
150inline T TPwcDistribution1D<T>::pdfContinuous(const T sample) const
151{
152 return pdfContinuous(continuousToDiscrete(sample));
153}
154
155template<typename T>
156inline T TPwcDistribution1D<T>::pdfContinuous(const std::size_t columnIndex) const
157{
158 PH_ASSERT(!m_cdf.empty() &&
159 0 <= columnIndex && columnIndex < numColumns());
160
161 return (m_cdf[columnIndex + 1] - m_cdf[columnIndex]) / m_delta;
162}
163
164template<typename T>
165inline T TPwcDistribution1D<T>::pdfDiscrete(const std::size_t columnIndex) const
166{
167 PH_ASSERT(!m_cdf.empty() &&
168 0 <= columnIndex && columnIndex < numColumns());
169
170 return m_cdf[columnIndex + 1] - m_cdf[columnIndex];
171}
172
173template<typename T>
174std::size_t TPwcDistribution1D<T>::continuousToDiscrete(const T sample) const
175{
176 PH_ASSERT_MSG(m_min <= sample && sample <= m_max,
177 "m_min = " + std::to_string(m_min) + ", "
178 "m_max = " + std::to_string(m_max) + ", "
179 "sample = " + std::to_string(sample));
180
181 const T continuousColumn = (sample - m_min) / m_delta;
182 return math::clamp(static_cast<std::size_t>(continuousColumn),
183 static_cast<std::size_t>(0), numColumns() - 1);
184}
185
186template<typename T>
187inline T TPwcDistribution1D<T>::continuouslySampleValue(const T sample, const std::size_t straddledColumn) const
188{
189 PH_ASSERT(straddledColumn < numColumns());
190
191 const T cdfDelta = m_cdf[straddledColumn + 1] - m_cdf[straddledColumn];
192 T overshoot = sample - m_cdf[straddledColumn];
193 if(cdfDelta > 0)
194 {
195 overshoot /= cdfDelta;
196 }
197 PH_ASSERT(0 <= overshoot && overshoot <= 1);
198
199 // NOTE: <sampledValue> may have value straddling neighbor column's range
200 // due to numerical error. Currently this is considered acceptable since
201 // continuous sample does not require precise result.
202 const T sampledValue = m_delta * (overshoot + static_cast<T>(straddledColumn));
203
204 // TODO: check rare, sampled value should rarely exceed [min, max]
205 return math::clamp(sampledValue, m_min, m_max);
206}
207
208}// end namespace ph::math
A 1-D piecewise constant distribution of floating-point type T. The sample weights can be seen as a h...
Definition TPwcDistribution1D.h:16
std::size_t sampleDiscrete(T sample) const
Generate an index. Given a uniform unit random sample, generate a column index according to the sampl...
Definition TPwcDistribution1D.ipp:99
T sampleContinuous(T sample) const
Generate a continuous sample. Given a uniform unit random sample, generate a continuous sample accord...
Definition TPwcDistribution1D.ipp:110
T pdfDiscrete(std::size_t columnIndex) const
Definition TPwcDistribution1D.ipp:165
T pdfContinuous(T value) const
Definition TPwcDistribution1D.ipp:150
std::size_t numColumns() const
Definition TPwcDistribution1D.ipp:142
std::size_t continuousToDiscrete(T value) const
Calculates the sampled column index given a continuously sampled value.
Definition TPwcDistribution1D.ipp:174
Miscellaneous math utilities.
Math functions and utilities.
Definition TransformInfo.h:10
T clamp(const T value, const T lowerBound, const T upperBound)
Clamps a value to [lowerBound, upperBound]. None of value, lowerBound and upperBound can be NaN,...
Definition math.h:77