svm_ies.h 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. /*
  2. * Copyright 2011-2013 Blender Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. CCL_NAMESPACE_BEGIN
  17. /* IES Light */
  18. ccl_device_inline float interpolate_ies_vertical(
  19. KernelGlobals *kg, int ofs, int v, int v_num, float v_frac, int h)
  20. {
  21. /* Since lookups are performed in spherical coordinates, clamping the coordinates at the low end
  22. * of v (corresponding to the north pole) would result in artifacts. The proper way of dealing
  23. * with this would be to lookup the corresponding value on the other side of the pole, but since
  24. * the horizontal coordinates might be nonuniform, this would require yet another interpolation.
  25. * Therefore, the assumtion is made that the light is going to be symmetrical, which means that
  26. * we can just take the corresponding value at the current horizontal coordinate. */
  27. #define IES_LOOKUP(v) kernel_tex_fetch(__ies, ofs + h * v_num + (v))
  28. /* If v is zero, assume symmetry and read at v=1 instead of v=-1. */
  29. float a = IES_LOOKUP((v == 0) ? 1 : v - 1);
  30. float b = IES_LOOKUP(v);
  31. float c = IES_LOOKUP(v + 1);
  32. float d = IES_LOOKUP(min(v + 2, v_num - 1));
  33. #undef IES_LOOKUP
  34. return cubic_interp(a, b, c, d, v_frac);
  35. }
  36. ccl_device_inline float kernel_ies_interp(KernelGlobals *kg,
  37. int slot,
  38. float h_angle,
  39. float v_angle)
  40. {
  41. /* Find offset of the IES data in the table. */
  42. int ofs = __float_as_int(kernel_tex_fetch(__ies, slot));
  43. if (ofs == -1) {
  44. return 100.0f;
  45. }
  46. int h_num = __float_as_int(kernel_tex_fetch(__ies, ofs++));
  47. int v_num = __float_as_int(kernel_tex_fetch(__ies, ofs++));
  48. #define IES_LOOKUP_ANGLE_H(h) kernel_tex_fetch(__ies, ofs + (h))
  49. #define IES_LOOKUP_ANGLE_V(v) kernel_tex_fetch(__ies, ofs + h_num + (v))
  50. /* Check whether the angle is within the bounds of the IES texture. */
  51. if (v_angle >= IES_LOOKUP_ANGLE_V(v_num - 1)) {
  52. return 0.0f;
  53. }
  54. kernel_assert(v_angle >= IES_LOOKUP_ANGLE_V(0));
  55. kernel_assert(h_angle >= IES_LOOKUP_ANGLE_H(0));
  56. kernel_assert(h_angle <= IES_LOOKUP_ANGLE_H(h_num - 1));
  57. /* Lookup the angles to find the table position. */
  58. int h_i, v_i;
  59. /* TODO(lukas): Consider using bisection.
  60. * Probably not worth it for the vast majority of IES files. */
  61. for (h_i = 0; IES_LOOKUP_ANGLE_H(h_i + 1) < h_angle; h_i++)
  62. ;
  63. for (v_i = 0; IES_LOOKUP_ANGLE_V(v_i + 1) < v_angle; v_i++)
  64. ;
  65. float h_frac = inverse_lerp(IES_LOOKUP_ANGLE_H(h_i), IES_LOOKUP_ANGLE_H(h_i + 1), h_angle);
  66. float v_frac = inverse_lerp(IES_LOOKUP_ANGLE_V(v_i), IES_LOOKUP_ANGLE_V(v_i + 1), v_angle);
  67. #undef IES_LOOKUP_ANGLE_H
  68. #undef IES_LOOKUP_ANGLE_V
  69. /* Skip forward to the actual intensity data. */
  70. ofs += h_num + v_num;
  71. /* Perform cubic interpolation along the horizontal coordinate to get the intensity value.
  72. * If h_i is zero, just wrap around since the horizontal angles always go over the full circle.
  73. * However, the last entry (360°) equals the first one, so we need to wrap around to the one
  74. * before that. */
  75. float a = interpolate_ies_vertical(
  76. kg, ofs, v_i, v_num, v_frac, (h_i == 0) ? h_num - 2 : h_i - 1);
  77. float b = interpolate_ies_vertical(kg, ofs, v_i, v_num, v_frac, h_i);
  78. float c = interpolate_ies_vertical(kg, ofs, v_i, v_num, v_frac, h_i + 1);
  79. /* Same logic here, wrap around to the second element if necessary. */
  80. float d = interpolate_ies_vertical(
  81. kg, ofs, v_i, v_num, v_frac, (h_i + 2 == h_num) ? 1 : h_i + 2);
  82. /* Cubic interpolation can result in negative values, so get rid of them. */
  83. return max(cubic_interp(a, b, c, d, h_frac), 0.0f);
  84. }
  85. ccl_device void svm_node_ies(
  86. KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset)
  87. {
  88. uint vector_offset, strength_offset, fac_offset, dummy, slot = node.z;
  89. decode_node_uchar4(node.y, &strength_offset, &vector_offset, &fac_offset, &dummy);
  90. float3 vector = stack_load_float3(stack, vector_offset);
  91. float strength = stack_load_float_default(stack, strength_offset, node.w);
  92. vector = normalize(vector);
  93. float v_angle = safe_acosf(-vector.z);
  94. float h_angle = atan2f(vector.x, vector.y) + M_PI_F;
  95. float fac = strength * kernel_ies_interp(kg, slot, h_angle, v_angle);
  96. if (stack_valid(fac_offset)) {
  97. stack_store_float(stack, fac_offset, fac);
  98. }
  99. }
  100. CCL_NAMESPACE_END