Blender V5.0
kernel/util/ies.h
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#pragma once
6
7#include "kernel/globals.h"
8
10
11/* IES Light */
12
14 const int ofs,
15 const bool wrap_vlow,
16 const bool wrap_vhigh,
17 const int v,
18 const int v_num,
19 const float v_frac,
20 const int h)
21{
22 /* Since lookups are performed in spherical coordinates, clamping the coordinates at the low end
23 * of v (corresponding to the north pole) would result in artifacts. The proper way of dealing
24 * with this would be to lookup the corresponding value on the other side of the pole, but since
25 * the horizontal coordinates might be nonuniform, this would require yet another interpolation.
26 * Therefore, the assumption is made that the light is going to be symmetrical, which means that
27 * we can just take the corresponding value at the current horizontal coordinate. */
28
29#define IES_LOOKUP(v) kernel_data_fetch(ies, ofs + h * v_num + (v))
30
31 /* Look up the inner two points directly. */
32 const float c = IES_LOOKUP(v + 1);
33 const float b = IES_LOOKUP(v);
34
35 /* Look up first point, or fall back to second point if not available. */
36 float a = b;
37 if (v > 0) {
38 a = IES_LOOKUP(v - 1);
39 }
40 else if (wrap_vlow) {
41 a = IES_LOOKUP(1);
42 }
43
44 /* Look up last point, or fall back to third point if not available. */
45 float d = c;
46 if (v + 2 < v_num) {
47 d = IES_LOOKUP(v + 2);
48 }
49 else if (wrap_vhigh) {
50 d = IES_LOOKUP(v_num - 2);
51 }
52
53#undef IES_LOOKUP
54
55 return cubic_interp(a, b, c, d, v_frac);
56}
57
59 const int slot,
60 const float h_angle,
61 const float v_angle)
62{
63 /* Find offset of the IES data in the table. */
64 int ofs = __float_as_int(kernel_data_fetch(ies, slot));
65 if (ofs == -1) {
66 return 100.0f;
67 }
68
69 const int h_num = __float_as_int(kernel_data_fetch(ies, ofs++));
70 const int v_num = __float_as_int(kernel_data_fetch(ies, ofs++));
71
72#define IES_LOOKUP_ANGLE_H(h) kernel_data_fetch(ies, ofs + (h))
73#define IES_LOOKUP_ANGLE_V(v) kernel_data_fetch(ies, ofs + h_num + (v))
74
75 /* Check whether the angle is within the bounds of the IES texture. */
76 const float v_low = IES_LOOKUP_ANGLE_V(0);
77 const float v_high = IES_LOOKUP_ANGLE_V(v_num - 1);
78 const float h_low = IES_LOOKUP_ANGLE_H(0);
79 const float h_high = IES_LOOKUP_ANGLE_H(h_num - 1);
80 if (v_angle < v_low || v_angle >= v_high) {
81 return 0.0f;
82 }
83 if (h_angle < h_low || h_angle >= h_high) {
84 return 0.0f;
85 }
86
87 /* If the texture covers the full 360° range horizontally, wrap around the lookup
88 * to get proper cubic interpolation. Otherwise, just set the out-of-range values to zero.
89 * Similar logic for V, but there we check the lower and upper wrap separately. */
90 const bool wrap_h = (h_low < 1e-7f && h_high > M_2PI_F - 1e-7f);
91 const bool wrap_vlow = (v_low < 1e-7f);
92 const bool wrap_vhigh = (v_high > M_PI_F - 1e-7f);
93
94 /* Lookup the angles to find the table position. */
95 int h_i;
96 int v_i;
97 /* TODO(lukas): Consider using bisection.
98 * Probably not worth it for the vast majority of IES files. */
99 for (h_i = 0; IES_LOOKUP_ANGLE_H(h_i + 1) < h_angle; h_i++) {
100 ;
101 }
102 for (v_i = 0; IES_LOOKUP_ANGLE_V(v_i + 1) < v_angle; v_i++) {
103 ;
104 }
105
106 const float h_frac = inverse_lerp(IES_LOOKUP_ANGLE_H(h_i), IES_LOOKUP_ANGLE_H(h_i + 1), h_angle);
107 const float v_frac = inverse_lerp(IES_LOOKUP_ANGLE_V(v_i), IES_LOOKUP_ANGLE_V(v_i + 1), v_angle);
108
109#undef IES_LOOKUP_ANGLE_H
110#undef IES_LOOKUP_ANGLE_V
111
112 /* Skip forward to the actual intensity data. */
113 ofs += h_num + v_num;
114
115 /* Interpolate the inner two points directly. */
116 const float b = interpolate_ies_vertical(
117 kg, ofs, wrap_vlow, wrap_vhigh, v_i, v_num, v_frac, h_i);
118 const float c = interpolate_ies_vertical(
119 kg, ofs, wrap_vlow, wrap_vhigh, v_i, v_num, v_frac, h_i + 1);
120
121 /* Interpolate first point, or fall back to second point if not available. */
122 float a = b;
123 if (h_i > 0) {
124 a = interpolate_ies_vertical(kg, ofs, wrap_vlow, wrap_vhigh, v_i, v_num, v_frac, h_i - 1);
125 }
126 else if (wrap_h) {
127 /* The last entry (360°) equals the first one, so we need to wrap around to the one before. */
128 a = interpolate_ies_vertical(kg, ofs, wrap_vlow, wrap_vhigh, v_i, v_num, v_frac, h_num - 2);
129 }
130
131 /* Interpolate last point, or fall back to second point if not available. */
132 float d = b;
133 if (h_i + 2 < h_num) {
134 d = interpolate_ies_vertical(kg, ofs, wrap_vlow, wrap_vhigh, v_i, v_num, v_frac, h_i + 2);
135 }
136 else if (wrap_h) {
137 /* Same logic here, wrap around to the second element if necessary. */
138 d = interpolate_ies_vertical(kg, ofs, wrap_vlow, wrap_vhigh, v_i, v_num, v_frac, 1);
139 }
140
141 /* Cubic interpolation can result in negative values, so get rid of them. */
142 return max(cubic_interp(a, b, c, d, h_frac), 0.0f);
143}
144
ATTR_WARN_UNUSED_RESULT const BMVert * v
#define kernel_data_fetch(name, index)
const ThreadKernelGlobalsCPU * KernelGlobals
#define ccl_device_inline
#define CCL_NAMESPACE_END
#define __float_as_int(x)
#define IES_LOOKUP_ANGLE_V(v)
#define IES_LOOKUP(v)
#define IES_LOOKUP_ANGLE_H(h)
CCL_NAMESPACE_BEGIN ccl_device_inline float interpolate_ies_vertical(KernelGlobals kg, const int ofs, const bool wrap_vlow, const bool wrap_vhigh, const int v, const int v_num, const float v_frac, const int h)
ccl_device_inline float kernel_ies_interp(KernelGlobals kg, const int slot, const float h_angle, const float v_angle)
ccl_device_inline float cubic_interp(const float a, const float b, float c, const float d, float x)
Definition math_base.h:513
ccl_device_inline float inverse_lerp(const float a, const float b, const float x)
Definition math_base.h:507
#define M_2PI_F
#define M_PI_F
max
Definition text_draw.cc:251