Cycles / Wavelength to RGB node:
authorThomas Dinges <blender@dingto.org>
Mon, 10 Jun 2013 21:55:41 +0000 (21:55 +0000)
committerThomas Dinges <blender@dingto.org>
Mon, 10 Jun 2013 21:55:41 +0000 (21:55 +0000)
* Added a node to convert wavelength (in nanometers, from 380nm to 780nm) to RGB values. This can be useful to match real world colors easier.

* Code cleanup:
** Moved color functions (xyz and hsv) into dedicated utility files.
** Remove svm_lerp(), use interp() instead.

Documentation:
http://wiki.blender.org/index.php/Doc:2.6/Manual/Render/Cycles/Nodes/More#Wavelength

Example render:
http://www.pasteall.org/pic/show.php?id=53202

This is part of my GSoC 2013. (revisions 57322, 57326, 57335 and 57367 from soc-2013-dingto).

1  2 
intern/cycles/blender/blender_shader.cpp
intern/cycles/kernel/shaders/node_color.h
intern/cycles/kernel/shaders/node_sky_texture.osl
intern/cycles/kernel/svm/svm_hsv.h
intern/cycles/kernel/svm/svm_mix.h
intern/cycles/kernel/svm/svm_sky.h
intern/cycles/kernel/svm/svm_wavelength.h
intern/cycles/util/util_color.h

index c6b5d740f6a08e58571ba86b14665e322a6608ad,c6b5d740f6a08e58571ba86b14665e322a6608ad..92bd39b5828fb3449fffe37405ea366ddd3812a9
@@@ -58,6 -58,6 +58,26 @@@ color color_unpremultiply(color c, floa
  
  /* Color Operations */
  
++color xyY_to_xyz(float x, float y, float Y)
++{
++      float X, Z;
++
++      if (y != 0.0) X = (x / y) * Y;
++      else X = 0.0;
++
++      if (y != 0.0 && Y != 0.0) Z = ((1.0 - x - y) / y) * Y;
++      else Z = 0.0;
++
++      return color(X, Y, Z);
++}
++
++color xyz_to_rgb(float x, float y, float z)
++{
++      return color( 3.240479 * x + -1.537150 * y + -0.498535 * z,
++                   -0.969256 * x +  1.875991 * y +  0.041556 * z,
++                    0.055648 * x + -0.204043 * y +  1.057311 * z);
++}
++
  color rgb_to_hsv(color rgb)
  {
        float cmax, cmin, h, s, v, cdelta;
index e9f7dfb3a2abfa8da8b48f1932d5340b8821b823,e9f7dfb3a2abfa8da8b48f1932d5340b8821b823..61788799d99c7a62de569a058ae52fa05256e36d
@@@ -17,6 -17,6 +17,7 @@@
   */
  
  #include "stdosl.h"
++#include "node_color.h"
  
  struct KernelSunSky {
        /* sun direction in spherical and cartesian */
        float perez_Y[5], perez_x[5], perez_y[5];
  };
  
--color xyY_to_xyz(float x, float y, float Y)
--{
--      float X, Z;
--
--      if (y != 0.0) X = (x / y) * Y;
--      else X = 0.0;
--
--      if (y != 0.0 && Y != 0.0) Z = ((1.0 - x - y) / y) * Y;
--      else Z = 0.0;
--
--      return color(X, Y, Z);
--}
--
--color xyz_to_rgb(float x, float y, float z)
--{
--      return color( 3.240479 * x + -1.537150 * y + -0.498535 * z,
--                   -0.969256 * x +  1.875991 * y +  0.041556 * z,
--                    0.055648 * x + -0.204043 * y +  1.057311 * z);
--}
--
  float sky_angle_between(float thetav, float phiv, float theta, float phi)
  {
        float cospsi = sin(thetav) * sin(theta) * cos(phi - phiv) + cos(thetav) * cos(theta);
index 348f13f59f259860a8ea582b02bcf08cbd750184,348f13f59f259860a8ea582b02bcf08cbd750184..6e6a9dff159663bcfcddb9f4b1bfb5654971156f
  
  CCL_NAMESPACE_BEGIN
  
--__device float3 rgb_to_hsv(float3 rgb)
--{
--      float cmax, cmin, h, s, v, cdelta;
--      float3 c;
--
--      cmax = fmaxf(rgb.x, fmaxf(rgb.y, rgb.z));
--      cmin = min(rgb.x, min(rgb.y, rgb.z));
--      cdelta = cmax - cmin;
--
--      v = cmax;
--
--      if(cmax != 0.0f) {
--              s = cdelta/cmax;
--      }
--      else {
--              s = 0.0f;
--              h = 0.0f;
--      }
--
--      if(s == 0.0f) {
--              h = 0.0f;
--      }
--      else {
--              float3 cmax3 = make_float3(cmax, cmax, cmax);
--              c = (cmax3 - rgb)/cdelta;
--
--              if(rgb.x == cmax) h = c.z - c.y;
--              else if(rgb.y == cmax) h = 2.0f + c.x -  c.z;
--              else h = 4.0f + c.y - c.x;
--
--              h /= 6.0f;
--
--              if(h < 0.0f)
--                      h += 1.0f;
--      }
--
--      return make_float3(h, s, v);
--}
--
--__device float3 hsv_to_rgb(float3 hsv)
--{
--      float i, f, p, q, t, h, s, v;
--      float3 rgb;
--
--      h = hsv.x;
--      s = hsv.y;
--      v = hsv.z;
--
--      if(s == 0.0f) {
--              rgb = make_float3(v, v, v);
--      }
--      else {
--              if(h == 1.0f)
--                      h = 0.0f;
--              
--              h *= 6.0f;
--              i = floorf(h);
--              f = h - i;
--              rgb = make_float3(f, f, f);
--              p = v*(1.0f-s);
--              q = v*(1.0f-(s*f));
--              t = v*(1.0f-(s*(1.0f-f)));
--              
--              if(i == 0.0f) rgb = make_float3(v, t, p);
--              else if(i == 1.0f) rgb = make_float3(q, v, p);
--              else if(i == 2.0f) rgb = make_float3(p, v, t);
--              else if(i == 3.0f) rgb = make_float3(p, q, v);
--              else if(i == 4.0f) rgb = make_float3(t, p, v);
--              else rgb = make_float3(v, p, q);
--      }
--
--      return rgb;
--}
--
  __device void svm_node_hsv(KernelGlobals *kg, ShaderData *sd, float *stack, uint in_color_offset, uint fac_offset, uint out_color_offset, int *offset)
  {
        /* read extra data */
index 888e4d9645e82b633e1349dc248c06c899205ae1,888e4d9645e82b633e1349dc248c06c899205ae1..d6a306af64dccb795cf9f253843c2dcf792d946e
   * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
   */
  
--#include "svm_hsv.h"
--
  CCL_NAMESPACE_BEGIN
  
--__device float3 svm_lerp(const float3 a, const float3 b, float t)
--{
--      return (a * (1.0f - t) + b * t);
--}
--
  __device float3 svm_mix_blend(float t, float3 col1, float3 col2)
  {
--      return svm_lerp(col1, col2, t);
++      return interp(col1, col2, t);
  }
  
  __device float3 svm_mix_add(float t, float3 col1, float3 col2)
  {
--      return svm_lerp(col1, col1 + col2, t);
++      return interp(col1, col1 + col2, t);
  }
  
  __device float3 svm_mix_mul(float t, float3 col1, float3 col2)
  {
--      return svm_lerp(col1, col1 * col2, t);
++      return interp(col1, col1 * col2, t);
  }
  
  __device float3 svm_mix_screen(float t, float3 col1, float3 col2)
@@@ -75,7 -75,7 +68,7 @@@ __device float3 svm_mix_overlay(float t
  
  __device float3 svm_mix_sub(float t, float3 col1, float3 col2)
  {
--      return svm_lerp(col1, col1 - col2, t);
++      return interp(col1, col1 - col2, t);
  }
  
  __device float3 svm_mix_div(float t, float3 col1, float3 col2)
@@@ -93,7 -93,7 +86,7 @@@
  
  __device float3 svm_mix_diff(float t, float3 col1, float3 col2)
  {
--      return svm_lerp(col1, fabs(col1 - col2), t);
++      return interp(col1, fabs(col1 - col2), t);
  }
  
  __device float3 svm_mix_dark(float t, float3 col1, float3 col2)
@@@ -191,7 -191,7 +184,7 @@@ __device float3 svm_mix_hue(float t, fl
                hsv.x = hsv2.x;
                float3 tmp = hsv_to_rgb(hsv); 
  
--              outcol = svm_lerp(outcol, tmp, t);
++              outcol = interp(outcol, tmp, t);
        }
  
        return outcol;
@@@ -238,7 -238,7 +231,7 @@@ __device float3 svm_mix_color(float t, 
                hsv.y = hsv2.y;
                float3 tmp = hsv_to_rgb(hsv); 
  
--              outcol = svm_lerp(outcol, tmp, t);
++              outcol = interp(outcol, tmp, t);
        }
  
        return outcol;
index eaba4d183650f50fd41831ace156a5c2218f9e37,eaba4d183650f50fd41831ace156a5c2218f9e37..8b4e35816d09cf240cacc12840bb823f81ce9690
  
  CCL_NAMESPACE_BEGIN
  
--__device float3 xyY_to_xyz(float x, float y, float Y)
--{
--      float X, Z;
--
--      if(y != 0.0f) X = (x / y) * Y;
--      else X = 0.0f;
--
--      if(y != 0.0f && Y != 0.0f) Z = (1.0f - x - y) / y * Y;
--      else Z = 0.0f;
--
--      return make_float3(X, Y, Z);
--}
--
--__device float3 xyz_to_rgb(float x, float y, float z)
--{
--      return make_float3(3.240479f * x + -1.537150f * y + -0.498535f * z,
--                                        -0.969256f * x +  1.875991f * y +  0.041556f * z,
--                                         0.055648f * x + -0.204043f * y +  1.057311f * z);
--}
--
  /*
   * "A Practical Analytic Model for Daylight"
   * A. J. Preetham, Peter Shirley, Brian Smits
index 0000000000000000000000000000000000000000,2ae17b8a224069f4e560ae1f9fa62c00893d2a3e..f9dd24daceff8569c56b31b762c1a31571f77f6f
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,113 +1,100 @@@
 - *   notice, this list of conditions and the following disclaimer.
+ /*
+  * Adapted from Open Shading Language with this license:
+  *
+  * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
+  * All Rights Reserved.
+  *
+  * Modifications Copyright 2013, Blender Foundation.
+  * 
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions are
+  * met:
+  * * Redistributions of source code must retain the above copyright
 - *   notice, this list of conditions and the following disclaimer in the
 - *   documentation and/or other materials provided with the distribution.
++ *     notice, this list of conditions and the following disclaimer.
+  * * Redistributions in binary form must reproduce the above copyright
 - *   contributors may be used to endorse or promote products derived from
 - *   this software without specific prior written permission.
++ *     notice, this list of conditions and the following disclaimer in the
++ *     documentation and/or other materials provided with the distribution.
+  * * Neither the name of Sony Pictures Imageworks nor the names of its
 -/* ToDo: Move these 2 functions to an util file */
 -__device float3 xyz_to_rgb_wave(float x, float y, float z)
 -{
 -      return make_float3(3.240479f * x + -1.537150f * y + -0.498535f * z,
 -                                        -0.969256f * x +  1.875991f * y +  0.041556f * z,
 -                                         0.055648f * x + -0.204043f * y +  1.057311f * z);
 -}
 -
 -__device float3 wavelength_lerp(const float3 a, const float3 b, float t)
 -{
 -      return (a * (1.0f - t) + b * t);
 -}
 -
++ *     contributors may be used to endorse or promote products derived from
++ *     this software without specific prior written permission.
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  */
+ CCL_NAMESPACE_BEGIN
+ /* Wavelength to RGB */
 -      //   wavelengths from 380 through 780 nanometers, every 5
 -      //   nanometers.  For a wavelength lambda in this range:
 -      //        cie_colour_match[(lambda - 380) / 5][0] = xBar
 -      //        cie_colour_match[(lambda - 380) / 5][1] = yBar
 -      //        cie_colour_match[(lambda - 380) / 5][2] = zBar
 -      float cie_colour_match[81][3] = {
+ __device void svm_node_wavelength(ShaderData *sd, float *stack, uint wavelength, uint color_out)
+ {     
+       // CIE colour matching functions xBar, yBar, and zBar for
 -      float3 rgb;
++      //       wavelengths from 380 through 780 nanometers, every 5
++      //       nanometers.  For a wavelength lambda in this range:
++      //                cie_colour_match[(lambda - 380) / 5][0] = xBar
++      //                cie_colour_match[(lambda - 380) / 5][1] = yBar
++      //                cie_colour_match[(lambda - 380) / 5][2] = zBar
++      const float cie_colour_match[81][3] = {
+               {0.0014,0.0000,0.0065}, {0.0022,0.0001,0.0105}, {0.0042,0.0001,0.0201},
+               {0.0076,0.0002,0.0362}, {0.0143,0.0004,0.0679}, {0.0232,0.0006,0.1102},
+               {0.0435,0.0012,0.2074}, {0.0776,0.0022,0.3713}, {0.1344,0.0040,0.6456},
+               {0.2148,0.0073,1.0391}, {0.2839,0.0116,1.3856}, {0.3285,0.0168,1.6230},
+               {0.3483,0.0230,1.7471}, {0.3481,0.0298,1.7826}, {0.3362,0.0380,1.7721},
+               {0.3187,0.0480,1.7441}, {0.2908,0.0600,1.6692}, {0.2511,0.0739,1.5281},
+               {0.1954,0.0910,1.2876}, {0.1421,0.1126,1.0419}, {0.0956,0.1390,0.8130},
+               {0.0580,0.1693,0.6162}, {0.0320,0.2080,0.4652}, {0.0147,0.2586,0.3533},
+               {0.0049,0.3230,0.2720}, {0.0024,0.4073,0.2123}, {0.0093,0.5030,0.1582},
+               {0.0291,0.6082,0.1117}, {0.0633,0.7100,0.0782}, {0.1096,0.7932,0.0573},
+               {0.1655,0.8620,0.0422}, {0.2257,0.9149,0.0298}, {0.2904,0.9540,0.0203},
+               {0.3597,0.9803,0.0134}, {0.4334,0.9950,0.0087}, {0.5121,1.0000,0.0057},
+               {0.5945,0.9950,0.0039}, {0.6784,0.9786,0.0027}, {0.7621,0.9520,0.0021},
+               {0.8425,0.9154,0.0018}, {0.9163,0.8700,0.0017}, {0.9786,0.8163,0.0014},
+               {1.0263,0.7570,0.0011}, {1.0567,0.6949,0.0010}, {1.0622,0.6310,0.0008},
+               {1.0456,0.5668,0.0006}, {1.0026,0.5030,0.0003}, {0.9384,0.4412,0.0002},
+               {0.8544,0.3810,0.0002}, {0.7514,0.3210,0.0001}, {0.6424,0.2650,0.0000},
+               {0.5419,0.2170,0.0000}, {0.4479,0.1750,0.0000}, {0.3608,0.1382,0.0000},
+               {0.2835,0.1070,0.0000}, {0.2187,0.0816,0.0000}, {0.1649,0.0610,0.0000},
+               {0.1212,0.0446,0.0000}, {0.0874,0.0320,0.0000}, {0.0636,0.0232,0.0000},
+               {0.0468,0.0170,0.0000}, {0.0329,0.0119,0.0000}, {0.0227,0.0082,0.0000},
+               {0.0158,0.0057,0.0000}, {0.0114,0.0041,0.0000}, {0.0081,0.0029,0.0000},
+               {0.0058,0.0021,0.0000}, {0.0041,0.0015,0.0000}, {0.0029,0.0010,0.0000},
+               {0.0020,0.0007,0.0000}, {0.0014,0.0005,0.0000}, {0.0010,0.0004,0.0000},
+               {0.0007,0.0002,0.0000}, {0.0005,0.0002,0.0000}, {0.0003,0.0001,0.0000},
+               {0.0002,0.0001,0.0000}, {0.0002,0.0001,0.0000}, {0.0001,0.0000,0.0000},
+               {0.0001,0.0000,0.0000}, {0.0001,0.0000,0.0000}, {0.0000,0.0000,0.0000}
+       };
+       float lambda_nm = stack_load_float(stack, wavelength);
 -    float ii = (lambda_nm-380.0f) / 5.0f;  // scaled 0..80
 -    int i = float_to_int(ii);
 -    if (i < 0 || i >= 80) {
 -        rgb = make_float3(0.0f, 0.0f, 0.0f);
++      float ii = (lambda_nm-380.0f) * (1.0f/5.0f);  // scaled 0..80
++      int i = float_to_int(ii);
++      float3 color;
+       
 -              float *c = cie_colour_match[i];
 -              rgb = wavelength_lerp(make_float3(c[0], c[1], c[2]), make_float3(c[3], c[4], c[5]), ii);
++      if (i < 0 || i >= 80) {
++              color = make_float3(0.0f, 0.0f, 0.0f);
+       }
+       else {
+               ii -= i;
 -      rgb = xyz_to_rgb_wave(rgb.x, rgb.y, rgb.z);
 -      rgb *= 1.0/2.52;    // Empirical scale from lg to make all comps <= 1
++              const float *c = cie_colour_match[i];
++              color = interp(make_float3(c[0], c[1], c[2]), make_float3(c[3], c[4], c[5]), ii);
+       }
+       
 -      /* Clamp to Zero if values are smaller */
 -      rgb = max(rgb, make_float3(0.0f, 0.0f, 0.0f));
++      color = xyz_to_rgb(color.x, color.y, color.z);
++      color *= 1.0f/2.52f;    // Empirical scale from lg to make all comps <= 1
+       
 -              stack_store_float3(stack, color_out, rgb);
++      /* Clamp to zero if values are smaller */
++      color = max(color, make_float3(0.0f, 0.0f, 0.0f));
+       if(stack_valid(color_out))
++              stack_store_float3(stack, color_out, color);
+ }
+ CCL_NAMESPACE_END
index 5136ea5c5db582919b8aebd210b2886d9307c3b5,5136ea5c5db582919b8aebd210b2886d9307c3b5..0de29371899721ba43a844f213ba431c446fea37
@@@ -40,6 -40,6 +40,100 @@@ __device float color_scene_linear_to_sr
                return 1.055f * powf(c, 1.0f / 2.4f) - 0.055f;
  }
  
++__device float3 rgb_to_hsv(float3 rgb)
++{
++      float cmax, cmin, h, s, v, cdelta;
++      float3 c;
++
++      cmax = fmaxf(rgb.x, fmaxf(rgb.y, rgb.z));
++      cmin = min(rgb.x, min(rgb.y, rgb.z));
++      cdelta = cmax - cmin;
++
++      v = cmax;
++
++      if(cmax != 0.0f) {
++              s = cdelta/cmax;
++      }
++      else {
++              s = 0.0f;
++              h = 0.0f;
++      }
++
++      if(s == 0.0f) {
++              h = 0.0f;
++      }
++      else {
++              float3 cmax3 = make_float3(cmax, cmax, cmax);
++              c = (cmax3 - rgb)/cdelta;
++
++              if(rgb.x == cmax) h = c.z - c.y;
++              else if(rgb.y == cmax) h = 2.0f + c.x -  c.z;
++              else h = 4.0f + c.y - c.x;
++
++              h /= 6.0f;
++
++              if(h < 0.0f)
++                      h += 1.0f;
++      }
++
++      return make_float3(h, s, v);
++}
++
++__device float3 hsv_to_rgb(float3 hsv)
++{
++      float i, f, p, q, t, h, s, v;
++      float3 rgb;
++
++      h = hsv.x;
++      s = hsv.y;
++      v = hsv.z;
++
++      if(s == 0.0f) {
++              rgb = make_float3(v, v, v);
++      }
++      else {
++              if(h == 1.0f)
++                      h = 0.0f;
++              
++              h *= 6.0f;
++              i = floorf(h);
++              f = h - i;
++              rgb = make_float3(f, f, f);
++              p = v*(1.0f-s);
++              q = v*(1.0f-(s*f));
++              t = v*(1.0f-(s*(1.0f-f)));
++              
++              if(i == 0.0f) rgb = make_float3(v, t, p);
++              else if(i == 1.0f) rgb = make_float3(q, v, p);
++              else if(i == 2.0f) rgb = make_float3(p, v, t);
++              else if(i == 3.0f) rgb = make_float3(p, q, v);
++              else if(i == 4.0f) rgb = make_float3(t, p, v);
++              else rgb = make_float3(v, p, q);
++      }
++
++      return rgb;
++}
++
++__device float3 xyY_to_xyz(float x, float y, float Y)
++{
++      float X, Z;
++
++      if(y != 0.0f) X = (x / y) * Y;
++      else X = 0.0f;
++
++      if(y != 0.0f && Y != 0.0f) Z = (1.0f - x - y) / y * Y;
++      else Z = 0.0f;
++
++      return make_float3(X, Y, Z);
++}
++
++__device float3 xyz_to_rgb(float x, float y, float z)
++{
++      return make_float3(3.240479f * x + -1.537150f * y + -0.498535f * z,
++                                        -0.969256f * x +  1.875991f * y +  0.041556f * z,
++                                         0.055648f * x + -0.204043f * y +  1.057311f * z);
++}
++
  #ifndef __KERNEL_OPENCL__
  
  __device float3 color_srgb_to_scene_linear(float3 c)