1 // Copyright Contributors to the Open Shading Language project.
2 // SPDX-License-Identifier: BSD-3-Clause
3 // https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4
5
6 /////////////////////////////////////////////////////////////////////////
7 /// \file
8 ///
9 /// Shader interpreter implementation of color operations.
10 ///
11 /////////////////////////////////////////////////////////////////////////
12
13 #include "oslexec_pvt.h"
14 #include <OSL/dual.h>
15 #include <OSL/dual_vec.h>
16 #include <OSL/Imathx/Imathx.h>
17 #include <OSL/device_string.h>
18
19 #include <OpenImageIO/fmath.h>
20
21 #if defined(_MSC_VER) && _MSC_VER < 1800
22 using OIIO::expm1;
23 using OIIO::cbrtf;
24 #endif
25
26 #ifdef __CUDACC__
27 #include <optix.h>
28 #endif
29
30 #include "opcolor.h"
31
32 OSL_NAMESPACE_ENTER
33
34 namespace pvt {
35
36 // CIE colour matching functions xBar, yBar, and zBar for
37 // wavelengths from 380 through 780 nanometers, every 5
38 // nanometers. For a wavelength lambda in this range:
39 // cie_colour_match[(lambda - 380) / 5][0] = xBar
40 // cie_colour_match[(lambda - 380) / 5][1] = yBar
41 // cie_colour_match[(lambda - 380) / 5][2] = zBar
42 OSL_CONSTANT_DATA const static float cie_colour_match[81][3] =
43 {
44 {0.0014,0.0000,0.0065}, {0.0022,0.0001,0.0105}, {0.0042,0.0001,0.0201},
45 {0.0076,0.0002,0.0362}, {0.0143,0.0004,0.0679}, {0.0232,0.0006,0.1102},
46 {0.0435,0.0012,0.2074}, {0.0776,0.0022,0.3713}, {0.1344,0.0040,0.6456},
47 {0.2148,0.0073,1.0391}, {0.2839,0.0116,1.3856}, {0.3285,0.0168,1.6230},
48 {0.3483,0.0230,1.7471}, {0.3481,0.0298,1.7826}, {0.3362,0.0380,1.7721},
49 {0.3187,0.0480,1.7441}, {0.2908,0.0600,1.6692}, {0.2511,0.0739,1.5281},
50 {0.1954,0.0910,1.2876}, {0.1421,0.1126,1.0419}, {0.0956,0.1390,0.8130},
51 {0.0580,0.1693,0.6162}, {0.0320,0.2080,0.4652}, {0.0147,0.2586,0.3533},
52 {0.0049,0.3230,0.2720}, {0.0024,0.4073,0.2123}, {0.0093,0.5030,0.1582},
53 {0.0291,0.6082,0.1117}, {0.0633,0.7100,0.0782}, {0.1096,0.7932,0.0573},
54 {0.1655,0.8620,0.0422}, {0.2257,0.9149,0.0298}, {0.2904,0.9540,0.0203},
55 {0.3597,0.9803,0.0134}, {0.4334,0.9950,0.0087}, {0.5121,1.0000,0.0057},
56 {0.5945,0.9950,0.0039}, {0.6784,0.9786,0.0027}, {0.7621,0.9520,0.0021},
57 {0.8425,0.9154,0.0018}, {0.9163,0.8700,0.0017}, {0.9786,0.8163,0.0014},
58 {1.0263,0.7570,0.0011}, {1.0567,0.6949,0.0010}, {1.0622,0.6310,0.0008},
59 {1.0456,0.5668,0.0006}, {1.0026,0.5030,0.0003}, {0.9384,0.4412,0.0002},
60 {0.8544,0.3810,0.0002}, {0.7514,0.3210,0.0001}, {0.6424,0.2650,0.0000},
61 {0.5419,0.2170,0.0000}, {0.4479,0.1750,0.0000}, {0.3608,0.1382,0.0000},
62 {0.2835,0.1070,0.0000}, {0.2187,0.0816,0.0000}, {0.1649,0.0610,0.0000},
63 {0.1212,0.0446,0.0000}, {0.0874,0.0320,0.0000}, {0.0636,0.0232,0.0000},
64 {0.0468,0.0170,0.0000}, {0.0329,0.0119,0.0000}, {0.0227,0.0082,0.0000},
65 {0.0158,0.0057,0.0000}, {0.0114,0.0041,0.0000}, {0.0081,0.0029,0.0000},
66 {0.0058,0.0021,0.0000}, {0.0041,0.0015,0.0000}, {0.0029,0.0010,0.0000},
67 {0.0020,0.0007,0.0000}, {0.0014,0.0005,0.0000}, {0.0010,0.0004,0.0000},
68 {0.0007,0.0002,0.0000}, {0.0005,0.0002,0.0000}, {0.0003,0.0001,0.0000},
69 {0.0002,0.0001,0.0000}, {0.0002,0.0001,0.0000}, {0.0001,0.0000,0.0000},
70 {0.0001,0.0000,0.0000}, {0.0001,0.0000,0.0000}, {0.0000,0.0000,0.0000}
71 };
72
73 // White point chromaticities.
74 #define IlluminantC 0.3101, 0.3162 /* For NTSC television */
75 #define IlluminantD65 0.3127, 0.3291 /* For EBU and SMPTE */
76 #define IlluminantE 0.33333333, 0.33333333 /* CIE equal-energy illuminant */
77
78 OSL_CONSTANT_DATA const static ColorSystem::Chroma k_color_systems[11] = {
79 // Index, Name xRed yRed xGreen yGreen xBlue yBlue White point
80 /* 0 Rec709 */ { 0.64, 0.33, 0.30, 0.60, 0.15, 0.06, IlluminantD65 },
81 /* 1 sRGB */ { 0.64, 0.33, 0.30, 0.60, 0.15, 0.06, IlluminantD65 },
82 /* 2 NTSC */ { 0.67, 0.33, 0.21, 0.71, 0.14, 0.08, IlluminantC },
83 /* 3 EBU */ { 0.64, 0.33, 0.29, 0.60, 0.15, 0.06, IlluminantD65 },
84 /* 4 PAL */ { 0.64, 0.33, 0.29, 0.60, 0.15, 0.06, IlluminantD65 },
85 /* 5 SECAM */ { 0.64, 0.33, 0.29, 0.60, 0.15, 0.06, IlluminantD65 },
86 /* 6 SMPTE */ { 0.630, 0.340, 0.310, 0.595, 0.155, 0.070, IlluminantD65 },
87 /* 7 HDTV */ { 0.670, 0.330, 0.210, 0.710, 0.150, 0.060, IlluminantD65 },
88 /* 8 CIE */ { 0.7355, 0.2645, 0.2658, 0.7243, 0.1669, 0.0085, IlluminantE },
89 /* 9 AdobeRGB */ { 0.64, 0.33, 0.21, 0.71, 0.15, 0.06, IlluminantD65 },
90 /* 10 XYZ */ { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, IlluminantE },
91 };
92
93
94 OSL_HOSTDEVICE const ColorSystem::Chroma*
fromString(StringParam colorspace)95 ColorSystem::fromString(StringParam colorspace) {
96 if (colorspace == StringParams::Rec709)
97 return &k_color_systems[0];
98 if (colorspace == StringParams::sRGB)
99 return &k_color_systems[1];
100 if (colorspace == StringParams::NTSC)
101 return &k_color_systems[2];
102 if (colorspace == StringParams::EBU)
103 return &k_color_systems[3];
104 if (colorspace == StringParams::PAL)
105 return &k_color_systems[4];
106 if (colorspace == StringParams::SECAM)
107 return &k_color_systems[5];
108 if (colorspace == StringParams::SMPTE)
109 return &k_color_systems[6];
110 if (colorspace == StringParams::HDTV)
111 return &k_color_systems[7];
112 if (colorspace == StringParams::CIE)
113 return &k_color_systems[8];
114 if (colorspace == StringParams::AdobeRGB)
115 return &k_color_systems[9];
116 if (colorspace == StringParams::XYZ)
117 return &k_color_systems[10];
118 return nullptr;
119 }
120
121
122
123 namespace {
124
125
126 template <typename COLOR3> OSL_HOSTDEVICE
127 static COLOR3
hsv_to_rgb(const COLOR3 & hsv)128 hsv_to_rgb (const COLOR3& hsv)
129 {
130 // Reference for this technique: Foley & van Dam
131 using FLOAT = typename ScalarFromVec<COLOR3>::type;
132 FLOAT h = comp_x(hsv), s = comp_y(hsv), v = comp_z(hsv);
133 if (s < 0.0001f) {
134 return make_Color3 (v, v, v);
135 } else {
136 using std::floor; // to pick up the float one
137 using OIIO::ifloor;
138 h = 6.0f * (h - floor(h)); // expand to [0..6)
139 int hi = ifloor(h);
140 FLOAT f = h - FLOAT(hi);
141 FLOAT p = v * (1.0f-s);
142 FLOAT q = v * (1.0f-s*f);
143 FLOAT t = v * (1.0f-s*(1.0f-f));
144 switch (hi) {
145 case 0 : return make_Color3 (v, t, p);
146 case 1 : return make_Color3 (q, v, p);
147 case 2 : return make_Color3 (p, v, t);
148 case 3 : return make_Color3 (p, q, v);
149 case 4 : return make_Color3 (t, p, v);
150 default: return make_Color3 (v, p, q);
151 }
152 }
153 }
154
155
156 template <typename COLOR3> OSL_HOSTDEVICE
157 static COLOR3
rgb_to_hsv(const COLOR3 & rgb)158 rgb_to_hsv (const COLOR3& rgb)
159 {
160 // See Foley & van Dam
161 using FLOAT = typename ScalarFromVec<COLOR3>::type;
162 FLOAT r = comp_x(rgb), g = comp_y(rgb), b = comp_z(rgb);
163 FLOAT mincomp = std::min (r, std::min (g, b));
164 FLOAT maxcomp = std::max (r, std::max (g, b));
165 FLOAT delta = maxcomp - mincomp; // chroma
166 FLOAT h, s, v;
167 v = maxcomp;
168 if (maxcomp > 0.0f)
169 s = delta / maxcomp;
170 else s = 0.0f;
171 if (s <= 0.0f)
172 h = 0.0f;
173 else {
174 if (r >= maxcomp) h = (g-b) / delta;
175 else if (g >= maxcomp) h = 2.0f + (b-r) / delta;
176 else h = 4.0f + (r-g) / delta;
177 h *= (1.0f/6.0f);
178 if (h < 0.0f)
179 h += 1.0f;
180 }
181 return make_Color3 (h, s, v);
182 }
183
184
185
186 template <typename COLOR3> OSL_HOSTDEVICE
187 static COLOR3
hsl_to_rgb(const COLOR3 & hsl)188 hsl_to_rgb (const COLOR3& hsl)
189 {
190 using FLOAT = typename ScalarFromVec<COLOR3>::type;
191 FLOAT h = comp_x(hsl), s = comp_y(hsl), l = comp_z(hsl);
192 // Easiest to convert hsl -> hsv, then hsv -> RGB (per Foley & van Dam)
193 FLOAT v = (l <= 0.5f) ? (l * (1.0f + s)) : (l * (1.0f - s) + s);
194 if (v <= 0.0f) {
195 return make_Color3 (0.0f, 0.0f, 0.0f);
196 } else {
197 FLOAT min = 2.0f * l - v;
198 s = (v - min) / v;
199 return hsv_to_rgb (make_Color3(h, s, v));
200 }
201 }
202
203
204
205 template <typename COLOR3> OSL_HOSTDEVICE
206 static COLOR3
rgb_to_hsl(const COLOR3 & rgb)207 rgb_to_hsl (const COLOR3& rgb)
208 {
209 // See Foley & van Dam
210 // First convert rgb to hsv, then to hsl
211 using FLOAT = typename ScalarFromVec<COLOR3>::type;
212 FLOAT minval = std::min (comp_x(rgb), std::min (comp_y(rgb), comp_z(rgb)));
213 COLOR3 hsv = rgb_to_hsv (rgb);
214 FLOAT maxval = comp_z(hsv); // v == maxval
215 FLOAT h = comp_x(hsv), s, l = (minval+maxval) / 2.0f;
216 if (equalVal (minval, maxval))
217 s = 0.0f; // special 'achromatic' case, hue is 0
218 else if (l <= 0.5f)
219 s = (maxval - minval) / (maxval + minval);
220 else
221 s = (maxval - minval) / (2.0f - maxval - minval);
222 return make_Color3 (h, s, l);
223 }
224
225
226
227 template <typename COLOR3> OSL_HOSTDEVICE
228 static COLOR3
YIQ_to_rgb(const COLOR3 & YIQ)229 YIQ_to_rgb (const COLOR3& YIQ)
230 {
231 return YIQ * Matrix33(1.0000, 1.0000, 1.0000,
232 0.9557, -0.2716, -1.1082,
233 0.6199, -0.6469, 1.7051);
234 }
235
236
237 template <typename COLOR3> OSL_HOSTDEVICE
238 static COLOR3
rgb_to_YIQ(const COLOR3 & rgb)239 rgb_to_YIQ (const COLOR3& rgb)
240 {
241 return rgb * Matrix33(0.299, 0.596, 0.212,
242 0.587, -0.275, -0.523,
243 0.114, -0.321, 0.311);
244 }
245
246
247
248 #if 0
249 OSL_HOSTDEVICE inline Color3
250 XYZ_to_xyY (const Color3 &XYZ)
251 {
252 float n = (XYZ[0] + XYZ[1] + XYZ[2]);
253 float n_inv = (n >= 1.0e-6 ? 1.0f/n : 0.0f);
254 return Color3 (XYZ[0]*n_inv, XYZ[1]*n_inv, XYZ[1]);
255 // N.B. http://brucelindbloom.com/ suggests returning xy of the
256 // reference white in the X+Y+Z==0 case.
257 }
258 #endif
259
260
261 template <typename COLOR3> OSL_HOSTDEVICE
262 static COLOR3
xyY_to_XYZ(const COLOR3 & xyY)263 xyY_to_XYZ (const COLOR3 &xyY)
264 {
265 using FLOAT = typename ScalarFromVec<COLOR3>::type;
266 FLOAT Y = comp_z(xyY);
267 FLOAT Y_y = (comp_y(xyY) > 1.0e-6f ? Y/comp_y(xyY) : 0.0f);
268 FLOAT X = Y_y * comp_x(xyY);
269 FLOAT Z = Y_y * (1.0f - comp_x(xyY) - comp_y(xyY));
270 return make_Color3 (X, Y, Z);
271 }
272
273
274
275 template <typename COLOR3> OSL_HOSTDEVICE
276 static COLOR3
sRGB_to_linear(const COLOR3 & srgb)277 sRGB_to_linear (const COLOR3& srgb)
278 {
279 // See Foley & van Dam
280 using FLOAT = typename ScalarFromVec<COLOR3>::type;
281 using namespace OIIO;
282 //using safe_pow = std::conditional<is_Dual<COLOR3>::value, OSL::safe_pow, OIIO::safe_pow>::type;
283 FLOAT r = comp_x(srgb), g = comp_y(srgb), b = comp_z(srgb);
284 auto convert = [] (FLOAT x) -> FLOAT {
285 return (x <= 0.04045f) ?
286 (x * (1.0f / 12.92f)) :
287 safe_pow((x + 0.055f) * (1.0f / 1.055f), FLOAT(2.4f));
288 };
289 return make_Color3 (convert(r), convert(g), convert(b));
290 }
291
292
293
294 template <typename COLOR3> OSL_HOSTDEVICE
295 static COLOR3
linear_to_sRGB(const COLOR3 & rgb)296 linear_to_sRGB (const COLOR3& rgb)
297 {
298 // See Foley & van Dam
299 using FLOAT = typename ScalarFromVec<COLOR3>::type;
300 using namespace OIIO;
301 //using safe_pow = std::conditional<is_Dual<COLOR3>::value, OSL::safe_pow, OIIO::safe_pow>::type;
302 FLOAT r = comp_x(rgb), g = comp_y(rgb), b = comp_z(rgb);
303 auto convert = [] (FLOAT x) -> FLOAT {
304 return (x <= 0.0031308f) ?
305 (12.92f * x) :
306 (1.055f * safe_pow(x, FLOAT(1.f / 2.4f)) - 0.055f);
307 };
308
309 return make_Color3 (convert(r), convert(g), convert(b));
310 }
311
312
313
314
315 // Spectral rendering routines inspired by those found at:
316 // http://www.fourmilab.ch/documents/specrend/specrend.c
317 // which bore the notice:
318 // Colour Rendering of Spectra
319 // by John Walker
320 // http://www.fourmilab.ch/
321 // Last updated: March 9, 2003
322 // This program is in the public domain.
323 // For complete information about the techniques employed in
324 // this program, see the World-Wide Web document:
325 // http://www.fourmilab.ch/documents/specrend/
326
327
328
329 // Functor that calculates, by Planck's radiation law, the black body
330 // emittance at temperature (in Kelvin) and given wavelength (in nm).
331 // This is the differential (per unit of wavelength) flux density, in
332 // W/m^2 in the range [wavelength,wavelength+dwavelength].
333 class bb_spectrum {
334 public:
bb_spectrum(float temperature=5000)335 OSL_HOSTDEVICE bb_spectrum (float temperature=5000) : m_temp(temperature) { }
operator ()(float wavelength_nm) const336 OSL_HOSTDEVICE float operator() (float wavelength_nm) const {
337 double wlm = wavelength_nm * 1e-9; // Wavelength in meters
338 const double c1 = 3.74183e-16; // 2*pi*h*c^2, W*m^2
339 const double c2 = 1.4388e-2; // h*c/k, m*K
340 // h is Planck's const, k is Boltzmann's
341 return float((c1 * std::pow(wlm,-5.0)) / ::expm1(c2 / (wlm * m_temp)));
342 }
343 private:
344 double m_temp;
345 };
346
347
348
349 // For a given wavelength lambda (in nm), return the XYZ triple giving the
350 // XYZ color corresponding to that single wavelength;
351 OSL_HOSTDEVICE static Color3
wavelength_color_XYZ(float lambda_nm)352 wavelength_color_XYZ (float lambda_nm)
353 {
354 float ii = (lambda_nm-380.0f) / 5.0f; // scaled 0..80
355 int i = (int) ii;
356 if (i < 0 || i >= 80)
357 return Color3(0.0f,0.0f,0.0f);
358 ii -= i;
359 const float *c = cie_colour_match[i];
360 Color3 XYZ = OIIO::lerp (Color3(c[0], c[1], c[2]),
361 Color3(c[3], c[4], c[5]), ii);
362 #if 0
363 float n = (XYZ[0] + XYZ[1] + XYZ[2]);
364 float n_inv = (n >= 1.0e-6f ? 1.0f/n : 0.0f);
365 XYZ *= n_inv;
366 #endif
367 return XYZ;
368 }
369
370
371
372 // Integrate the CIE color matching values, weighted by function
373 // spec_intens(lambda_nm), returning the aggregate XYZ color.
374 template<class SPECTRUM> OSL_HOSTDEVICE
375 static Color3
spectrum_to_XYZ(const SPECTRUM & spec_intens)376 spectrum_to_XYZ (const SPECTRUM &spec_intens)
377 {
378 float X = 0, Y = 0, Z = 0;
379 const float dlambda = 5.0f * 1e-9; // in meters
380 for (int i = 0; i < 81; ++i) {
381 float lambda = 380.0f + 5.0f * i;
382 // N.B. spec_intens returns result in W/m^2 but it's a differential,
383 // needs to be scaled by dlambda!
384 float Me = spec_intens(lambda) * dlambda;
385 X += Me * cie_colour_match[i][0];
386 Y += Me * cie_colour_match[i][1];
387 Z += Me * cie_colour_match[i][2];
388 }
389 return Color3 (X, Y, Z);
390 }
391
392
393 #if 0
394 // If the requested RGB shade contains a negative weight for one of the
395 // primaries, it lies outside the colour gamut accessible from the given
396 // triple of primaries. Desaturate it by adding white, equal quantities
397 // of R, G, and B, enough to make RGB all positive. The function
398 // returns true if the components were modified, zero otherwise.
399 OSL_HOSTDEVICE inline bool
400 constrain_rgb (Color3 &rgb)
401 {
402 // Amount of white needed is w = - min(0,r,g,b)
403 float w = 0.0f;
404 w = (0 < rgb.x) ? w : rgb.x;
405 w = (w < rgb.y) ? w : rgb.y;
406 w = (w < rgb.z) ? w : rgb.z;
407 w = -w;
408
409 // Add just enough white to make r, g, b all positive.
410 if (w > 0) {
411 rgb.x += w; rgb.y += w; rgb.z += w;
412 return true; // Color modified to fit RGB gamut
413 }
414
415 return false; // color was within RGB gamut
416 }
417
418
419
420 // Rescale rgb so its largest component is 1.0, and return the original
421 // largest component.
422 OSL_HOSTDEVICE inline float
423 norm_rgb (Color3 &rgb)
424 {
425 float greatest = std::max(rgb.x, std::max(rgb.y, rgb.z));
426 if (greatest > 1.0e-12f)
427 rgb *= 1.0f/greatest;
428 return greatest;
429 }
430 #endif
431
432
clamp_zero(Color3 & c)433 OSL_HOSTDEVICE inline void clamp_zero (Color3 &c)
434 {
435 if (c.x < 0.0f)
436 c.x = 0.0f;
437 if (c.y < 0.0f)
438 c.y = 0.0f;
439 if (c.z < 0.0f)
440 c.z = 0.0f;
441 }
442
443
444
445 OSL_HOSTDEVICE inline Color3
colpow(const Color3 & c,float p)446 colpow (const Color3 &c, float p)
447 {
448 return Color3 (powf(c.x,p), powf(c.y,p), powf(c.z,p));
449 }
450
451
452 }; // End anonymous namespace
453
454
455
456 // In order to speed up the blackbody computation, we have a table
457 // storing the precomputed BB values for a range of temperatures. Less
458 // than BB_DRAPER always returns 0. Greater than BB_MAX_TABLE_RANGE
459 // does the full computation, we think it'll be rare to inquire higher
460 // temperatures.
461 //
462 // Since the bb function is so nonlinear, we actually space the table
463 // entries nonlinearly, with the relationship between the table index i
464 // and the temperature T as follows:
465 // i = ((T-Draper)/spacing)^(1/xpower)
466 // T = pow(i, xpower) * spacing + Draper
467 // And furthermore, we store in the table the true value raised ^(1/5).
468 // I tuned this a bit, and with the current values we can have all
469 // blackbody results accurate to within 0.1% with a table size of 317
470 // (about 5 KB of data).
471 #define BB_DRAPER 800.0f /* really 798K, below this visible BB is negligible */
472 #define BB_MAX_TABLE_RANGE 12000.0f /* max temp for which we use the table */
473 #define BB_TABLE_XPOWER 1.5f // NOTE: not used, hardcoded into expressions below
474 #define BB_TABLE_YPOWER 5.0f // NOTE: decode is hardcoded
475 #define BB_TABLE_SPACING 2.0f
476
BB_TABLE_MAP(float i)477 OSL_HOSTDEVICE inline float BB_TABLE_MAP(float i) {
478 // return powf (i, BB_TABLE_XPOWER) * BB_TABLE_SPACING + BB_DRAPER;
479 float is = sqrtf(i);
480 float ip = is * is * is; // ^3/2
481 return ip * BB_TABLE_SPACING + BB_DRAPER;
482 }
483
BB_TABLE_UNMAP(float T)484 OSL_HOSTDEVICE inline float BB_TABLE_UNMAP(float T) {
485 // return powf ((T - BB_DRAPER) / BB_TABLE_SPACING, 1.0f/BB_TABLE_XPOWER);
486 float t = (T - BB_DRAPER) / BB_TABLE_SPACING;
487 float ic = cbrtf(t);
488 return ic * ic; // ^2/3
489 }
490
491
492 OSL_HOSTDEVICE bool
set_colorspace(StringParam colorspace)493 ColorSystem::set_colorspace (StringParam colorspace)
494 {
495 if (colorspace == m_colorspace)
496 return true;
497
498 const Chroma* chroma = fromString(colorspace);
499 if (!chroma)
500 return false;
501
502 // Record the current colorspace
503 m_colorspace = colorspace;
504
505 m_Red.setValue (chroma->xRed, chroma->yRed, 0.0f);
506 m_Green.setValue (chroma->xGreen, chroma->yGreen, 0.0f);
507 m_Blue.setValue (chroma->xBlue, chroma->yBlue, 0.0f);
508 m_White.setValue (chroma->xWhite, chroma->yWhite, 0.0f);
509 // set z values to normalize
510 m_Red.z = 1.0f - (m_Red.x + m_Red.y);
511 m_Green.z = 1.0f - (m_Green.x + m_Green.y);
512 m_Blue.z = 1.0f - (m_Blue.x + m_Blue.y);
513 m_White.z = 1.0f - (m_White.x + m_White.y);
514
515 const Color3 &R(m_Red), &G(m_Green), &B(m_Blue), &W(m_White);
516 // xyz -> rgb matrix, before scaling to white.
517 Color3 r (G.y*B.z - B.y*G.z, B.x*G.z - G.x*B.z, G.x*B.y - B.x*G.y);
518 Color3 g (B.y*R.z - R.y*B.z, R.x*B.z - B.x*R.z, B.x*R.y - R.x*B.y);
519 Color3 b (R.y*G.z - G.y*R.z, G.x*R.z - R.x*G.z, R.x*G.y - G.x*R.y);
520 Color3 w (r.dot(W), g.dot(W), b.dot(W)); // White scaling factor
521 if (W.y != 0.0f) // divide by W.y to scale luminance to 1.0
522 w *= 1.0f/W.y;
523 // xyz -> rgb matrix, correctly scaled to white.
524 r /= w.x;
525 g /= w.y;
526 b /= w.z;
527 m_XYZ2RGB = Matrix33 (r.x, g.x, b.x,
528 r.y, g.y, b.y,
529 r.z, g.z, b.z);
530 m_RGB2XYZ = m_XYZ2RGB.inverse();
531 m_luminance_scale = Color3 (m_RGB2XYZ.x[0][1], m_RGB2XYZ.x[1][1], m_RGB2XYZ.x[2][1]);
532
533 // Mathematical imprecision can lead to the luminance scale not
534 // quite summing to 1.0. If it's very close, adjust to make it
535 // exact.
536 float lum2 = (1.0f - m_luminance_scale.x - m_luminance_scale.y);
537 if (fabsf(lum2 - m_luminance_scale.z) < 0.001f)
538 m_luminance_scale.z = lum2;
539
540 // Precompute a table of blackbody values
541 // FIXME: With c++14 and constexpr cbrtf, this could be static_assert
542 assert( std::ceil(BB_TABLE_UNMAP(BB_MAX_TABLE_RANGE)) <
543 std::extent<decltype(m_blackbody_table)>::value);
544
545 float lastT = 0;
546 for (int i = 0; lastT <= BB_MAX_TABLE_RANGE; ++i) {
547 float T = BB_TABLE_MAP(float(i));
548 lastT = T;
549 bb_spectrum spec (T);
550 Color3 rgb = XYZ_to_RGB (spectrum_to_XYZ (spec));
551 clamp_zero (rgb);
552 rgb = colpow (rgb, 1.0f/BB_TABLE_YPOWER);
553 m_blackbody_table[i] = rgb;
554 // std::cout << "Table[" << i << "; T=" << T << "] = " << rgb << "\n";
555 }
556
557 #if 0 && !defined(__CUDACC__)
558 std::cout << "Made " << m_blackbody_table.size() << " table entries for blackbody\n";
559
560 // Sanity checks
561 std::cout << "m_XYZ2RGB = " << m_XYZ2RGB << "\n";
562 std::cout << "m_RGB2XYZ = " << m_RGB2XYZ << "\n";
563 std::cout << "m_luminance_scale = " << m_luminance_scale << "\n";
564 #endif
565 return true;
566 }
567
568 // For Optix, this will be defined by the renderer. Otherwise inline a getter.
569 #ifdef __CUDACC__
570 extern "C" __device__
571 void osl_printf (void* sg_, char* fmt_str, void* args) __attribute__((weak));
572
573 extern "C" __device__ int
574 rend_get_userdata (StringParam name, void* data, int data_size,
575 const OSL::TypeDesc& type, int index) __attribute__((weak));
576
577 OSL_HOSTDEVICE void
error(StringParam src,StringParam dst,Context sg)578 ColorSystem::error(StringParam src, StringParam dst, Context sg) {
579 const char* args[2] = { src.c_str(), dst.c_str() };
580 osl_printf (sg,
581 (char*) // FIXME!
582 "ERROR: Unknown color space transformation \"%s\" -> \"%s\"\n",
583 args);
584 }
585
op_color_colorsystem(void * sg)586 __device__ static inline ColorSystem& op_color_colorsystem (void *sg) {
587 void* ptr;
588 rend_get_userdata(StringParams::colorsystem, &ptr, 8, OSL::TypeDesc::PTR, 0);
589 return *((ColorSystem*)ptr);
590 }
591
op_color_context(void * sg)592 __device__ static inline void* op_color_context (void *sg) {
593 return sg;
594 }
595 #else
596 void
error(StringParam src,StringParam dst,Context context)597 ColorSystem::error(StringParam src, StringParam dst, Context context) {
598 context->errorf("Unknown color space transformation"
599 " \"%s\" -> \"%s\"", src, dst);
600 }
601
op_color_colorsystem(void * sg)602 static inline ColorSystem& op_color_colorsystem (void *sg) {
603 return ((ShaderGlobals *)sg)->context->shadingsys().colorsystem();
604 }
605
op_color_context(void * sg)606 static inline OSL::ShadingContext* op_color_context (void *sg) {
607 return (ShadingContext*)((ShaderGlobals *)sg)->context;
608 }
609 #endif
610
611
612
613 template <typename Color> OSL_HOSTDEVICE Color
ocio_transform(StringParam fromspace,StringParam tospace,const Color & C,Context ctx)614 ColorSystem::ocio_transform (StringParam fromspace, StringParam tospace,
615 const Color& C, Context ctx)
616 {
617 #if OIIO_HAS_COLORPROCESSOR
618 Color Cout;
619 if (ctx->shadingsys().ocio_transform(fromspace, tospace, C, Cout))
620 return Cout;
621 #endif
622
623 error (fromspace, tospace, ctx);
624 return C;
625 }
626
627
628
629 OSL_HOSTDEVICE Color3
to_rgb(StringParam fromspace,const Color3 & C,Context context)630 ColorSystem::to_rgb (StringParam fromspace, const Color3& C, Context context)
631 {
632 if (fromspace == StringParams::RGB || fromspace == StringParams::rgb
633 || fromspace == m_colorspace)
634 return C;
635 if (fromspace == StringParams::hsv)
636 return hsv_to_rgb (C);
637 if (fromspace == StringParams::hsl)
638 return hsl_to_rgb (C);
639 if (fromspace == StringParams::YIQ)
640 return YIQ_to_rgb (C);
641 if (fromspace == StringParams::XYZ)
642 return XYZ_to_RGB (C);
643 if (fromspace == StringParams::xyY)
644 return XYZ_to_RGB (xyY_to_XYZ (C));
645 else
646 return ocio_transform (fromspace, StringParams::RGB, C, context);
647 }
648
649
650
651 OSL_HOSTDEVICE Color3
from_rgb(StringParam tospace,const Color3 & C,Context context)652 ColorSystem::from_rgb (StringParam tospace, const Color3& C, Context context)
653 {
654 if (tospace == StringParams::RGB || tospace == StringParams::rgb
655 || tospace == m_colorspace)
656 return C;
657 if (tospace == StringParams::hsv)
658 return rgb_to_hsv (C);
659 if (tospace == StringParams::hsl)
660 return rgb_to_hsl (C);
661 if (tospace == StringParams::YIQ)
662 return rgb_to_YIQ (C);
663 if (tospace == StringParams::XYZ)
664 return RGB_to_XYZ (C);
665 if (tospace == StringParams::xyY)
666 return RGB_to_XYZ (xyY_to_XYZ (C));
667 else
668 return ocio_transform (StringParams::RGB, tospace, C, context);
669 }
670
671
672
673 template <typename COLOR> OSL_HOSTDEVICE COLOR
transformc(StringParam fromspace,StringParam tospace,const COLOR & C,Context context)674 ColorSystem::transformc (StringParam fromspace, StringParam tospace,
675 const COLOR& C, Context context)
676 {
677 bool use_colorconfig = false;
678 COLOR Crgb;
679 if (fromspace == StringParams::RGB || fromspace == StringParams::rgb
680 || fromspace == StringParams::linear || fromspace == m_colorspace)
681 Crgb = C;
682 else if (fromspace == StringParams::hsv)
683 Crgb = hsv_to_rgb (C);
684 else if (fromspace == StringParams::hsl)
685 Crgb = hsl_to_rgb (C);
686 else if (fromspace == StringParams::YIQ)
687 Crgb = YIQ_to_rgb (C);
688 else if (fromspace == StringParams::XYZ)
689 Crgb = XYZ_to_RGB (C);
690 else if (fromspace == StringParams::xyY)
691 Crgb = XYZ_to_RGB (xyY_to_XYZ (C));
692 else if (fromspace == StringParams::sRGB)
693 Crgb = sRGB_to_linear (C);
694 else {
695 use_colorconfig = true;
696 }
697
698 COLOR Cto;
699 if (use_colorconfig) {
700 // do things the ColorConfig way, so skip all these other clauses...
701 }
702 else if (tospace == StringParams::RGB || tospace == StringParams::rgb
703 || tospace == StringParams::linear || tospace == m_colorspace)
704 Cto = Crgb;
705 else if (tospace == StringParams::hsv)
706 Cto = rgb_to_hsv (Crgb);
707 else if (tospace == StringParams::hsl)
708 Cto = rgb_to_hsl (Crgb);
709 else if (tospace == StringParams::YIQ)
710 Cto = rgb_to_YIQ (Crgb);
711 else if (tospace == StringParams::XYZ)
712 Cto = RGB_to_XYZ (Crgb);
713 else if (tospace == StringParams::xyY)
714 Cto = RGB_to_XYZ (xyY_to_XYZ (Crgb));
715 else if (tospace == StringParams::sRGB)
716 Cto = linear_to_sRGB (Crgb);
717 else {
718 use_colorconfig = true;
719 }
720
721 if (use_colorconfig) {
722 Cto = ocio_transform (fromspace, tospace, C, context);
723 }
724
725 return Cto;
726 }
727
728
729
730 OSL_HOSTDEVICE Dual2<Color3>
transformc(StringParam fromspace,StringParam tospace,const Dual2<Color3> & color,Context ctx)731 ColorSystem::transformc (StringParam fromspace, StringParam tospace,
732 const Dual2<Color3>& color, Context ctx) {
733 return transformc<Dual2<Color3>>(fromspace, tospace, color, ctx);
734 }
735
736
737
738 OSL_HOSTDEVICE Color3
transformc(StringParam fromspace,StringParam tospace,const Color3 & color,Context ctx)739 ColorSystem::transformc (StringParam fromspace, StringParam tospace,
740 const Color3& color, Context ctx) {
741 return transformc<Color3>(fromspace, tospace, color, ctx);
742 }
743
744
745
746 OSL_HOSTDEVICE Color3
blackbody_rgb(float T)747 ColorSystem::blackbody_rgb (float T)
748 {
749 if (T < BB_DRAPER)
750 return Color3(1.0e-6f,0.0f,0.0f); // very very dim red
751 if (T < BB_MAX_TABLE_RANGE) {
752 float t = BB_TABLE_UNMAP(T);
753 int ti = (int)t;
754 t -= ti;
755 Color3 rgb = OIIO::lerp (m_blackbody_table[ti], m_blackbody_table[ti+1], t);
756 //return colpow(rgb, BB_TABLE_YPOWER);
757 Color3 rgb2 = rgb * rgb;
758 Color3 rgb4 = rgb2 * rgb2;
759 return rgb4 * rgb; // ^5
760 }
761 // Otherwise, compute for real
762 bb_spectrum spec (T);
763 Color3 rgb = XYZ_to_RGB (spectrum_to_XYZ (spec));
764 clamp_zero (rgb);
765 return rgb;
766 }
767
768
769
770 } // namespace pvt
771
772
773
osl_blackbody_vf(void * sg,void * out,float temp)774 OSL_SHADEOP OSL_HOSTDEVICE void osl_blackbody_vf (void *sg, void *out, float temp)
775 {
776 ColorSystem &cs = op_color_colorsystem(sg);
777 *(Color3 *)out = cs.blackbody_rgb (temp);
778 }
779
780
781
osl_wavelength_color_vf(void * sg,void * out,float lambda)782 OSL_SHADEOP OSL_HOSTDEVICE void osl_wavelength_color_vf (void *sg, void *out, float lambda)
783 {
784 ColorSystem &cs = op_color_colorsystem(sg);
785 Color3 rgb = cs.XYZ_to_RGB (wavelength_color_XYZ (lambda));
786 // constrain_rgb (rgb);
787 rgb *= 1.0/2.52; // Empirical scale from lg to make all comps <= 1
788 // norm_rgb (rgb);
789 clamp_zero (rgb);
790 *(Color3 *)out = rgb;
791 }
792
793
794
osl_luminance_fv(void * sg,void * out,void * c)795 OSL_SHADEOP OSL_HOSTDEVICE void osl_luminance_fv (void *sg, void *out, void *c)
796 {
797 ColorSystem &cs = op_color_colorsystem(sg);
798 ((float *)out)[0] = cs.luminance (((const Color3 *)c)[0]);
799 }
800
801
802
osl_luminance_dfdv(void * sg,void * out,void * c)803 OSL_SHADEOP OSL_HOSTDEVICE void osl_luminance_dfdv (void *sg, void *out, void *c)
804 {
805 ColorSystem &cs = op_color_colorsystem(sg);
806 ((float *)out)[0] = cs.luminance (((const Color3 *)c)[0]);
807 ((float *)out)[1] = cs.luminance (((const Color3 *)c)[1]);
808 ((float *)out)[2] = cs.luminance (((const Color3 *)c)[2]);
809 }
810
811
812
813 OSL_SHADEOP OSL_HOSTDEVICE void
osl_prepend_color_from(void * sg,void * c_,const char * from)814 osl_prepend_color_from (void *sg, void *c_, const char *from)
815 {
816 ColorSystem &cs = op_color_colorsystem(sg);
817 COL(c_) = cs.to_rgb (HDSTR(from), COL(c_), op_color_context(sg));
818 }
819
820
821
822 OSL_SHADEOP OSL_HOSTDEVICE int
osl_transformc(void * sg,void * Cin,int Cin_derivs,void * Cout,int Cout_derivs,void * from_,void * to_)823 osl_transformc (void *sg, void *Cin, int Cin_derivs,
824 void *Cout, int Cout_derivs,
825 void *from_, void *to_)
826 {
827 ColorSystem &cs = op_color_colorsystem(sg);
828 StringParam from = HDSTR(from_);
829 StringParam to = HDSTR(to_);
830
831 if (Cout_derivs) {
832 if (Cin_derivs) {
833 DCOL(Cout) = cs.transformc (from, to, DCOL(Cin), op_color_context(sg));
834 return true;
835 } else {
836 // We had output derivs, but not input. Zero the output
837 // derivs and fall through to the non-deriv case.
838 ((Color3 *)Cout)[1].setValue (0.0f, 0.0f, 0.0f);
839 ((Color3 *)Cout)[2].setValue (0.0f, 0.0f, 0.0f);
840 }
841 }
842
843 // No-derivs case
844 COL(Cout) = cs.transformc (from, to, COL(Cin), op_color_context(sg));
845 return true;
846 }
847
848
849
850 OSL_NAMESPACE_EXIT
851