Path Tracer
sampled_spectrum.hpp
1 #pragma once
2 // sampled spectrum object
3 #include <color/color.hpp>
4 #include <color/colorable.hpp>
5 #include <color/pbr_spectrum.hpp>
6 #include <color/spd.hpp>
7 #include <color/specdata.hpp>
8 #include <color/specutils.hpp>
9 #include <color/wave.hpp>
10 #include <common.hpp>
11 #include <math3d/vec3.hpp>
12 #include <utils.hpp>
13 
14 using namespace ptracey;
15 namespace ptracey {
16 
17 spd FromRGB(const vec3 &rgb, SpectrumType type) {
18  spd r = spd::zeros_like(rgbRefl2SpectWhite);
19  if (type == SpectrumType::Reflectance) {
20  // Convert reflectance spectrum to RGB
21  if (rgb.x() <= rgb.y() && rgb.x() <= rgb.z()) {
22  // Compute reflectance _SampledSpectrum_ with _rgb[0]_
23  // as minimum
24  r += rgb.x() * rgbRefl2SpectWhite.normalized();
25  if (rgb.y() <= rgb.z()) {
26  r += (rgb.y() - rgb.x()) *
27  rgbRefl2SpectCyan.normalized();
28  r += (rgb.z() - rgb.y()) *
29  rgbRefl2SpectBlue.normalized();
30  } else {
31  r += (rgb.z() - rgb.x()) *
32  rgbRefl2SpectCyan.normalized();
33  r += (rgb.y() - rgb.z()) *
34  rgbRefl2SpectGreen.normalized();
35  }
36  } else if (rgb.y() <= rgb.x() && rgb.y() <= rgb.z()) {
37  // Compute reflectance _SampledSpectrum_ with _rgb[1]_
38  // as minimum
39  r += rgb.y() * rgbRefl2SpectWhite.normalized();
40  if (rgb.x() <= rgb.z()) {
41  r += (rgb.x() - rgb.y()) *
42  rgbRefl2SpectMagenta.normalized();
43  r += (rgb.z() - rgb.x()) *
44  rgbRefl2SpectBlue.normalized();
45  } else {
46  r += (rgb.z() - rgb.y()) *
47  rgbRefl2SpectMagenta.normalized();
48  r += (rgb.x() - rgb.z()) *
49  rgbRefl2SpectRed.normalized();
50  }
51  } else {
52  // Compute reflectance _SampledSpectrum_ with _rgb[2]_
53  // as minimum
54  r += rgb.z() * rgbRefl2SpectWhite.normalized();
55  if (rgb.x() <= rgb.y()) {
56  r += (rgb.x() - rgb.z()) *
57  rgbRefl2SpectYellow.normalized();
58  r += (rgb.y() - rgb.x()) *
59  rgbRefl2SpectGreen.normalized();
60  } else {
61  r += (rgb.y() - rgb.z()) *
62  rgbRefl2SpectYellow.normalized();
63  r += (rgb.x() - rgb.y()) *
64  rgbRefl2SpectRed.normalized();
65  }
66  }
67  r *= .94;
68  } else {
69  // Convert illuminant spectrum to RGB
70  if (rgb.x() <= rgb.y() && rgb.x() <= rgb.z()) {
71  // Compute illuminant _SampledSpectrum_ with _rgb[0]_
72  // as minimum
73  r += rgb.x() * rgbIllum2SpectWhite.normalized();
74  if (rgb.y() <= rgb.z()) {
75  r += (rgb.y() - rgb.x()) *
76  rgbIllum2SpectCyan.normalized();
77  r += (rgb.z() - rgb.y()) *
78  rgbIllum2SpectBlue.normalized();
79  } else {
80  r += (rgb.z() - rgb.x()) *
81  rgbIllum2SpectCyan.normalized();
82  r += (rgb.y() - rgb.z()) *
83  rgbIllum2SpectGreen.normalized();
84  }
85  } else if (rgb.y() <= rgb.x() && rgb.y() <= rgb.z()) {
86  // Compute illuminant _SampledSpectrum_ with _rgb[1]_
87  // as minimum
88  r += rgb.y() * rgbIllum2SpectWhite.normalized();
89  if (rgb.x() <= rgb.z()) {
90  r += (rgb.x() - rgb.y()) *
91  rgbIllum2SpectMagenta.normalized();
92  r += (rgb.z() - rgb.x()) *
93  rgbIllum2SpectBlue.normalized();
94  } else {
95  r += (rgb.z() - rgb.y()) *
96  rgbIllum2SpectMagenta.normalized();
97  r += (rgb.x() - rgb.z()) *
98  rgbIllum2SpectRed.normalized();
99  }
100  } else {
101  // Compute illuminant _SampledSpectrum_ with _rgb.z()_
102  // as minimum
103  r += rgb.z() * rgbIllum2SpectWhite.normalized();
104  if (rgb.x() <= rgb.y()) {
105  r += (rgb.x() - rgb.z()) *
106  rgbIllum2SpectYellow.normalized();
107  r += (rgb.y() - rgb.x()) *
108  rgbIllum2SpectGreen.normalized();
109  } else {
110  r += (rgb.y() - rgb.z()) *
111  rgbIllum2SpectYellow.normalized();
112  r += (rgb.x() - rgb.y()) *
113  rgbIllum2SpectRed.normalized();
114  }
115  }
116  r *= 0.86445;
117  }
118  auto sp = r.clamp();
119  auto minw = sp.min_wave();
120  auto maxw = sp.max_wave();
121  sp.resample(VISIBLE_LAMBDA_START, VISIBLE_LAMBDA_END,
122  SPD_NB_SAMPLE);
123  return sp;
124 }
125 
126 class sampled_spectrum : public colorable {
127 public:
128  spd spect; //
129  //
130  spd sX, sY, sZ;
131 
132  // spd sRgbRefl2SpectWhite, sRgbRefl2SpectCyan;
133  // spd sRgbRefl2SpectMagenta, sRgbRefl2SpectYellow;
134  // spd sRgbRefl2SpectRed, sRgbRefl2SpectGreen;
135  // spd sRgbRefl2SpectBlue;
136  // spd sRgbIllum2SpectWhite, sRgbIllum2SpectCyan;
137  // spd sRgbIllum2SpectMagenta, sRgbIllum2SpectYellow;
138  // spd sRgbIllum2SpectRed, sRgbIllum2SpectGreen;
139  // spd sRgbIllum2SpectBlue;
140 
141  SpectrumType type;
142 
143 public:
145  : spect(spd()), type(SpectrumType::Reflectance) {}
147  const path &csv_path,
148  const std::string &wave_col_name = "wavelength",
149  const std::string &power_col_name = "power",
150  const char &sep = ',',
151  const unsigned int stride = SPD_STRIDE,
152  SpectrumType stype = SpectrumType::Reflectance)
153  : spect(spd(csv_path, wave_col_name, power_col_name,
154  sep, stride)),
155  type(stype) {
156  Init();
157  }
158  sampled_spectrum(const spd &s_lambda, SpectrumType stype)
159  : spect(s_lambda), type(stype) {}
161  const Real &r, const Real &g, const Real &b,
162  SpectrumType stype = SpectrumType::Reflectance)
163  : type(stype) {
164  spect = from_rgb(rgb_model(r, g, b));
165  Init();
166  }
168  const Real &r,
169  SpectrumType stype = SpectrumType::Reflectance)
170  : type(stype) {
171  spect = from_rgb(rgb_model(r, r, r));
172  Init();
173  }
175  const rgb_model &_rgb,
176  SpectrumType stype = SpectrumType::Reflectance)
177  : type(stype) {
178  spect = from_rgb(_rgb);
179  }
181  const vec3 &_rgb,
182  SpectrumType stype = SpectrumType::Reflectance)
183  : type(stype) {
184  spect = from_rgb(_rgb);
185  Init();
186  }
187  spd _from_rgb(const vec3 &rgb) {
188  // from
189  // http://scottburns.us/fast-rgb-to-spectrum-conversion-for-reflectances/
190  // convert sRGB to linear rgb in range [0,1]
191  Real rgbm = rgb.max();
192  Real ins, ine;
193  if (rgbm > 1.0) {
194  ins = 0.0;
195  ine = 256.0;
196  } else {
197  ins = 0.0;
198  ine = 1.0;
199  }
200  auto lr = interp<Real>(rgb.r(), ins, ine, 0.0, 1.0);
201  auto lg = interp<Real>(rgb.g(), ins, ine, 0.0, 1.0);
202  auto lb = interp<Real>(rgb.b(), ins, ine, 0.0, 1.0);
203  //
204  auto rho_r_wave = rho_r.powers();
205  auto rho_g_wave = rho_g.powers();
206  auto rho_b_wave = rho_b.powers();
207  auto rho = lr * rho_r_wave;
208  rho += lg * rho_g_wave;
209  rho += lb * rho_b_wave;
210  return spd(rho, rho_r.wavelengths());
211  }
212 
213  spd from_rgb(const vec3 &rgb) {
214  //
215  return FromRGB(rgb, type);
216  }
217  spd from_rgb(const rgb_model &rgb) {
218  return from_rgb(vec3(rgb.r(), rgb.g(), rgb.b()));
219  }
220  static sampled_spectrum
221  random(SpectrumType stype = SpectrumType::Reflectance) {
222  auto sp1 = spd::random();
223  return sampled_spectrum(sp1, stype);
224  }
225  static sampled_spectrum
226  random(Real mn, Real mx,
227  SpectrumType stype = SpectrumType::Reflectance) {
228  auto sp1 = spd::random(mn, mx);
229  return sampled_spectrum(sp1, stype);
230  }
231  void insert(unsigned int wavelength, Real power) {
232  spect.insert(wavelength, power);
233  }
234  vec3 to_xyz() const override {
235  vec3 xyz(0.0);
236  auto waves = spect.wavelengths();
237  for (auto wave : waves) {
238  xyz.e[0] += sX[wave] * spect[wave];
239  xyz.e[1] += sY[wave] * spect[wave];
240  xyz.e[2] += sZ[wave] * spect[wave];
241  }
242  auto wsize = (uint)waves.size();
243  auto scale = (spect.wave_end - spect.wave_start) /
244  Real(CIE_Y_integral * wsize);
245  xyz *= scale;
246  return xyz;
247  }
248  vec3 _to_xyz() const {
249  //
250  vec3 xyz(0.0);
251  if (type == SpectrumType::Reflectance) {
252  get_cie_values(standard_d65, spect, xyz);
253  } else {
254  get_cie_values(spect, xyz);
255  }
256  return xyz;
257  }
258  Power evaluate(const WaveLength &w) const {
259  return spect[w];
260  }
261  void add(const color &r_color, const WaveLength &w) {
262  spect.add(w, r_color.pdata);
263  }
264  void scale(Real coeff) { spect *= coeff; }
265  vec3 to_rgb() const override {
266  vec3 xyz = to_xyz();
267  vec3 rgb = xyz2rgb_pbr(xyz);
268  return rgb;
269  }
270  void Init() {
271  //
272  auto wsize = (uint)spect.wavelengths().size();
273  auto wstart = spect.wave_start;
274  auto wend = spect.wave_end;
275  sX = Xspd.resample_c(wstart, wend, wsize).normalized();
276  sY = Yspd.resample_c(wstart, wend, wsize).normalized();
277  sZ = Zspd.resample_c(wstart, wend, wsize).normalized();
278 
279  // sRgbRefl2SpectWhite =
280  // spd::rgbRefl2SpectWhite.resample_c(wstart, wend,
281  // wsize);
282  // sRgbRefl2SpectBlue =
283  // spd::rgbRefl2SpectBlue.resample_c(
284  // wstart, wend, wsize);
285  // sRgbRefl2SpectGreen =
286  // spd::rgbRefl2SpectGreen.resample_c(wstart, wend,
287  // wsize);
288  // sRgbRefl2SpectRed = spd::rgbRefl2SpectRed.resample_c(
289  // wstart, wend, wsize);
290 
291  // sRgbRefl2SpectMagenta =
292  // spd::rgbRefl2SpectMagenta.resample_c(wstart,
293  // wend,
294  // wsize);
295  // sRgbRefl2SpectCyan =
296  // spd::rgbRefl2SpectCyan.resample_c(
297  // wstart, wend, wsize);
298 
299  // sRgbIllum2SpectWhite =
300  // spd::rgbIllum2SpectWhite.resample_c(wstart, wend,
301  // wsize);
302  // sRgbIllum2SpectBlue =
303  // spd::rgbIllum2SpectBlue.resample_c(wstart, wend,
304  // wsize);
305  // sRgbIllum2SpectGreen =
306  // spd::rgbIllum2SpectGreen.resample_c(wstart, wend,
307  // wsize);
308  // sRgbIllum2SpectRed =
309  // spd::rgbIllum2SpectRed.resample_c(
310  // wstart, wend, wsize);
311 
312  // sRgbIllum2SpectMagenta =
313  // spd::rgbIllum2SpectMagenta.resample_c(wstart,
314  // wend,
315  // wsize);
316  // sRgbIllum2SpectCyan =
317  // spd::rgbIllum2SpectCyan.resample_c(wstart, wend,
318  // wsize);
319  }
320 };
321 inline std::ostream &
322 operator<<(std::ostream &out, const sampled_spectrum &ss) {
323  return out << ss.spect << std::endl;
324 }
325 }
ptracey::colorable
Definition: colorable.hpp:9
ptracey::vec3
Definition: vec3.hpp:7
ptracey::sampled_spectrum
Definition: sampled_spectrum.hpp:126
ptracey::spd
Definition: spd.hpp:13
ptracey::color
Definition: color.hpp:9
ptracey::rgb_model
Definition: rgb_model.hpp:13