Blender V4.5
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 float a = 0.0f;
31 if (v > 0) {
32 a = IES_LOOKUP(v - 1);
33 }
34 else if (wrap_vlow) {
35 a = IES_LOOKUP(1);
36 }
37 const float b = IES_LOOKUP(v);
38 const float c = IES_LOOKUP(v + 1);
39 float d = 0.0f;
40 if (v + 2 < v_num) {
41 d = IES_LOOKUP(v + 2);
42 }
43 else if (wrap_vhigh) {
44 d = IES_LOOKUP(v_num - 2);
45 }
46#undef IES_LOOKUP
47
48 return cubic_interp(a, b, c, d, v_frac);
49}
50
52 const int slot,
53 const float h_angle,
54 const float v_angle)
55{
56 /* Find offset of the IES data in the table. */
57 int ofs = __float_as_int(kernel_data_fetch(ies, slot));
58 if (ofs == -1) {
59 return 100.0f;
60 }
61
62 const int h_num = __float_as_int(kernel_data_fetch(ies, ofs++));
63 const int v_num = __float_as_int(kernel_data_fetch(ies, ofs++));
64
65#define IES_LOOKUP_ANGLE_H(h) kernel_data_fetch(ies, ofs + (h))
66#define IES_LOOKUP_ANGLE_V(v) kernel_data_fetch(ies, ofs + h_num + (v))
67
68 /* Check whether the angle is within the bounds of the IES texture. */
69 const float v_low = IES_LOOKUP_ANGLE_V(0);
70 const float v_high = IES_LOOKUP_ANGLE_V(v_num - 1);
71 const float h_low = IES_LOOKUP_ANGLE_H(0);
72 const float h_high = IES_LOOKUP_ANGLE_H(h_num - 1);
73 if (v_angle < v_low || v_angle >= v_high) {
74 return 0.0f;
75 }
76 if (h_angle < h_low || h_angle >= h_high) {
77 return 0.0f;
78 }
79
80 /* If the texture covers the full 360° range horizontally, wrap around the lookup
81 * to get proper cubic interpolation. Otherwise, just set the out-of-range values to zero.
82 * Similar logic for V, but there we check the lower and upper wrap separately. */
83 const bool wrap_h = (h_low < 1e-7f && h_high > M_2PI_F - 1e-7f);
84 const bool wrap_vlow = (v_low < 1e-7f);
85 const bool wrap_vhigh = (v_high > M_PI_F - 1e-7f);
86
87 /* Lookup the angles to find the table position. */
88 int h_i;
89 int v_i;
90 /* TODO(lukas): Consider using bisection.
91 * Probably not worth it for the vast majority of IES files. */
92 for (h_i = 0; IES_LOOKUP_ANGLE_H(h_i + 1) < h_angle; h_i++) {
93 ;
94 }
95 for (v_i = 0; IES_LOOKUP_ANGLE_V(v_i + 1) < v_angle; v_i++) {
96 ;
97 }
98
99 const float h_frac = inverse_lerp(IES_LOOKUP_ANGLE_H(h_i), IES_LOOKUP_ANGLE_H(h_i + 1), h_angle);
100 const float v_frac = inverse_lerp(IES_LOOKUP_ANGLE_V(v_i), IES_LOOKUP_ANGLE_V(v_i + 1), v_angle);
101
102#undef IES_LOOKUP_ANGLE_H
103#undef IES_LOOKUP_ANGLE_V
104
105 /* Skip forward to the actual intensity data. */
106 ofs += h_num + v_num;
107
108 float a = 0.0f;
109 if (h_i > 0) {
110 a = interpolate_ies_vertical(kg, ofs, wrap_vlow, wrap_vhigh, v_i, v_num, v_frac, h_i - 1);
111 }
112 else if (wrap_h) {
113 /* The last entry (360°) equals the first one, so we need to wrap around to the one before. */
114 a = interpolate_ies_vertical(kg, ofs, wrap_vlow, wrap_vhigh, v_i, v_num, v_frac, h_num - 2);
115 }
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 float d = 0.0f;
121 if (h_i + 2 < h_num) {
122 d = interpolate_ies_vertical(kg, ofs, wrap_vlow, wrap_vhigh, v_i, v_num, v_frac, h_i + 2);
123 }
124 else if (wrap_h) {
125 /* Same logic here, wrap around to the second element if necessary. */
126 d = interpolate_ies_vertical(kg, ofs, wrap_vlow, wrap_vhigh, v_i, v_num, v_frac, 1);
127 }
128
129 /* Cubic interpolation can result in negative values, so get rid of them. */
130 return max(cubic_interp(a, b, c, d, h_frac), 0.0f);
131}
132
ATTR_WARN_UNUSED_RESULT const BMVert * v
#define kernel_data_fetch(name, index)
#define M_2PI_F
const ThreadKernelGlobalsCPU * KernelGlobals
#define ccl_device_inline
#define M_PI_F
#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
max
Definition text_draw.cc:251