Path Tracer
pbr_spectrum.hpp
1 #pragma once
2 #include <color/spd.hpp>
3 #include <color/specdata.hpp>
4 #include <color/specutils.hpp>
5 #include <color/wave.hpp>
6 #include <common.hpp>
7 #include <math3d/vec3.hpp>
8 #include <utils.hpp>
9 
10 using namespace ptracey;
11 namespace ptracey {
12 //
13 Power averageSpectrum(const sampled_wave<Power> &powers,
14  const std::vector<WaveLength> &waves,
15  WaveLength waveLStart,
16  WaveLength waveLEnd) {
17  // from
18  // https://github.com/mmp/pbrt-v3/blob/master/src/core/spectrum.cpp
19 
20  uint end = (uint)waves.size() - 1;
21  if (waveLStart <= waves[0])
22  return powers[0];
23  if (waveLEnd >= waves[end])
24  return powers[end];
25  if ((uint)waves.size() == 1)
26  return powers[0];
27  //
28  Power psum = 0.0;
29  if (waveLStart < waves[0])
30  psum += powers[0] * (waves[0] - waveLStart);
31 
32  if (waveLEnd > waves[end])
33  psum += powers[end] * (waveLEnd - waves[end]);
34  //
35  uint i = 0;
36  while (waveLStart > waves[i + 1])
37  i++;
38  auto wsize = (uint)waves.size();
39  COMP_CHECK((i + 1) < wsize, (i + 1), wsize);
40  auto interpSeg = [waves, powers](auto w, uint j) {
41  auto t1 = (w - waves[j]) / (waves[j + 1] - waves[j]);
42  auto t2 = powers[j];
43  auto t3 = powers[j + 1];
44  return mix(t1, t2, t3);
45  };
46  //
47  for (uint j = i;
48  j + 1 < (uint)waves.size() && waveLEnd >= waves[j];
49  j++) {
50  auto segLambdaStart = std::max(waveLStart, waves[j]);
51  auto segLambdaEnd = std::min(waveLEnd, waves[j + 1]);
52  auto interp_seg_start = interpSeg(segLambdaStart, j);
53  auto interp_seg_end = interpSeg(segLambdaEnd, j);
54  psum += 0.5 * (interp_seg_start + interp_seg_end) *
55  (segLambdaEnd - segLambdaStart);
56  }
57  return psum / (waveLEnd - waveLStart);
58 }
59 
60 Power averageSpectrum(const spd &in_spd,
61  WaveLength waveLStart,
62  WaveLength waveLEnd) {
63  auto powers2 = in_spd.powers();
64  auto waves = in_spd.wavelengths();
65  return averageSpectrum(powers2, waves, waveLStart,
66  waveLEnd);
67 }
68 
69 void resample_wave_power(
70  const sampled_wave<Power> &in_powers,
71  const std::vector<WaveLength> &in_waves,
72  std::vector<Power> &out_powers,
73  std::vector<WaveLength> &out_waves,
74  const WaveLength &waveLStart,
75  const WaveLength &waveLEnd, const uint &out_size) {
76  out_waves.clear();
77  out_powers.clear();
78  //
79  uint wsize = (uint)in_waves.size();
80  COMP_CHECK(wsize > 2, wsize, 2);
81  auto miwave = waveLStart;
82  auto mwave = waveLEnd;
83  auto waves = in_waves;
84  auto pwrs = in_powers;
85  //
86  WaveLength delta = (mwave - miwave) / (wsize - 1);
87 
88  //
89  auto wlstartClamp = [miwave, mwave, waves, wsize,
90  delta](int index) -> WaveLength {
91  //
92  COMP_CHECK((index >= -1) && (index <= (int)wsize),
93  index, wsize);
94  if (index == -1) {
95  //
96  COMP_CHECK(miwave - delta < waves[0], miwave - delta,
97  waves[0]);
98  return miwave - delta;
99  } else if (index == wsize) {
100  COMP_CHECK(mwave + delta > waves[wsize - 1],
101  mwave + delta, waves[wsize - 1]);
102  return mwave + delta;
103  }
104  return waves[index];
105  };
106  auto pwClamped = [wsize, pwrs](int index) -> Power {
107  COMP_CHECK(index >= -1 && index <= (int)wsize, index,
108  wsize);
109  return pwrs[dclamp<uint>(index, 0, wsize - 1)];
110  };
111  //
112  auto samplingfn = [waves, pwrs, delta, wsize, pwClamped,
113  wlstartClamp](WaveLength wl) -> Power {
114  if (wl + delta / 2 <= waves[0])
115  return pwrs[0];
116  if (wl - delta / 2 >= waves[wsize - 1])
117  return pwrs[wsize - 1];
118  //
119  if (wsize == 1)
120  return pwrs[0];
121  //
122 
123  int start, end;
124 
125  if (wl - delta < waves[0]) {
126  start = -1;
127  } else {
128  auto intervalfn = [waves, delta, wl](int i) -> bool {
129  return waves[i] <= (wl - delta);
130  };
131  start = findInterval((int)wsize, intervalfn);
132  bool sb1 = start >= 0;
133  bool sb2 = start < (int)wsize;
134  COMP_CHECK(sb1 && sb2, start, wsize);
135  }
136  //
137  if (wl + delta > waves[wsize - 1]) {
138  end = (int)wsize;
139  } else {
140  end = start > 0 ? start : 0;
141  while (end < (int)wsize && wl + delta > waves[end])
142  end++;
143  }
144  bool cond1 = end - start == 2;
145  bool cond2 = (wlstartClamp(start) <= (wl - delta));
146  bool cond3 = waves[start + 1] == wl;
147  bool cond4 = wlstartClamp(end) >= wl + delta;
148  if (cond1 && cond2 && cond3 && cond4) {
149  return pwrs[start + 1];
150  } else if (end - start == 1) {
151  //
152  WaveLength t =
153  (wl - wlstartClamp(start)) /
154  (wlstartClamp(end) - wlstartClamp(start));
155  COMP_CHECK(t >= 0 && t <= 1, t, t);
156  return mix(t, pwClamped(start), pwClamped(end));
157  } else {
158  return averageSpectrum(pwrs, waves, wl - delta / 2,
159  wl + delta / 2);
160  }
161  };
162 
163  for (int outOffset = 0; outOffset < out_size;
164  outOffset++) {
165  //
166  auto wave = mix<WaveLength>(WaveLength(outOffset) /
167  (out_size - 1),
168  waveLStart, waveLEnd);
169  out_waves.push_back(wave);
170  Power pwr = samplingfn(wave);
171  out_powers.push_back(pwr);
172  }
173 }
174 
175 spd spd::resample_c(const spd &s) const {
176  //
177  auto waves1 = wavelengths();
178  auto waves2 = s.wavelengths();
179  auto powers1 = powers();
180  auto powers2 = s.powers();
181 
182  auto wsize1 = waves1.size();
183  auto wsize2 = waves2.size();
184  auto wsize = wsize1 > wsize2 ? wsize1 : wsize2;
185  COMP_CHECK(wsize > 2, wsize, 2);
186  //
187  auto waves = wsize == wsize1 ? waves1 : waves2;
188  auto pwrs = wsize == wsize1 ? powers1 : powers2;
189  //
190  auto mwave1 = max_wave();
191  auto mwave2 = s.max_wave();
192  auto mwave = mwave1 > mwave2 ? mwave1 : mwave2;
193  //
194  auto miwave1 = min_wave();
195  auto miwave2 = s.min_wave();
196  auto miwave = miwave1 > miwave2 ? miwave1 : miwave2;
197  //
198  std::vector<Power> out_powers;
199  std::vector<WaveLength> out_waves;
200  resample_wave_power(pwrs, waves, out_powers, out_waves,
201  miwave, mwave, wsize);
202  sampled_wave<Power> spowers(out_powers);
203  auto sp = spd(spowers, out_waves);
204  return sp;
205 }
206 spd spd::resample_c(const WaveLength &waveLStart,
207  const WaveLength &waveLEnd,
208  const uint &outSize) const {
209  //
210  auto waves = wavelengths();
211  auto spowers = powers();
212  std::vector<Power> out_powers;
213  std::vector<WaveLength> out_waves;
214  resample_wave_power(spowers, waves, out_powers, out_waves,
215  waveLStart, waveLEnd, outSize);
216  sampled_wave<Power> sspowers(out_powers);
217  auto sp = spd(sspowers, out_waves);
218  return sp;
219 }
220 spd spd::resample_c(const uint &outSize) const {
221  auto miwave = min_wave();
222  auto mwave = max_wave();
223  auto sp = resample_c(miwave, mwave, outSize);
224  return sp;
225 }
226 
227 void spd::resample(const spd &s) { *this = resample_c(s); }
228 void spd::resample(const WaveLength &waveLStart,
229  const WaveLength &waveLEnd,
230  const uint &outSize) {
231  *this = resample_c(waveLStart, waveLEnd, outSize);
232 }
233 
234 //
235 
236 spd rgb_to_spect(std::vector<Real> rgb_wavelength,
237  std::vector<Real> rgb_spect) {
238  //
239  COMP_CHECK(rgb_spect.size() == rgb_wavelength.size(),
240  rgb_spect.size(), rgb_wavelength.size());
241  std::vector<WaveLength> waves =
242  cast_vec<Real, WaveLength>(
243  rgb_wavelength, [](auto rgbw) {
244  return static_cast<WaveLength>(rgbw);
245  });
246  WaveLength mx_wave = *std::max_element(
247  rgb_wavelength.begin(), rgb_wavelength.end());
248  WaveLength mn_wave = *std::min_element(
249  rgb_wavelength.begin(), rgb_wavelength.end());
250 
251  std::vector<Power> powers =
252  cast_vec<Real, Power>(rgb_spect, [](auto rgbp) {
253  return static_cast<Power>(rgbp);
254  });
255  sampled_wave sampled_powers(powers);
256  std::vector<Power> pwrs;
257  std::vector<WaveLength> wvs;
258  for (uint i = 0; i < SPD_NB_SAMPLE; i++) {
259  WaveLength wl0 = mix<WaveLength>(
260  WaveLength(i) / WaveLength(SPD_NB_SAMPLE),
261  VISIBLE_LAMBDA_START, VISIBLE_LAMBDA_END);
262  WaveLength wl1 = mix<WaveLength>(
263  WaveLength(i + 1) / WaveLength(SPD_NB_SAMPLE),
264  VISIBLE_LAMBDA_START, VISIBLE_LAMBDA_END);
265  Power p =
266  averageSpectrum(sampled_powers, waves, wl0, wl1);
267  pwrs.push_back(p);
268  wvs.push_back((wl0 + wl1) / 2);
269  }
270  sampled_wave spower_s(pwrs);
271 
272  auto sp = spd(spower_s, wvs);
273  return sp;
274 }
275 
276 static spd rgbRefl2SpectWhite =
277  rgb_to_spect(RGB2SpectLambda, RGBRefl2SpectWhite);
278 
279 static spd rgbRefl2SpectCyan =
280  rgb_to_spect(RGB2SpectLambda, RGBRefl2SpectCyan);
281 
282 static spd rgbRefl2SpectMagenta =
283  rgb_to_spect(RGB2SpectLambda, RGBRefl2SpectMagenta);
284 
285 static spd rgbRefl2SpectYellow =
286  rgb_to_spect(RGB2SpectLambda, RGBRefl2SpectYellow);
287 
288 static spd rgbRefl2SpectRed =
289  rgb_to_spect(RGB2SpectLambda, RGBRefl2SpectRed);
290 
291 static spd rgbRefl2SpectGreen =
292  rgb_to_spect(RGB2SpectLambda, RGBRefl2SpectGreen);
293 
294 static spd rgbRefl2SpectBlue =
295  rgb_to_spect(RGB2SpectLambda, RGBRefl2SpectBlue);
296 
297 static spd rgbIllum2SpectWhite =
298  rgb_to_spect(RGB2SpectLambda, RGBIllum2SpectWhite);
299 
300 static spd rgbIllum2SpectCyan =
301  rgb_to_spect(RGB2SpectLambda, RGBIllum2SpectCyan);
302 
303 static spd rgbIllum2SpectBlue =
304  rgb_to_spect(RGB2SpectLambda, RGBIllum2SpectBlue);
305 
306 static spd rgbIllum2SpectGreen =
307  rgb_to_spect(RGB2SpectLambda, RGBIllum2SpectGreen);
308 
309 static spd rgbIllum2SpectRed =
310  rgb_to_spect(RGB2SpectLambda, RGBIllum2SpectRed);
311 
312 static spd rgbIllum2SpectMagenta =
313  rgb_to_spect(RGB2SpectLambda, RGBIllum2SpectMagenta);
314 
315 static spd rgbIllum2SpectYellow =
316  rgb_to_spect(RGB2SpectLambda, RGBIllum2SpectYellow);
317 
318 static spd Xspd = rgb_to_spect(CIE_LAMBDA_REAL, CIE_X);
319 
320 static spd Yspd = rgb_to_spect(CIE_LAMBDA_REAL, CIE_Y);
321 
322 static spd Zspd = rgb_to_spect(CIE_LAMBDA_REAL, CIE_Z);
323 
324 //
325 }
ptracey::sampled_wave
a wave sample representing a continuous wave in discreet form
Definition: wave.hpp:12
ptracey::sampled_wave::size
uint size() const
find the number of samples inside this wave
Definition: wave.hpp:486
ptracey::spd
Definition: spd.hpp:13