1
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2019 Esteban Tovagliari, The appleseedhq Organization
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining a copy
11 // of this software and associated documentation files (the "Software"), to deal
12 // in the Software without restriction, including without limitation the rights
13 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 // copies of the Software, and to permit persons to whom the Software is
15 // furnished to do so, subject to the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be included in
18 // all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 // THE SOFTWARE.
27 //
28
29 #pragma once
30
31 // appleseed.foundation headers.
32 #include "foundation/image/color.h"
33 #include "foundation/image/colorspace.h"
34 #include "foundation/image/regularspectrum.h"
35 #include "foundation/math/fp.h"
36 #include "foundation/math/scalar.h"
37 #include "foundation/platform/compiler.h"
38 #ifdef APPLESEED_USE_SSE
39 #include "foundation/platform/sse.h"
40 #endif
41 #include "foundation/platform/types.h"
42 #include "foundation/utility/poison.h"
43
44 // Standard headers.
45 #include <algorithm>
46 #include <cassert>
47 #include <cmath>
48 #include <cstddef>
49
50 namespace renderer
51 {
52
53 //
54 // ...
55 //
56
57 template <typename T>
58 class RGBSpectrum
59 {
60 public:
61 // Value type and number of samples.
62 typedef T ValueType;
63 static const size_t Samples = 3;
64
65 // Number of stored samples such that the size of the sample array is a multiple of 16 bytes.
66 static const size_t StoredSamples = (((3 * sizeof(T)) + 15) & ~15) / sizeof(T);
67
68 enum Mode
69 {
70 RGB = 0, // RGBSpectrum stores and operates on RGB triplets
71 };
72
73 enum Intent
74 {
75 Reflectance = 0, // this spectrum represents a reflectance in [0, 1]^N
76 Illuminance = 1 // this spectrum represents an illuminance in [0, infinity)^N
77 };
78
79 // Change the current thread-local spectrum mode. Return the previous mode.
80 static Mode set_mode(const Mode mode);
81
82 // Return the current thread-local spectrum mode.
83 static Mode get_mode();
84
85 // Return the number of active color channels for the current spectrum mode.
86 static size_t size();
87
88 // Constructors.
89 #ifdef APPLESEED_USE_SSE
90 RGBSpectrum(); // leave all components uninitialized
91 #else
92 #if !defined(_MSC_VER) || _MSC_VER >= 1800
93 RGBSpectrum() = default; // leave all components uninitialized
94 #else
RGBSpectrum()95 RGBSpectrum() {} // leave all components uninitialized
96 #endif
97 #endif
98 explicit RGBSpectrum(const ValueType val); // set all components to `val`
99 RGBSpectrum(
100 const foundation::Color<ValueType, 3>& rgb,
101 const foundation::LightingConditions& lighting_conditions,
102 const Intent intent);
103
104 template<size_t N>
105 RGBSpectrum(
106 const foundation::RegularSpectrum<ValueType, N>& spectrum,
107 const foundation::LightingConditions& lighting_conditions,
108 const Intent intent);
109
110 // Construct a spectrum from another spectrum of a different type.
111 template <typename U>
112 RGBSpectrum(const RGBSpectrum<U>& rhs);
113
114 // Construct a spectrum from an array of `3` scalars.
115 static RGBSpectrum from_array(const ValueType* rhs);
116
117 // Set all components to a given value.
118 void set(const ValueType val);
119
120 // Initialize the spectrum from a linear RGB value.
121 void set(
122 const foundation::Color<ValueType, 3>& rgb,
123 const foundation::LightingConditions& lighting_conditions,
124 const Intent intent);
125
126 // Initialize the spectrum from a spectral value.
127 template <size_t N>
128 void set(
129 const foundation::RegularSpectrum<ValueType, N>& spectrum,
130 const foundation::LightingConditions& lighting_conditions,
131 const Intent intent);
132
133 // Unchecked array subscripting.
134 ValueType& operator[](const size_t i);
135 const ValueType& operator[](const size_t i) const;
136
137 // Convert the spectrum to a linear RGB color.
138 foundation::Color<ValueType, 3> to_rgb(
139 const foundation::LightingConditions& lighting_conditions) const;
140
141 // Convert the spectrum to a CIE XYZ color.
142 foundation::Color<ValueType, 3> to_ciexyz(
143 const foundation::LightingConditions& lighting_conditions) const;
144
145 private:
146 APPLESEED_SIMD4_ALIGN ValueType m_samples[StoredSamples];
147 };
148
149 // Exact inequality and equality tests.
150 template <typename T> bool operator!=(const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
151 template <typename T> bool operator==(const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
152
153 // Spectrum arithmetic.
154 template <typename T> RGBSpectrum<T> operator+ (const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
155 template <typename T> RGBSpectrum<T> operator- (const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
156 template <typename T> RGBSpectrum<T> operator- (const RGBSpectrum<T>& lhs);
157 template <typename T> RGBSpectrum<T> operator* (const RGBSpectrum<T>& lhs, const T rhs);
158 template <typename T> RGBSpectrum<T> operator* (const T lhs, const RGBSpectrum<T>& rhs);
159 template <typename T> RGBSpectrum<T> operator* (const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
160 template <typename T> RGBSpectrum<T> operator/ (const RGBSpectrum<T>& lhs, const T rhs);
161 template <typename T> RGBSpectrum<T> operator/ (const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
162 template <typename T> RGBSpectrum<T>& operator+=(RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
163 template <typename T> RGBSpectrum<T>& operator-=(RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
164 template <typename T> RGBSpectrum<T>& operator*=(RGBSpectrum<T>& lhs, const T rhs);
165 template <typename T> RGBSpectrum<T>& operator*=(RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
166 template <typename T> RGBSpectrum<T>& operator/=(RGBSpectrum<T>& lhs, const T rhs);
167 template <typename T> RGBSpectrum<T>& operator/=(RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
168
169 // Multiply-add: a = a + b * c.
170 template <typename T> void madd(RGBSpectrum<T>& a, const RGBSpectrum<T>& b, const RGBSpectrum<T>& c);
171 template <typename T> void madd(RGBSpectrum<T>& a, const RGBSpectrum<T>& b, const T c);
172
173
174 //
175 // Full specializations for spectra of type float and double.
176 //
177
178 typedef RGBSpectrum<float> RGBSpectrumf;
179 typedef RGBSpectrum<double> RGBSpectrumd;
180
181 } // namespace renderer
182
183 namespace foundation
184 {
185
186 // Return whether all components of a spectrum are exactly zero.
187 template <typename T> bool is_zero(const renderer::RGBSpectrum<T>& s);
188
189 // Approximate equality tests.
190 template <typename T> bool feq(const renderer::RGBSpectrum<T>& lhs, const renderer::RGBSpectrum<T>& rhs);
191 template <typename T> bool feq(const renderer::RGBSpectrum<T>& lhs, const renderer::RGBSpectrum<T>& rhs, const T eps);
192
193 // Approximate zero tests.
194 template <typename T> bool fz(const renderer::RGBSpectrum<T>& s);
195 template <typename T> bool fz(const renderer::RGBSpectrum<T>& s, const T eps);
196
197 // Component-wise reciprocal.
198 template <typename T> renderer::RGBSpectrum<T> rcp(const renderer::RGBSpectrum<T>& s);
199
200 // Return whether all components of a spectrum are in [0,1].
201 template <typename T> bool is_saturated(const renderer::RGBSpectrum<T>& s);
202
203 // Clamp the argument to [0,1].
204 template <typename T> renderer::RGBSpectrum<T> saturate(const renderer::RGBSpectrum<T>& s);
205 template <typename T> void saturate_in_place(renderer::RGBSpectrum<T>& s);
206
207 // Clamp the argument to [min, max].
208 template <typename T> renderer::RGBSpectrum<T> clamp(const renderer::RGBSpectrum<T>& s, const T min, const T max);
209 template <typename T> void clamp_in_place(renderer::RGBSpectrum<T>& s, const T min, const T max);
210
211 // Clamp the argument to [min, +infinity).
212 template <typename T> renderer::RGBSpectrum<T> clamp_low(const renderer::RGBSpectrum<T>& s, const T min);
213 template <typename T> void clamp_low_in_place(renderer::RGBSpectrum<T>& s, const T min);
214
215 // Clamp the argument to (-infinity, max].
216 template <typename T> renderer::RGBSpectrum<T> clamp_high(const renderer::RGBSpectrum<T>& s, const T max);
217 template <typename T> void clamp_high_in_place(renderer::RGBSpectrum<T>& s, const T max);
218
219 // Component-wise linear interpolation between a and b.
220 template <typename T> renderer::RGBSpectrum<T> lerp(
221 const renderer::RGBSpectrum<T>& a,
222 const renderer::RGBSpectrum<T>& b,
223 const renderer::RGBSpectrum<T>& t);
224
225 // Return the smallest or largest signed component of a spectrum.
226 template <typename T> T min_value(const renderer::RGBSpectrum<T>& s);
227 template <typename T> T max_value(const renderer::RGBSpectrum<T>& s);
228
229 // Return the index of the smallest or largest signed component of a spectrum.
230 template <typename T> size_t min_index(const renderer::RGBSpectrum<T>& s);
231 template <typename T> size_t max_index(const renderer::RGBSpectrum<T>& s);
232
233 // Return the index of the smallest or largest component of a spectrum, in absolute value.
234 template <typename T> size_t min_abs_index(const renderer::RGBSpectrum<T>& s);
235 template <typename T> size_t max_abs_index(const renderer::RGBSpectrum<T>& s);
236
237 // Return the sum of the components of a spectrum.
238 template <typename T> T sum_value(const renderer::RGBSpectrum<T>& s);
239
240 // Return the average value of a spectrum.
241 template <typename T> T average_value(const renderer::RGBSpectrum<T>& s);
242
243 // Return true if a spectrum contains at least one NaN value.
244 template <typename T> bool has_nan(const renderer::RGBSpectrum<T>& s);
245
246 // Return true if all components of a spectrum are finite (not NaN, not infinite).
247 template <typename T> bool is_finite(const renderer::RGBSpectrum<T>& s);
248
249 // Return the square root of a spectrum.
250 template <typename T> renderer::RGBSpectrum<T> sqrt(const renderer::RGBSpectrum<T>& s);
251
252 // Raise a spectrum to a given power.
253 template <typename T> renderer::RGBSpectrum<T> pow(const renderer::RGBSpectrum<T>& x, const T y);
254
255 // Raise a spectrum to a given power, component-wise.
256 template <typename T> renderer::RGBSpectrum<T> pow(
257 const renderer::RGBSpectrum<T>& x,
258 const renderer::RGBSpectrum<T>& y);
259
260 // Compute the logarithm of a spectrum.
261 template <typename T> renderer::RGBSpectrum<T> log(const renderer::RGBSpectrum<T>& s);
262
263 // Compute the exponential of a spectrum.
264 template <typename T> renderer::RGBSpectrum<T> exp(const renderer::RGBSpectrum<T>& s);
265
266 } // namespace foundation
267
268
269 //
270 // RGBSpectrum class implementation.
271 //
272
273 namespace renderer
274 {
275
276 template <typename T>
set_mode(const Mode mode)277 typename RGBSpectrum<T>::Mode RGBSpectrum<T>::set_mode(const Mode mode)
278 {
279 return RGB;
280 }
281
282 template <typename T>
get_mode()283 inline typename RGBSpectrum<T>::Mode RGBSpectrum<T>::get_mode()
284 {
285 return RGB;
286 }
287
288 template <typename T>
size()289 inline size_t RGBSpectrum<T>::size()
290 {
291 return 3;
292 }
293
294 #ifdef APPLESEED_USE_SSE
295
296 template <typename T>
RGBSpectrum()297 inline RGBSpectrum<T>::RGBSpectrum()
298 {
299 m_samples[3] = T(0.0);
300 }
301
302 #endif
303
304 template <typename T>
RGBSpectrum(const ValueType val)305 inline RGBSpectrum<T>::RGBSpectrum(const ValueType val)
306 {
307 set(val);
308
309 #ifdef APPLESEED_USE_SSE
310 m_samples[3] = T(0.0);
311 #endif
312 }
313
314 template <typename T>
RGBSpectrum(const foundation::Color<ValueType,3> & rgb,const foundation::LightingConditions & lighting_conditions,const Intent intent)315 inline RGBSpectrum<T>::RGBSpectrum(
316 const foundation::Color<ValueType, 3>& rgb,
317 const foundation::LightingConditions& lighting_conditions,
318 const Intent intent)
319 {
320 set(rgb, lighting_conditions, intent);
321
322 #ifdef APPLESEED_USE_SSE
323 m_samples[3] = T(0.0);
324 #endif
325 }
326
327 template <typename T>
328 template <size_t N>
RGBSpectrum(const foundation::RegularSpectrum<ValueType,N> & spectrum,const foundation::LightingConditions & lighting_conditions,const Intent intent)329 inline RGBSpectrum<T>::RGBSpectrum(
330 const foundation::RegularSpectrum<ValueType, N>& spectrum,
331 const foundation::LightingConditions& lighting_conditions,
332 const Intent intent)
333 {
334 set(spectrum, lighting_conditions, intent);
335
336 #ifdef APPLESEED_USE_SSE
337 m_samples[3] = T(0.0);
338 #endif
339 }
340
341 template <typename T>
342 template <typename U>
RGBSpectrum(const RGBSpectrum<U> & rhs)343 inline RGBSpectrum<T>::RGBSpectrum(const RGBSpectrum<U>& rhs)
344 {
345 for (size_t i = 0; i < 3; ++i)
346 m_samples[i] = static_cast<ValueType>(rhs[i]);
347
348 #ifdef APPLESEED_USE_SSE
349 m_samples[3] = T(0.0);
350 #endif
351 }
352
353 template <typename T>
from_array(const ValueType * rhs)354 inline RGBSpectrum<T> RGBSpectrum<T>::from_array(const ValueType* rhs)
355 {
356 assert(rhs);
357
358 RGBSpectrum result;
359
360 for (size_t i = 0; i < 3; ++i)
361 result.m_samples[i] = rhs[i];
362
363 return result;
364 }
365
366 template <typename T>
set(const ValueType val)367 inline void RGBSpectrum<T>::set(const ValueType val)
368 {
369 for (size_t i = 0; i < 3; ++i)
370 m_samples[i] = val;
371 }
372
373 template <typename T>
set(const foundation::Color<ValueType,3> & rgb,const foundation::LightingConditions & lighting_conditions,const Intent intent)374 void RGBSpectrum<T>::set(
375 const foundation::Color<ValueType, 3>& rgb,
376 const foundation::LightingConditions& lighting_conditions,
377 const Intent intent)
378 {
379 m_samples[0] = rgb[0];
380 m_samples[1] = rgb[1];
381 m_samples[2] = rgb[2];
382 }
383
384 template <typename T>
385 template <size_t N>
set(const foundation::RegularSpectrum<ValueType,N> & spectrum,const foundation::LightingConditions & lighting_conditions,const Intent intent)386 void RGBSpectrum<T>::set(
387 const foundation::RegularSpectrum<ValueType, N>& spectrum,
388 const foundation::LightingConditions& lighting_conditions,
389 const Intent intent)
390 {
391 reinterpret_cast<foundation::Color<T, 3>&>(m_samples[0]) =
392 foundation::ciexyz_to_linear_rgb(
393 foundation::spectrum_to_ciexyz<T>(lighting_conditions, spectrum));
394 }
395
396 template <typename T>
397 inline T& RGBSpectrum<T>::operator[](const size_t i)
398 {
399 assert(i < 3);
400 return m_samples[i];
401 }
402
403 template <typename T>
404 inline const T& RGBSpectrum<T>::operator[](const size_t i) const
405 {
406 assert(i < 3);
407 return m_samples[i];
408 }
409
410 template <typename T>
to_rgb(const foundation::LightingConditions & lighting_conditions)411 inline foundation::Color<T, 3> RGBSpectrum<T>::to_rgb(
412 const foundation::LightingConditions& lighting_conditions) const
413 {
414 return foundation::Color<T, 3>(m_samples[0], m_samples[1], m_samples[2]);
415 }
416
417 template <typename T>
to_ciexyz(const foundation::LightingConditions & lighting_conditions)418 inline foundation::Color<T, 3> RGBSpectrum<T>::to_ciexyz(
419 const foundation::LightingConditions& lighting_conditions) const
420 {
421 return
422 linear_rgb_to_ciexyz(
423 foundation::Color<T, 3>(m_samples[0], m_samples[1], m_samples[2]));
424 }
425
426 template <typename T>
427 inline bool operator!=(const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
428 {
429 for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
430 {
431 if (lhs[i] != rhs[i])
432 return true;
433 }
434
435 return false;
436 }
437
438 template <typename T>
439 inline bool operator==(const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
440 {
441 return !(lhs != rhs);
442 }
443
444 template <typename T>
445 inline RGBSpectrum<T> operator+(const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
446 {
447 RGBSpectrum<T> result;
448
449 for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
450 result[i] = lhs[i] + rhs[i];
451
452 return result;
453 }
454
455 template <typename T>
456 inline RGBSpectrum<T> operator-(const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
457 {
458 RGBSpectrum<T> result;
459
460 for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
461 result[i] = lhs[i] - rhs[i];
462
463 return result;
464 }
465
466 template <typename T>
467 inline RGBSpectrum<T> operator-(const RGBSpectrum<T>& lhs)
468 {
469 RGBSpectrum<T> result;
470
471 for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
472 result[i] = -lhs[i];
473
474 return result;
475 }
476
477 template <typename T>
478 inline RGBSpectrum<T> operator*(const RGBSpectrum<T>& lhs, const T rhs)
479 {
480 RGBSpectrum<T> result;
481
482 for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
483 result[i] = lhs[i] * rhs;
484
485 return result;
486 }
487
488 template <typename T>
489 inline RGBSpectrum<T> operator*(const T lhs, const RGBSpectrum<T>& rhs)
490 {
491 return rhs * lhs;
492 }
493
494 template <typename T>
495 inline RGBSpectrum<T> operator*(const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
496 {
497 RGBSpectrum<T> result;
498
499 for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
500 result[i] = lhs[i] * rhs[i];
501
502 return result;
503 }
504
505 template <typename T>
506 inline RGBSpectrum<T> operator/(const RGBSpectrum<T>& lhs, const T rhs)
507 {
508 RGBSpectrum<T> result;
509
510 for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
511 result[i] = lhs[i] / rhs;
512
513 return result;
514 }
515
516 template <>
517 inline RGBSpectrum<float> operator/(const RGBSpectrum<float>& lhs, const float rhs)
518 {
519 return lhs * (1.0f / rhs);
520 }
521
522 template <size_t N>
523 inline RGBSpectrum<double> operator/(const RGBSpectrum<double>& lhs, const double rhs)
524 {
525 return lhs * (1.0 / rhs);
526 }
527
528 template <size_t N>
529 inline RGBSpectrum<long double> operator/(const RGBSpectrum<long double>& lhs, const long double rhs)
530 {
531 return lhs * (1.0L / rhs);
532 }
533
534 template <typename T>
535 inline RGBSpectrum<T> operator/(const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
536 {
537 RGBSpectrum<T> result;
538
539 for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
540 result[i] = lhs[i] / rhs[i];
541
542 return result;
543 }
544
545 template <typename T>
546 inline RGBSpectrum<T>& operator+=(RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
547 {
548 for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
549 lhs[i] += rhs[i];
550
551 return lhs;
552 }
553
554 #ifdef APPLESEED_USE_SSE
555
556 template <>
557 APPLESEED_FORCE_INLINE RGBSpectrum<float>& operator+=(RGBSpectrum<float>& lhs, const RGBSpectrum<float>& rhs)
558 {
559 _mm_store_ps(&lhs[ 0], _mm_add_ps(_mm_load_ps(&lhs[ 0]), _mm_load_ps(&rhs[ 0])));
560
561 if (RGBSpectrum<float>::size() > 3)
562 {
563 _mm_store_ps(&lhs[ 4], _mm_add_ps(_mm_load_ps(&lhs[ 4]), _mm_load_ps(&rhs[ 4])));
564 _mm_store_ps(&lhs[ 8], _mm_add_ps(_mm_load_ps(&lhs[ 8]), _mm_load_ps(&rhs[ 8])));
565 _mm_store_ps(&lhs[12], _mm_add_ps(_mm_load_ps(&lhs[12]), _mm_load_ps(&rhs[12])));
566 _mm_store_ps(&lhs[16], _mm_add_ps(_mm_load_ps(&lhs[16]), _mm_load_ps(&rhs[16])));
567 _mm_store_ps(&lhs[20], _mm_add_ps(_mm_load_ps(&lhs[20]), _mm_load_ps(&rhs[20])));
568 _mm_store_ps(&lhs[24], _mm_add_ps(_mm_load_ps(&lhs[24]), _mm_load_ps(&rhs[24])));
569 _mm_store_ps(&lhs[28], _mm_add_ps(_mm_load_ps(&lhs[28]), _mm_load_ps(&rhs[28])));
570 }
571
572 return lhs;
573 }
574
575 #endif // APPLESEED_USE_SSE
576
577 template <typename T>
578 inline RGBSpectrum<T>& operator-=(RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
579 {
580 for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
581 lhs[i] -= rhs[i];
582
583 return lhs;
584 }
585
586 template <typename T>
587 inline RGBSpectrum<T>& operator*=(RGBSpectrum<T>& lhs, const T rhs)
588 {
589 for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
590 lhs[i] *= rhs;
591
592 return lhs;
593 }
594
595 #ifdef APPLESEED_USE_SSE
596
597 template <>
598 APPLESEED_FORCE_INLINE RGBSpectrum<float>& operator*=(RGBSpectrum<float>& lhs, const float rhs)
599 {
600 const __m128 mrhs = _mm_set1_ps(rhs);
601
602 _mm_store_ps(&lhs[ 0], _mm_mul_ps(_mm_load_ps(&lhs[ 0]), mrhs));
603
604 if (RGBSpectrum<float>::size() > 3)
605 {
606 _mm_store_ps(&lhs[ 4], _mm_mul_ps(_mm_load_ps(&lhs[ 4]), mrhs));
607 _mm_store_ps(&lhs[ 8], _mm_mul_ps(_mm_load_ps(&lhs[ 8]), mrhs));
608 _mm_store_ps(&lhs[12], _mm_mul_ps(_mm_load_ps(&lhs[12]), mrhs));
609 _mm_store_ps(&lhs[16], _mm_mul_ps(_mm_load_ps(&lhs[16]), mrhs));
610 _mm_store_ps(&lhs[20], _mm_mul_ps(_mm_load_ps(&lhs[20]), mrhs));
611 _mm_store_ps(&lhs[24], _mm_mul_ps(_mm_load_ps(&lhs[24]), mrhs));
612 _mm_store_ps(&lhs[28], _mm_mul_ps(_mm_load_ps(&lhs[28]), mrhs));
613 }
614
615 return lhs;
616 }
617
618 #endif // APPLESEED_USE_SSE
619
620 template <typename T>
621 inline RGBSpectrum<T>& operator*=(RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
622 {
623 for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
624 lhs[i] *= rhs[i];
625
626 return lhs;
627 }
628
629 #ifdef APPLESEED_USE_SSE
630
631 template <>
632 APPLESEED_FORCE_INLINE RGBSpectrum<float>& operator*=(RGBSpectrum<float>& lhs, const RGBSpectrum<float>& rhs)
633 {
634 _mm_store_ps(&lhs[ 0], _mm_mul_ps(_mm_load_ps(&lhs[ 0]), _mm_load_ps(&rhs[ 0])));
635
636 if (RGBSpectrum<float>::size() > 3)
637 {
638 _mm_store_ps(&lhs[ 4], _mm_mul_ps(_mm_load_ps(&lhs[ 4]), _mm_load_ps(&rhs[ 4])));
639 _mm_store_ps(&lhs[ 8], _mm_mul_ps(_mm_load_ps(&lhs[ 8]), _mm_load_ps(&rhs[ 8])));
640 _mm_store_ps(&lhs[12], _mm_mul_ps(_mm_load_ps(&lhs[12]), _mm_load_ps(&rhs[12])));
641 _mm_store_ps(&lhs[16], _mm_mul_ps(_mm_load_ps(&lhs[16]), _mm_load_ps(&rhs[16])));
642 _mm_store_ps(&lhs[20], _mm_mul_ps(_mm_load_ps(&lhs[20]), _mm_load_ps(&rhs[20])));
643 _mm_store_ps(&lhs[24], _mm_mul_ps(_mm_load_ps(&lhs[24]), _mm_load_ps(&rhs[24])));
644 _mm_store_ps(&lhs[28], _mm_mul_ps(_mm_load_ps(&lhs[28]), _mm_load_ps(&rhs[28])));
645 }
646
647 return lhs;
648 }
649
650 #endif // APPLESEED_USE_SSE
651
652 template <typename T>
653 inline RGBSpectrum<T>& operator/=(RGBSpectrum<T>& lhs, const T rhs)
654 {
655 for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
656 lhs[i] /= rhs;
657
658 return lhs;
659 }
660
661 inline RGBSpectrum<float>& operator/=(RGBSpectrum<float>& lhs, const float rhs)
662 {
663 return lhs *= 1.0f / rhs;
664 }
665
666 inline RGBSpectrum<double>& operator/=(RGBSpectrum<double>& lhs, const double rhs)
667 {
668 return lhs *= 1.0 / rhs;
669 }
670
671 inline RGBSpectrum<long double>& operator/=(RGBSpectrum<long double>& lhs, const long double rhs)
672 {
673 return lhs *= 1.0L / rhs;
674 }
675
676 template <typename T>
677 inline RGBSpectrum<T>& operator/=(RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
678 {
679 for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
680 lhs[i] /= rhs[i];
681
682 return lhs;
683 }
684
685 template <typename T>
madd(RGBSpectrum<T> & a,const RGBSpectrum<T> & b,const RGBSpectrum<T> & c)686 inline void madd(
687 RGBSpectrum<T>& a,
688 const RGBSpectrum<T>& b,
689 const RGBSpectrum<T>& c)
690 {
691 for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
692 a[i] += b[i] * c[i];
693 }
694
695 template <typename T>
madd(RGBSpectrum<T> & a,const RGBSpectrum<T> & b,const T c)696 inline void madd(
697 RGBSpectrum<T>& a,
698 const RGBSpectrum<T>& b,
699 const T c)
700 {
701 for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
702 a[i] += b[i] * c;
703 }
704
705 #ifdef APPLESEED_USE_SSE
706
707 template <>
madd(RGBSpectrum<float> & a,const RGBSpectrum<float> & b,const RGBSpectrum<float> & c)708 APPLESEED_FORCE_INLINE void madd(
709 RGBSpectrum<float>& a,
710 const RGBSpectrum<float>& b,
711 const RGBSpectrum<float>& c)
712 {
713 _mm_store_ps(&a[0], _mm_add_ps(_mm_load_ps(&a[0]), _mm_mul_ps(_mm_load_ps(&b[0]), _mm_load_ps(&c[0]))));
714 }
715
716 template <>
madd(RGBSpectrum<float> & a,const RGBSpectrum<float> & b,const float c)717 APPLESEED_FORCE_INLINE void madd(
718 RGBSpectrum<float>& a,
719 const RGBSpectrum<float>& b,
720 const float c)
721 {
722 const __m128 k = _mm_set_ps1(c);
723 _mm_store_ps(&a[0], _mm_add_ps(_mm_load_ps(&a[0]), _mm_mul_ps(_mm_load_ps(&b[0]), k)));
724 }
725
726 #endif // APPLESEED_USE_SSE
727
728 } // namespace renderer
729
730 namespace foundation
731 {
732
733 template <typename T>
is_zero(const renderer::RGBSpectrum<T> & s)734 inline bool is_zero(const renderer::RGBSpectrum<T>& s)
735 {
736 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
737 {
738 if (s[i] != T(0.0))
739 return false;
740 }
741
742 return true;
743 }
744
745 template <typename T>
feq(const renderer::RGBSpectrum<T> & lhs,const renderer::RGBSpectrum<T> & rhs)746 inline bool feq(const renderer::RGBSpectrum<T>& lhs, const renderer::RGBSpectrum<T>& rhs)
747 {
748 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
749 {
750 if (!feq(lhs[i], rhs[i]))
751 return false;
752 }
753
754 return true;
755 }
756
757 template <typename T>
feq(const renderer::RGBSpectrum<T> & lhs,const renderer::RGBSpectrum<T> & rhs,const T eps)758 inline bool feq(const renderer::RGBSpectrum<T>& lhs, const renderer::RGBSpectrum<T>& rhs, const T eps)
759 {
760 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
761 {
762 if (!feq(lhs[i], rhs[i], eps))
763 return false;
764 }
765
766 return true;
767 }
768
769 template <typename T>
fz(const renderer::RGBSpectrum<T> & s)770 inline bool fz(const renderer::RGBSpectrum<T>& s)
771 {
772 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
773 {
774 if (!fz(s[i]))
775 return false;
776 }
777
778 return true;
779 }
780
781 template <typename T>
fz(const renderer::RGBSpectrum<T> & s,const T eps)782 inline bool fz(const renderer::RGBSpectrum<T>& s, const T eps)
783 {
784 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
785 {
786 if (!fz(s[i], eps))
787 return false;
788 }
789
790 return true;
791 }
792
793 template <typename T>
rcp(const renderer::RGBSpectrum<T> & s)794 inline renderer::RGBSpectrum<T> rcp(const renderer::RGBSpectrum<T>& s)
795 {
796 renderer::RGBSpectrum<T> result;
797
798 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
799 result[i] = T(1.0) / s[i];
800
801 return result;
802 }
803
804 template <typename T>
is_saturated(const renderer::RGBSpectrum<T> & s)805 inline bool is_saturated(const renderer::RGBSpectrum<T>& s)
806 {
807 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
808 {
809 if (s[i] < T(0.0) || s[i] > T(1.0))
810 return false;
811 }
812
813 return true;
814 }
815
816 template <typename T>
saturate(const renderer::RGBSpectrum<T> & s)817 inline renderer::RGBSpectrum<T> saturate(const renderer::RGBSpectrum<T>& s)
818 {
819 renderer::RGBSpectrum<T> result;
820
821 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
822 result[i] = saturate(s[i]);
823
824 return result;
825 }
826
827 template <typename T>
saturate_in_place(renderer::RGBSpectrum<T> & s)828 inline void saturate_in_place(renderer::RGBSpectrum<T>& s)
829 {
830 clamp_in_place(s, T(0.0), T(1.0));
831 }
832
833 template <typename T>
clamp(const renderer::RGBSpectrum<T> & s,const T min,const T max)834 inline renderer::RGBSpectrum<T> clamp(const renderer::RGBSpectrum<T>& s, const T min, const T max)
835 {
836 renderer::RGBSpectrum<T> result;
837
838 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
839 result[i] = clamp(s[i], min, max);
840
841 return result;
842 }
843
844 template <typename T>
clamp_in_place(renderer::RGBSpectrum<T> & s,const T min,const T max)845 inline void clamp_in_place(renderer::RGBSpectrum<T>& s, const T min, const T max)
846 {
847 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
848 {
849 if (s[i] < min)
850 s[i] = min;
851
852 if (s[i] > max)
853 s[i] = max;
854 }
855 }
856
857 template <typename T>
clamp_low(const renderer::RGBSpectrum<T> & s,const T min)858 inline renderer::RGBSpectrum<T> clamp_low(const renderer::RGBSpectrum<T>& s, const T min)
859 {
860 renderer::RGBSpectrum<T> result;
861
862 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
863 result[i] = std::max(s[i], min);
864
865 return result;
866 }
867
868 template <typename T>
clamp_low_in_place(renderer::RGBSpectrum<T> & s,const T min)869 inline void clamp_low_in_place(renderer::RGBSpectrum<T>& s, const T min)
870 {
871 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
872 {
873 if (s[i] < min)
874 s[i] = min;
875 }
876 }
877
878 template <typename T>
clamp_high(const renderer::RGBSpectrum<T> & s,const T max)879 inline renderer::RGBSpectrum<T> clamp_high(const renderer::RGBSpectrum<T>& s, const T max)
880 {
881 renderer::RGBSpectrum<T> result;
882
883 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
884 result[i] = std::min(s[i], max);
885
886 return result;
887 }
888
889 template <typename T>
clamp_high_in_place(renderer::RGBSpectrum<T> & s,const T max)890 inline void clamp_high_in_place(renderer::RGBSpectrum<T>& s, const T max)
891 {
892 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
893 {
894 if (s[i] > max)
895 s[i] = max;
896 }
897 }
898
899 template <typename T>
lerp(const renderer::RGBSpectrum<T> & a,const renderer::RGBSpectrum<T> & b,const renderer::RGBSpectrum<T> & t)900 inline renderer::RGBSpectrum<T> lerp(
901 const renderer::RGBSpectrum<T>& a,
902 const renderer::RGBSpectrum<T>& b,
903 const renderer::RGBSpectrum<T>& t)
904 {
905 renderer::RGBSpectrum<T> result;
906
907 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
908 result[i] = foundation::lerp(a[i], b[i], t[i]);
909
910 return result;
911 }
912
913 #ifdef APPLESEED_USE_SSE
914
915 template <>
lerp(const renderer::RGBSpectrum<float> & a,const renderer::RGBSpectrum<float> & b,const renderer::RGBSpectrum<float> & t)916 APPLESEED_FORCE_INLINE renderer::RGBSpectrum<float> lerp(
917 const renderer::RGBSpectrum<float>& a,
918 const renderer::RGBSpectrum<float>& b,
919 const renderer::RGBSpectrum<float>& t)
920 {
921 renderer::RGBSpectrum<float> result;
922
923 __m128 one4 = _mm_set1_ps(1.0f);
924 __m128 t4 = _mm_load_ps(&t[0]);
925 __m128 one_minus_t4 = _mm_sub_ps(one4, t4);
926 __m128 x = _mm_mul_ps(_mm_load_ps(&a[0]), one_minus_t4);
927 __m128 y = _mm_mul_ps(_mm_load_ps(&b[0]), t4);
928 _mm_store_ps(&result[0], _mm_add_ps(x, y));
929
930 return result;
931 }
932
933 #endif // APPLESEED_USE_SSE
934
935 template <typename T>
min_value(const renderer::RGBSpectrum<T> & s)936 inline T min_value(const renderer::RGBSpectrum<T>& s)
937 {
938 return std::min(std::min(s[0], s[1]), s[2]);
939 }
940
941 template <typename T>
max_value(const renderer::RGBSpectrum<T> & s)942 inline T max_value(const renderer::RGBSpectrum<T>& s)
943 {
944 return std::max(std::max(s[0], s[1]), s[2]);
945 }
946
947 template <typename T>
min_index(const renderer::RGBSpectrum<T> & s)948 inline size_t min_index(const renderer::RGBSpectrum<T>& s)
949 {
950 size_t index = 0;
951 T value = s[0];
952
953 for (size_t i = 1, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
954 {
955 const T x = s[i];
956
957 if (value > x)
958 {
959 value = x;
960 index = i;
961 }
962 }
963
964 return index;
965 }
966
967 template <typename T>
max_index(const renderer::RGBSpectrum<T> & s)968 inline size_t max_index(const renderer::RGBSpectrum<T>& s)
969 {
970 size_t index = 0;
971 T value = s[0];
972
973 for (size_t i = 1, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
974 {
975 const T x = s[i];
976
977 if (value < x)
978 {
979 value = x;
980 index = i;
981 }
982 }
983
984 return index;
985 }
986
987 template <typename T>
min_abs_index(const renderer::RGBSpectrum<T> & s)988 inline size_t min_abs_index(const renderer::RGBSpectrum<T>& s)
989 {
990 size_t index = 0;
991 T value = std::abs(s[0]);
992
993 for (size_t i = 1, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
994 {
995 const T x = std::abs(s[i]);
996
997 if (value > x)
998 {
999 value = x;
1000 index = i;
1001 }
1002 }
1003
1004 return index;
1005 }
1006
1007 template <typename T>
max_abs_index(const renderer::RGBSpectrum<T> & s)1008 inline size_t max_abs_index(const renderer::RGBSpectrum<T>& s)
1009 {
1010 size_t index = 0;
1011 T value = std::abs(s[0]);
1012
1013 for (size_t i = 1, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1014 {
1015 const T x = std::abs(s[i]);
1016
1017 if (value < x)
1018 {
1019 value = x;
1020 index = i;
1021 }
1022 }
1023
1024 return index;
1025 }
1026
1027 template <typename T>
sum_value(const renderer::RGBSpectrum<T> & s)1028 inline T sum_value(const renderer::RGBSpectrum<T>& s)
1029 {
1030 T sum = s[0];
1031
1032 for (size_t i = 1, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1033 sum += s[i];
1034
1035 return sum;
1036 }
1037
1038 template <typename T>
average_value(const renderer::RGBSpectrum<T> & s)1039 inline T average_value(const renderer::RGBSpectrum<T>& s)
1040 {
1041 return sum_value(s) / renderer::RGBSpectrum<T>::size();
1042 }
1043
1044 template <typename T>
has_nan(const renderer::RGBSpectrum<T> & s)1045 inline bool has_nan(const renderer::RGBSpectrum<T>& s)
1046 {
1047 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1048 {
1049 if (s[i] != s[i])
1050 return true;
1051 }
1052
1053 return false;
1054 }
1055
1056 template <typename T>
is_finite(const renderer::RGBSpectrum<T> & s)1057 inline bool is_finite(const renderer::RGBSpectrum<T>& s)
1058 {
1059 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1060 {
1061 if (!FP<T>::is_finite(s[i]))
1062 return false;
1063 }
1064
1065 return true;
1066 }
1067
1068 template <typename T>
sqrt(const renderer::RGBSpectrum<T> & s)1069 inline renderer::RGBSpectrum<T> sqrt(const renderer::RGBSpectrum<T>& s)
1070 {
1071 renderer::RGBSpectrum<T> result;
1072
1073 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1074 result[i] = std::sqrt(s[i]);
1075
1076 return result;
1077 }
1078
1079 #ifdef APPLESEED_USE_SSE
1080
sqrt(const renderer::RGBSpectrum<float> & s)1081 APPLESEED_FORCE_INLINE renderer::RGBSpectrum<float> sqrt(const renderer::RGBSpectrum<float>& s)
1082 {
1083 renderer::RGBSpectrum<float> result;
1084 _mm_store_ps(&result[ 0], _mm_sqrt_ps(_mm_load_ps(&s[ 0])));
1085 return result;
1086 }
1087
1088 #endif // APPLESEED_USE_SSE
1089
1090 template <typename T>
pow(const renderer::RGBSpectrum<T> & x,const T y)1091 inline renderer::RGBSpectrum<T> pow(const renderer::RGBSpectrum<T>& x, const T y)
1092 {
1093 renderer::RGBSpectrum<T> result;
1094
1095 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1096 result[i] = std::pow(x[i], y);
1097
1098 return result;
1099 }
1100
1101 template <typename T>
pow(const renderer::RGBSpectrum<T> & x,const renderer::RGBSpectrum<T> & y)1102 inline renderer::RGBSpectrum<T> pow(
1103 const renderer::RGBSpectrum<T>& x,
1104 const renderer::RGBSpectrum<T>& y)
1105 {
1106 renderer::RGBSpectrum<T> result;
1107
1108 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1109 result[i] = std::pow(x[i], y[i]);
1110
1111 return result;
1112 }
1113
1114 template <typename T>
log(const renderer::RGBSpectrum<T> & s)1115 inline renderer::RGBSpectrum<T> log(const renderer::RGBSpectrum<T>& s)
1116 {
1117 renderer::RGBSpectrum<T> result;
1118
1119 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1120 result[i] = std::log(s[i]);
1121
1122 return result;
1123 }
1124
1125 template <typename T>
exp(const renderer::RGBSpectrum<T> & s)1126 inline renderer::RGBSpectrum<T> exp(const renderer::RGBSpectrum<T>& s)
1127 {
1128 renderer::RGBSpectrum<T> result;
1129
1130 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1131 result[i] = std::exp(s[i]);
1132
1133 return result;
1134 }
1135
1136 template <typename T>
1137 class PoisonImpl<renderer::RGBSpectrum<T>>
1138 {
1139 public:
do_poison(renderer::RGBSpectrum<T> & s)1140 static void do_poison(renderer::RGBSpectrum<T>& s)
1141 {
1142 for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1143 debug_poison(s[i]);
1144 }
1145 };
1146
1147 } // namespace foundation
1148