diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 2695dafb55a..1b0a6e09ba9 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,7 +27,7 @@ /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 88 +#define BLENDER_FILE_SUBVERSION 89 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenloader/intern/versioning_500.cc b/source/blender/blenloader/intern/versioning_500.cc index a01503d7fb0..a3ac12872c0 100644 --- a/source/blender/blenloader/intern/versioning_500.cc +++ b/source/blender/blenloader/intern/versioning_500.cc @@ -2366,6 +2366,81 @@ static void do_version_displace_node_remove_xy_scale(bNodeTree &node_tree, bNode } } +/* The Size input is now in pixels, while previously, it was relative to 0.01 of the greater image + * dimension. */ +static void do_version_bokeh_blur_pixel_size(bNodeTree &node_tree, bNode &node) +{ + blender::bke::node_tree_set_type(node_tree); + + bNodeSocket *image_input = blender::bke::node_find_socket(node, SOCK_IN, "Image"); + bNodeSocket *size_input = blender::bke::node_find_socket(node, SOCK_IN, "Size"); + + /* Find the link going into the inputs of the node. */ + bNodeLink *image_link = nullptr; + bNodeLink *size_link = nullptr; + LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) { + if (link->tosock == size_input) { + size_link = link; + } + if (link->tosock == image_input) { + image_link = link; + } + } + + bNode &multiply_node = version_node_add_empty(node_tree, "ShaderNodeMath"); + multiply_node.parent = node.parent; + multiply_node.location[0] = node.location[0] - node.width - 20.0f; + multiply_node.location[1] = node.location[1]; + multiply_node.custom1 = NODE_MATH_MULTIPLY; + + bNodeSocket &multiply_a_input = version_node_add_socket( + node_tree, multiply_node, SOCK_IN, "NodeSocketFloat", "Value"); + bNodeSocket &multiply_b_input = version_node_add_socket( + node_tree, multiply_node, SOCK_IN, "NodeSocketFloat", "Value_001"); + bNodeSocket &multiply_output = version_node_add_socket( + node_tree, multiply_node, SOCK_OUT, "NodeSocketFloat", "Value"); + + multiply_a_input.default_value_typed()->value = + size_input->default_value_typed()->value; + if (size_link) { + version_node_add_link( + node_tree, *size_link->fromnode, *size_link->fromsock, multiply_node, multiply_a_input); + blender::bke::node_remove_link(&node_tree, *size_link); + } + + version_node_add_link(node_tree, multiply_node, multiply_output, node, *size_input); + + bNode *relative_to_pixel_node = blender::bke::node_add_node( + nullptr, node_tree, "CompositorNodeRelativeToPixel"); + relative_to_pixel_node->parent = node.parent; + relative_to_pixel_node->location[0] = multiply_node.location[0] - multiply_node.width - 20.0f; + relative_to_pixel_node->location[1] = multiply_node.location[1]; + relative_to_pixel_node->custom1 = CMP_NODE_RELATIVE_TO_PIXEL_DATA_TYPE_FLOAT; + relative_to_pixel_node->custom2 = CMP_NODE_RELATIVE_TO_PIXEL_REFERENCE_DIMENSION_GREATER; + + bNodeSocket *relative_to_pixel_image_input = blender::bke::node_find_socket( + *relative_to_pixel_node, SOCK_IN, "Image"); + bNodeSocket *relative_to_pixel_value_input = blender::bke::node_find_socket( + *relative_to_pixel_node, SOCK_IN, "Float Value"); + bNodeSocket *relative_to_pixel_value_output = blender::bke::node_find_socket( + *relative_to_pixel_node, SOCK_OUT, "Float Value"); + + version_node_add_link(node_tree, + *relative_to_pixel_node, + *relative_to_pixel_value_output, + multiply_node, + multiply_b_input); + + relative_to_pixel_value_input->default_value_typed()->value = 0.01f; + if (image_link) { + version_node_add_link(node_tree, + *image_link->fromnode, + *image_link->fromsock, + *relative_to_pixel_node, + *relative_to_pixel_image_input); + } +} + void do_versions_after_linking_500(FileData *fd, Main *bmain) { if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 9)) { @@ -3514,6 +3589,21 @@ void blo_do_versions_500(FileData *fd, Library * /*lib*/, Main *bmain) FOREACH_NODETREE_END; } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 89)) { + FOREACH_NODETREE_BEGIN (bmain, node_tree, id) { + if (node_tree->type != NTREE_COMPOSIT) { + continue; + } + version_node_input_socket_name(node_tree, CMP_NODE_BOKEHBLUR, "Bounding box", "Mask"); + LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) { + if (node->type_legacy == CMP_NODE_BOKEHBLUR) { + do_version_bokeh_blur_pixel_size(*node_tree, *node); + } + } + } + FOREACH_NODETREE_END; + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. diff --git a/source/blender/compositor/shaders/compositor_bokeh_blur_variable_size.glsl b/source/blender/compositor/shaders/compositor_bokeh_blur_variable_size.glsl index 0c4909a76dc..b7d61f763ed 100644 --- a/source/blender/compositor/shaders/compositor_bokeh_blur_variable_size.glsl +++ b/source/blender/compositor/shaders/compositor_bokeh_blur_variable_size.glsl @@ -47,7 +47,7 @@ void main() return; } - float center_size = max(0.0f, texture_load(size_tx, texel).x * base_size); + float center_size = max(0.0f, texture_load(size_tx, texel).x); /* Go over the window of the given search radius and accumulate the colors multiplied by their * respective weights as well as the weights themselves, but only if both the size of the center @@ -57,7 +57,7 @@ void main() float4 accumulated_weight = float4(0.0f); for (int y = -search_radius; y <= search_radius; y++) { for (int x = -search_radius; x <= search_radius; x++) { - float candidate_size = max(0.0f, texture_load(size_tx, texel + int2(x, y)).x * base_size); + float candidate_size = max(0.0f, texture_load(size_tx, texel + int2(x, y)).x); /* Skip accumulation if either the x or y distances of the candidate pixel are larger than * either the center or candidate pixel size. Note that the max and min functions here denote diff --git a/source/blender/compositor/shaders/infos/compositor_bokeh_blur_variable_size_info.hh b/source/blender/compositor/shaders/infos/compositor_bokeh_blur_variable_size_info.hh index 384582e89f5..4e36448a8a3 100644 --- a/source/blender/compositor/shaders/infos/compositor_bokeh_blur_variable_size_info.hh +++ b/source/blender/compositor/shaders/infos/compositor_bokeh_blur_variable_size_info.hh @@ -6,7 +6,6 @@ GPU_SHADER_CREATE_INFO(compositor_bokeh_blur_variable_size) LOCAL_GROUP_SIZE(16, 16) -PUSH_CONSTANT(float, base_size) PUSH_CONSTANT(int, search_radius) SAMPLER(0, sampler2D, input_tx) SAMPLER(1, sampler2D, weights_tx) diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc index 8b2ff9cac64..29338838628 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc @@ -2,15 +2,9 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -/** \file - * \ingroup cmpnodes - */ - #include "BLI_math_base.hh" #include "BLI_math_vector_types.hh" -#include "UI_resources.hh" - #include "COM_algorithm_pad.hh" #include "COM_algorithm_parallel_reduction.hh" #include "COM_node_operation.hh" @@ -18,8 +12,6 @@ #include "node_composite_util.hh" -/* **************** BLUR ******************** */ - namespace blender::nodes::node_composite_bokehblur_cc { static void cmp_node_bokehblur_declare(NodeDeclarationBuilder &b) @@ -31,13 +23,10 @@ static void cmp_node_bokehblur_declare(NodeDeclarationBuilder &b) .default_value({1.0f, 1.0f, 1.0f, 1.0f}) .compositor_realization_mode(CompositorInputRealizationMode::Transforms) .structure_type(StructureType::Dynamic); - b.add_input("Size").default_value(1.0f).min(0.0f).max(10.0f).structure_type( + b.add_input("Size").default_value(0.0f).min(0.0f).structure_type( + StructureType::Dynamic); + b.add_input("Mask").default_value(1.0f).min(0.0f).max(1.0f).structure_type( StructureType::Dynamic); - b.add_input("Bounding box") - .default_value(1.0f) - .min(0.0f) - .max(1.0f) - .structure_type(StructureType::Dynamic); b.add_input("Extend Bounds").default_value(false); b.add_output("Image").structure_type(StructureType::Dynamic); @@ -85,7 +74,7 @@ class BokehBlurOperation : public NodeOperation { /* For constant sized blur, the extension should just be the blur radius. */ if (size.is_single_value()) { - return int(math::ceil(this->compute_blur_radius())); + return this->get_blur_radius(); } /* For variable sized blur, the extension should be the bokeh search radius. */ @@ -95,10 +84,10 @@ class BokehBlurOperation : public NodeOperation { void execute_blur(const Result &input, const Result &size) { if (size.is_single_value()) { - execute_constant_size(input); + this->execute_constant_size(input); } else { - execute_variable_size(input, size); + this->execute_variable_size(input, size); } } @@ -117,18 +106,18 @@ class BokehBlurOperation : public NodeOperation { gpu::Shader *shader = context().get_shader("compositor_bokeh_blur"); GPU_shader_bind(shader); - GPU_shader_uniform_1i(shader, "radius", int(compute_blur_radius())); + GPU_shader_uniform_1i(shader, "radius", this->get_blur_radius()); input.bind_as_texture(shader, "input_tx"); - const Result &input_weights = get_input("Bokeh"); + const Result &input_weights = this->get_input("Bokeh"); input_weights.bind_as_texture(shader, "weights_tx"); - const Result &input_mask = get_input("Bounding box"); + const Result &input_mask = this->get_input("Mask"); input_mask.bind_as_texture(shader, "mask_tx"); const Domain domain = input.domain(); - Result &output_image = get_result("Image"); + Result &output_image = this->get_result("Image"); output_image.allocate_texture(domain); output_image.bind_as_image(shader, "output_img"); @@ -143,9 +132,9 @@ class BokehBlurOperation : public NodeOperation { void execute_constant_size_cpu(const Result &input) { - const int radius = int(this->compute_blur_radius()); + const int radius = this->get_blur_radius(); - const Result &mask_image = this->get_input("Bounding box"); + const Result &mask_image = this->get_input("Mask"); const Domain domain = input.domain(); Result &output = this->get_result("Image"); @@ -192,26 +181,25 @@ class BokehBlurOperation : public NodeOperation { void execute_variable_size_gpu(const Result &input, const Result &size) { - const int search_radius = compute_variable_size_search_radius(); + const int search_radius = this->compute_variable_size_search_radius(); - gpu::Shader *shader = context().get_shader("compositor_bokeh_blur_variable_size"); + gpu::Shader *shader = this->context().get_shader("compositor_bokeh_blur_variable_size"); GPU_shader_bind(shader); - GPU_shader_uniform_1f(shader, "base_size", compute_blur_radius()); GPU_shader_uniform_1i(shader, "search_radius", search_radius); input.bind_as_texture(shader, "input_tx"); - const Result &input_weights = get_input("Bokeh"); + const Result &input_weights = this->get_input("Bokeh"); input_weights.bind_as_texture(shader, "weights_tx"); size.bind_as_texture(shader, "size_tx"); - const Result &input_mask = get_input("Bounding box"); + const Result &input_mask = this->get_input("Mask"); input_mask.bind_as_texture(shader, "mask_tx"); const Domain domain = input.domain(); - Result &output_image = get_result("Image"); + Result &output_image = this->get_result("Image"); output_image.allocate_texture(domain); output_image.bind_as_image(shader, "output_img"); @@ -227,14 +215,13 @@ class BokehBlurOperation : public NodeOperation { void execute_variable_size_cpu(const Result &input, const Result &size_input) { - const float base_size = this->compute_blur_radius(); const int search_radius = this->compute_variable_size_search_radius(); - const Result &weights = get_input("Bokeh"); - const Result &mask_image = get_input("Bounding box"); + const Result &weights = this->get_input("Bokeh"); + const Result &mask_image = this->get_input("Mask"); const Domain domain = input.domain(); - Result &output = get_result("Image"); + Result &output = this->get_result("Image"); output.allocate_texture(domain); /* Given the texel in the range [-radius, radius] in both axis, load the appropriate weight @@ -275,7 +262,7 @@ class BokehBlurOperation : public NodeOperation { return; } - float center_size = math::max(0.0f, size_input.load_pixel(texel) * base_size); + float center_size = math::max(0.0f, size_input.load_pixel(texel)); /* Go over the window of the given search radius and accumulate the colors multiplied by * their respective weights as well as the weights themselves, but only if both the size of @@ -286,7 +273,7 @@ class BokehBlurOperation : public NodeOperation { for (int y = -search_radius; y <= search_radius; y++) { for (int x = -search_radius; x <= search_radius; x++) { float candidate_size = math::max( - 0.0f, size_input.load_pixel_extended(texel + int2(x, y)) * base_size); + 0.0f, size_input.load_pixel_extended(texel + int2(x, y))); /* Skip accumulation if either the x or y distances of the candidate pixel are larger * than either the center or candidate pixel size. Note that the max and min functions @@ -320,7 +307,7 @@ class BokehBlurOperation : public NodeOperation { { const Result &bokeh = this->get_input("Bokeh"); - Result kernel = context().create_result(ResultType::Color); + Result kernel = this->context().create_result(ResultType::Color); const int2 kernel_size = int2(radius * 2 + 1); kernel.allocate_texture(kernel_size); parallel_for(kernel_size, [&](const int2 texel) { @@ -338,41 +325,28 @@ class BokehBlurOperation : public NodeOperation { int compute_variable_size_search_radius() { - const Result &input_size = get_input("Size"); - const float maximum_size = maximum_float(context(), input_size); - - const float base_size = compute_blur_radius(); - return math::max(0, int(maximum_size * base_size)); + return math::max(0, int(maximum_float(context(), this->get_input("Size")))); } - float compute_blur_radius() + int get_blur_radius() { - const int2 image_size = get_input("Image").domain().size; - const int max_size = math::max(image_size.x, image_size.y); - - /* The [0, 10] range of the size is arbitrary and is merely in place to avoid very long - * computations of the bokeh blur. */ - const float size = math::clamp(get_input("Size").get_single_value_default(1.0f), 0.0f, 10.0f); - - /* The 100 divisor is arbitrary and was chosen using visual judgment. */ - return size * (max_size / 100.0f); + return math::max(0, int(this->get_input("Size").get_single_value())); } bool is_identity() { - const Result &input = get_input("Image"); + const Result &input = this->get_input("Image"); if (input.is_single_value()) { return true; } - if (compute_blur_radius() == 0.0f) { + const Result &size = this->get_input("Size"); + if (size.is_single_value() && this->get_blur_radius() == 0) { return true; } - /* This input is, in fact, a boolean mask. If it is zero, no blurring will take place. - * Otherwise, the blurring will take place ignoring the value of the input entirely. */ - const Result &bounding_box = get_input("Bounding box"); - if (bounding_box.is_single_value() && bounding_box.get_single_value() == 0.0) { + const Result &mask = this->get_input("Mask"); + if (mask.is_single_value() && mask.get_single_value() == 0.0) { return true; }