Commit: f4e5a8654487ef8b66cf5fb2ca3f4d66cb207618
Author: Omar Emara
Date:   Fri Sep 9 14:32:58 2022 +0200
Branches: master
https://developer.blender.org/rBf4e5a8654487ef8b66cf5fb2ca3f4d66cb207618

Realtime Compositor: Implement bokeh blur node

This patch implements the bokeh blur node for the realtime compositor.
The patch is still missing the Variable Size option because it depends
on the Levels node, which is yet to be implemented. In particular, it
requires the computation of global texture properties like the maximum
color.

Differential Revision: https://developer.blender.org/D15768

Reviewed By: Clement Foucault

===================================================================

M       source/blender/gpu/CMakeLists.txt
A       source/blender/gpu/shaders/compositor/compositor_blur.glsl
A       source/blender/gpu/shaders/compositor/infos/compositor_blur_info.hh
M       source/blender/nodes/composite/nodes/node_composite_bokehblur.cc

===================================================================

diff --git a/source/blender/gpu/CMakeLists.txt 
b/source/blender/gpu/CMakeLists.txt
index cb5bb4331f9..8b38c22ae28 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -326,6 +326,7 @@ set(GLSL_SRC
 
   shaders/compositor/compositor_alpha_crop.glsl
   shaders/compositor/compositor_bilateral_blur.glsl
+  shaders/compositor/compositor_blur.glsl
   shaders/compositor/compositor_bokeh_image.glsl
   shaders/compositor/compositor_box_mask.glsl
   shaders/compositor/compositor_convert.glsl
@@ -604,6 +605,7 @@ set(SRC_SHADER_CREATE_INFOS
 
   shaders/compositor/infos/compositor_alpha_crop_info.hh
   shaders/compositor/infos/compositor_bilateral_blur_info.hh
+  shaders/compositor/infos/compositor_blur_info.hh
   shaders/compositor/infos/compositor_bokeh_image_info.hh
   shaders/compositor/infos/compositor_box_mask_info.hh
   shaders/compositor/infos/compositor_convert_info.hh
diff --git a/source/blender/gpu/shaders/compositor/compositor_blur.glsl 
b/source/blender/gpu/shaders/compositor/compositor_blur.glsl
new file mode 100644
index 00000000000..4f981c84f59
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_blur.glsl
@@ -0,0 +1,55 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+vec4 load_input(ivec2 texel)
+{
+  vec4 color;
+  if (extend_bounds) {
+    /* If bounds are extended, then we treat the input as padded by a radius 
amount of pixels. So
+     * we load the input with an offset by the radius amount and fallback to a 
transparent color if
+     * it is out of bounds. */
+    color = texture_load(input_tx, texel - radius, vec4(0.0));
+  }
+  else {
+    color = texture_load(input_tx, texel);
+  }
+
+  return color;
+}
+
+/* Given the texel in the range [-radius, radius] in both axis, load the 
appropriate weight from
+ * the weights texture, where the texel (0, 0) is considered the center of 
weights texture. */
+vec4 load_weight(ivec2 texel)
+{
+  /* Add the radius to transform the texel into the range [0, radius * 2], 
then divide by the upper
+   * bound plus one to transform the texel into the normalized range [0, 1] 
needed to sample the
+   * weights sampler. Finally, also add 0.5 to sample at the center of the 
pixels. */
+  return texture(weights_tx, (texel + vec2(radius + 0.5)) / (radius * 2 + 1));
+}
+
+void main()
+{
+  ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+  /* The mask input is treated as a boolean. If it is zero, then no blurring 
happens for this
+   * pixel. Otherwise, the pixel is blurred normally and the mask value is 
irrelevant. */
+  float mask = texture_load(mask_tx, texel).x;
+  if (mask == 0.0) {
+    imageStore(output_img, texel, texture_load(input_tx, texel));
+    return;
+  }
+
+  /* Go over the window of the given radius and accumulate the colors 
multiplied by their
+   * respective weights as well as the weights themselves. */
+  vec4 accumulated_color = vec4(0.0);
+  vec4 accumulated_weight = vec4(0.0);
+  for (int y = -radius; y <= radius; y++) {
+    for (int x = -radius; x <= radius; x++) {
+      vec4 weight = load_weight(ivec2(x, y));
+      accumulated_color += load_input(texel + ivec2(x, y)) * weight;
+      accumulated_weight += weight;
+    }
+  }
+
+  imageStore(output_img, texel, safe_divide(accumulated_color, 
accumulated_weight));
+}
diff --git 
a/source/blender/gpu/shaders/compositor/infos/compositor_blur_info.hh 
b/source/blender/gpu/shaders/compositor/infos/compositor_blur_info.hh
new file mode 100644
index 00000000000..36b772aa486
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_blur_info.hh
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_blur)
+    .local_group_size(16, 16)
+    .push_constant(Type::INT, "radius")
+    .push_constant(Type::BOOL, "extend_bounds")
+    .sampler(0, ImageType::FLOAT_2D, "input_tx")
+    .sampler(1, ImageType::FLOAT_2D, "weights_tx")
+    .sampler(2, ImageType::FLOAT_2D, "mask_tx")
+    .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+    .compute_source("compositor_blur.glsl")
+    .do_static_compilation(true);
diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc 
b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
index 538f00af12d..182169405de 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
@@ -5,10 +5,16 @@
  * \ingroup cmpnodes
  */
 
+#include "BLI_math_base.hh"
+#include "BLI_math_vec_types.hh"
+
 #include "UI_interface.h"
 #include "UI_resources.h"
 
+#include "GPU_texture.h"
+
 #include "COM_node_operation.hh"
+#include "COM_utilities.hh"
 
 #include "node_composite_util.hh"
 
@@ -18,10 +24,22 @@ namespace blender::nodes::node_composite_bokehblur_cc {
 
 static void cmp_node_bokehblur_declare(NodeDeclarationBuilder &b)
 {
-  b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 
1.0f});
-  b.add_input<decl::Color>(N_("Bokeh")).default_value({1.0f, 1.0f, 1.0f, 
1.0f});
-  
b.add_input<decl::Float>(N_("Size")).default_value(1.0f).min(0.0f).max(10.0f);
-  b.add_input<decl::Float>(N_("Bounding 
box")).default_value(1.0f).min(0.0f).max(1.0f);
+  b.add_input<decl::Color>(N_("Image"))
+      .default_value({0.8f, 0.8f, 0.8f, 1.0f})
+      .compositor_domain_priority(0);
+  b.add_input<decl::Color>(N_("Bokeh"))
+      .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+      .compositor_skip_realization();
+  b.add_input<decl::Float>(N_("Size"))
+      .default_value(1.0f)
+      .min(0.0f)
+      .max(10.0f)
+      .compositor_domain_priority(1);
+  b.add_input<decl::Float>(N_("Bounding box"))
+      .default_value(1.0f)
+      .min(0.0f)
+      .max(1.0f)
+      .compositor_domain_priority(2);
   b.add_output<decl::Color>(N_("Image"));
 }
 
@@ -47,7 +65,82 @@ class BokehBlurOperation : public NodeOperation {
 
   void execute() override
   {
-    get_input("Image").pass_through(get_result("Image"));
+    if (is_identity()) {
+      get_input("Image").pass_through(get_result("Image"));
+      return;
+    }
+
+    GPUShader *shader = shader_manager().get("compositor_blur");
+    GPU_shader_bind(shader);
+
+    GPU_shader_uniform_1i(shader, "radius", compute_blur_radius());
+    GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds());
+
+    const Result &input_image = get_input("Image");
+    input_image.bind_as_texture(shader, "input_tx");
+
+    const Result &input_weights = get_input("Bokeh");
+    input_weights.bind_as_texture(shader, "weights_tx");
+
+    const Result &input_mask = get_input("Bounding box");
+    input_mask.bind_as_texture(shader, "mask_tx");
+
+    Domain domain = compute_domain();
+    if (get_extend_bounds()) {
+      /* Add a radius amount of pixels in both sides of the image, hence the 
multiply by 2. */
+      domain.size += int2(compute_blur_radius() * 2);
+    }
+
+    Result &output_image = get_result("Image");
+    output_image.allocate_texture(domain);
+    output_image.bind_as_image(shader, "output_img");
+
+    compute_dispatch_threads_at_least(shader, domain.size);
+
+    GPU_shader_unbind();
+    output_image.unbind_as_image();
+    input_image.unbind_as_texture();
+    input_weights.unbind_as_texture();
+    input_mask.unbind_as_texture();
+  }
+
+  int compute_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_float_value_default(1.0f), 0.0f, 10.0f);
+
+    /* The 100 divisor is arbitrary and was chosen using visual judgement. */
+    return size * (max_size / 100.0f);
+  }
+
+  bool is_identity()
+  {
+    const Result &input = get_input("Image");
+    if (input.is_single_value()) {
+      return true;
+    }
+
+    if (compute_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_float_value() == 
0.0) {
+      return true;
+    }
+
+    return false;
+  }
+
+  bool get_extend_bounds()
+  {
+    return bnode().custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS;
   }
 };

_______________________________________________
Bf-blender-cvs mailing list
Bf-blender-cvs@blender.org
List details, subscription details or unsubscribe:
https://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to