From 48155c210a3fd65f6fc2b5be3357b47a8af3711a Mon Sep 17 00:00:00 2001 From: Lukas Stockner Date: Sun, 27 May 2018 00:46:37 +0200 Subject: Cycles: Add Support for IES files as textures for light strength This patch adds support for IES files, a file format that is commonly used to store the directional intensity distribution of light sources. The new IES node is supposed to be plugged into the Strength input of the Emission node of the lamp. Since people generating IES files do not really seem to care about the standard, the parser is flexible enough to accept all test files I have tried. Some common weirdnesses are distributing values over multiple lines that should go into one line, using commas instead of spaces as delimiters and adding various useless stuff at the end of the file. The user interface of the node is similar to the script node, the user can either select an internal Text or load a file. Internally, IES files are handled similar to Image textures: They are stored in slots by the LightManager and each unique IES is assigned to one slot. The local coordinate system of the lamp is used, so that the direction of the light can be changed. For UI reasons, it's usually best to add an area light, rotate it and then change its type, since especially the point light does not immediately show its local coordinate system in the viewport. Reviewers: #cycles, dingto, sergey, brecht Reviewed By: #cycles, dingto, brecht Subscribers: OgDEV, crazyrobinhood, secundar, cardboard, pisuke, intrah, swerner, micah_denn, harvester, gottfried, disnel, campbellbarton, duarteframos, Lapineige, brecht, juicyfruit, dingto, marek, rickyblender, bliblubli, lockal, sergey Differential Revision: https://developer.blender.org/D1543 --- intern/cycles/kernel/CMakeLists.txt | 1 + intern/cycles/kernel/kernel_light.h | 4 +- intern/cycles/kernel/kernel_textures.h | 3 + intern/cycles/kernel/osl/osl_services.cpp | 8 +- intern/cycles/kernel/shaders/CMakeLists.txt | 1 + intern/cycles/kernel/shaders/node_ies_light.osl | 42 +++++++++ intern/cycles/kernel/svm/svm.h | 4 + intern/cycles/kernel/svm/svm_ies.h | 110 ++++++++++++++++++++++++ intern/cycles/kernel/svm/svm_types.h | 1 + 9 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 intern/cycles/kernel/shaders/node_ies_light.osl create mode 100644 intern/cycles/kernel/svm/svm_ies.h (limited to 'intern/cycles/kernel') diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 9b7f4e00084..457602ae4e7 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -178,6 +178,7 @@ set(SRC_SVM_HEADERS svm/svm_geometry.h svm/svm_gradient.h svm/svm_hsv.h + svm/svm_ies.h svm/svm_image.h svm/svm_invert.h svm/svm_light_path.h diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h index efab69ee37d..ec7203d36eb 100644 --- a/intern/cycles/kernel/kernel_light.h +++ b/intern/cycles/kernel/kernel_light.h @@ -170,7 +170,7 @@ float3 background_map_sample(KernelGlobals *kg, float randu, float randv, float float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res); /* importance-sampled V direction */ - float dv = (randv - cdf_v.y) / (cdf_next_v.y - cdf_v.y); + float dv = inverse_lerp(cdf_v.y, cdf_next_v.y, randv); float v = (index_v + dv) / res; /* this is basically std::lower_bound as used by pbrt */ @@ -196,7 +196,7 @@ float3 background_map_sample(KernelGlobals *kg, float randu, float randv, float float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_count + res); /* importance-sampled U direction */ - float du = (randu - cdf_u.y) / (cdf_next_u.y - cdf_u.y); + float du = inverse_lerp(cdf_u.y, cdf_next_u.y, randu); float u = (index_u + du) / res; /* compute pdf */ diff --git a/intern/cycles/kernel/kernel_textures.h b/intern/cycles/kernel/kernel_textures.h index 9047b93a0b2..a7b8c492ee9 100644 --- a/intern/cycles/kernel/kernel_textures.h +++ b/intern/cycles/kernel/kernel_textures.h @@ -81,5 +81,8 @@ KERNEL_TEX(uint, __sobol_directions) /* image textures */ KERNEL_TEX(TextureInfo, __texture_info) +/* ies lights */ +KERNEL_TEX(float, __ies) + #undef KERNEL_TEX diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index 0c5e5e30e47..32d86b7192a 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -956,9 +956,15 @@ bool OSLRenderServices::texture(ustring filename, status = true; } } + else if(filename[1] == 'l') { + /* IES light. */ + int slot = atoi(filename.c_str() + 2); + result[0] = kernel_ies_interp(kg, slot, s, t); + status = true; + } else { /* Packed texture. */ - int slot = atoi(filename.c_str() + 1); + int slot = atoi(filename.c_str() + 2); float4 rgba = kernel_tex_image_interp(kg, slot, s, 1.0f - t); result[0] = rgba[0]; diff --git a/intern/cycles/kernel/shaders/CMakeLists.txt b/intern/cycles/kernel/shaders/CMakeLists.txt index 6ec651a96d8..b28d017c1c2 100644 --- a/intern/cycles/kernel/shaders/CMakeLists.txt +++ b/intern/cycles/kernel/shaders/CMakeLists.txt @@ -39,6 +39,7 @@ set(SRC_OSL node_principled_volume.osl node_holdout.osl node_hsv.osl + node_ies_light.osl node_image_texture.osl node_invert.osl node_layer_weight.osl diff --git a/intern/cycles/kernel/shaders/node_ies_light.osl b/intern/cycles/kernel/shaders/node_ies_light.osl new file mode 100644 index 00000000000..a0954e3a444 --- /dev/null +++ b/intern/cycles/kernel/shaders/node_ies_light.osl @@ -0,0 +1,42 @@ +/* + * Copyright 2011-2015 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stdosl.h" +#include "node_texture.h" + +/* IES Light */ + +shader node_ies_light( + int use_mapping = 0, + matrix mapping = matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + int slot = 0, + float Strength = 1.0, + point Vector = I, + output float Fac = 0.0) +{ + point p = Vector; + + if (use_mapping) { + p = transform(mapping, p); + } + + p = normalize(p); + + float v_angle = acos(-p[2]); + float h_angle = atan2(p[0], p[1]) + M_PI; + + Fac = Strength * texture(format("@l%d", slot), h_angle, v_angle); +} diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 39cd5da7b12..bfa146f2d93 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -157,6 +157,7 @@ CCL_NAMESPACE_END #include "kernel/svm/svm_camera.h" #include "kernel/svm/svm_geometry.h" #include "kernel/svm/svm_hsv.h" +#include "kernel/svm/svm_ies.h" #include "kernel/svm/svm_image.h" #include "kernel/svm/svm_gamma.h" #include "kernel/svm/svm_brightness.h" @@ -421,6 +422,9 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, ccl_a case NODE_LIGHT_FALLOFF: svm_node_light_falloff(sd, stack, node); break; + case NODE_IES: + svm_node_ies(kg, sd, stack, node, &offset); + break; # endif /* __EXTRA_NODES__ */ #endif /* NODES_GROUP(NODE_GROUP_LEVEL_2) */ diff --git a/intern/cycles/kernel/svm/svm_ies.h b/intern/cycles/kernel/svm/svm_ies.h new file mode 100644 index 00000000000..6130c3348b0 --- /dev/null +++ b/intern/cycles/kernel/svm/svm_ies.h @@ -0,0 +1,110 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +CCL_NAMESPACE_BEGIN + +/* IES Light */ + +ccl_device_inline float interpolate_ies_vertical(KernelGlobals *kg, int ofs, int v, int v_num, float v_frac, int h) +{ + /* Since lookups are performed in spherical coordinates, clamping the coordinates at the low end of v + * (corresponding to the north pole) would result in artifacts. + * The proper way of dealing with this would be to lookup the corresponding value on the other side of the pole, + * but since the horizontal coordinates might be nonuniform, this would require yet another interpolation. + * Therefore, the assumtion is made that the light is going to be symmetrical, which means that we can just take + * the corresponding value at the current horizontal coordinate. */ + +#define IES_LOOKUP(v) kernel_tex_fetch(__ies, ofs+h*v_num+(v)) + /* If v is zero, assume symmetry and read at v=1 instead of v=-1. */ + float a = IES_LOOKUP((v == 0)? 1 : v-1); + float b = IES_LOOKUP(v); + float c = IES_LOOKUP(v+1); + float d = IES_LOOKUP(min(v+2, v_num-1)); +#undef IES_LOOKUP + + return cubic_interp(a, b, c, d, v_frac); +} + +ccl_device_inline float kernel_ies_interp(KernelGlobals *kg, int slot, float h_angle, float v_angle) +{ + /* Find offset of the IES data in the table. */ + int ofs = __float_as_int(kernel_tex_fetch(__ies, slot)); + if(ofs == -1) { + return 100.0f; + } + + int h_num = __float_as_int(kernel_tex_fetch(__ies, ofs++)); + int v_num = __float_as_int(kernel_tex_fetch(__ies, ofs++)); + +#define IES_LOOKUP_ANGLE_H(h) kernel_tex_fetch(__ies, ofs+(h)) +#define IES_LOOKUP_ANGLE_V(v) kernel_tex_fetch(__ies, ofs+h_num+(v)) + + /* Check whether the angle is within the bounds of the IES texture. */ + if(v_angle >= IES_LOOKUP_ANGLE_V(v_num-1)) { + return 0.0f; + } + kernel_assert(v_angle >= IES_LOOKUP_ANGLE_V(0)); + kernel_assert(h_angle >= IES_LOOKUP_ANGLE_H(0)); + kernel_assert(h_angle <= IES_LOOKUP_ANGLE_H(h_num-1)); + + /* Lookup the angles to find the table position. */ + int h_i, v_i; + /* TODO(lukas): Consider using bisection. Probably not worth it for the vast majority of IES files. */ + for(h_i = 0; IES_LOOKUP_ANGLE_H(h_i+1) < h_angle; h_i++); + for(v_i = 0; IES_LOOKUP_ANGLE_V(v_i+1) < v_angle; v_i++); + + float h_frac = inverse_lerp(IES_LOOKUP_ANGLE_H(h_i), IES_LOOKUP_ANGLE_H(h_i+1), h_angle); + float v_frac = inverse_lerp(IES_LOOKUP_ANGLE_V(v_i), IES_LOOKUP_ANGLE_V(v_i+1), v_angle); + +#undef IES_LOOKUP_ANGLE_H +#undef IES_LOOKUP_ANGLE_V + + /* Skip forward to the actual intensity data. */ + ofs += h_num+v_num; + + /* Perform cubic interpolation along the horizontal coordinate to get the intensity value. + * If h_i is zero, just wrap around since the horizontal angles always go over the full circle. + * However, the last entry (360°) equals the first one, so we need to wrap around to the one before that. */ + float a = interpolate_ies_vertical(kg, ofs, v_i, v_num, v_frac, (h_i == 0)? h_num-2 : h_i-1); + float b = interpolate_ies_vertical(kg, ofs, v_i, v_num, v_frac, h_i); + float c = interpolate_ies_vertical(kg, ofs, v_i, v_num, v_frac, h_i+1); + /* Same logic here, wrap around to the second element if necessary. */ + float d = interpolate_ies_vertical(kg, ofs, v_i, v_num, v_frac, (h_i+2 == h_num)? 1 : h_i+2); + + /* Cubic interpolation can result in negative values, so get rid of them. */ + return max(cubic_interp(a, b, c, d, h_frac), 0.0f); +} + +ccl_device void svm_node_ies(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +{ + uint vector_offset, strength_offset, fac_offset, dummy, slot = node.z; + decode_node_uchar4(node.y, &strength_offset, &vector_offset, &fac_offset, &dummy); + + float3 vector = stack_load_float3(stack, vector_offset); + float strength = stack_load_float_default(stack, strength_offset, node.w); + + vector = normalize(vector); + float v_angle = safe_acosf(-vector.z); + float h_angle = atan2f(vector.x, vector.y) + M_PI_F; + + float fac = strength * kernel_ies_interp(kg, slot, h_angle, v_angle); + + if(stack_valid(fac_offset)) { + stack_store_float(stack, fac_offset, fac); + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index dc62e25b340..ac24d23ecd2 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -136,6 +136,7 @@ typedef enum ShaderNodeType { NODE_DISPLACEMENT, NODE_VECTOR_DISPLACEMENT, NODE_PRINCIPLED_VOLUME, + NODE_IES, } ShaderNodeType; typedef enum NodeAttributeType { -- cgit v1.2.3