[Bf-blender-cvs] [50943f5dc7f] master: Realtime Compositor: Implement variable size bokeh blur

Omar Emara noreply at git.blender.org
Tue Oct 11 13:42:03 CEST 2022


Commit: 50943f5dc7f8eb753771dea76819c556ede32f2b
Author: Omar Emara
Date:   Tue Oct 11 13:40:48 2022 +0200
Branches: master
https://developer.blender.org/rB50943f5dc7f8eb753771dea76819c556ede32f2b

Realtime Compositor: Implement variable size bokeh blur

This patch implements the variable size blur option in the Bokeh Blur
node. The implementation is different from the CPU one in that it also
takes the Bounding Box input into account, which is ignored for some
reason for the CPU. Additionally, this implementation does not do the
optimization where the search radius is limited relative to the maximum
value in the size texture. That's because the cost of computing the
maximum is not worth it for most use cases.

The reference implementation does three unexpected things that are
replicated here nonetheless. First, the center bokeh weight is always
ignored and assumed to be 1. Second the size of the center pixel is
taken into account. Third, a unidimensional distance is used instead of
a 2D euclidean one. Those need to be considered independently.

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

Reviewed By: Clement Foucault

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

M	source/blender/gpu/CMakeLists.txt
A	source/blender/gpu/shaders/compositor/compositor_blur_variable_size.glsl
A	source/blender/gpu/shaders/compositor/infos/compositor_blur_variable_size_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 34d53eaa230..f387a4588b6 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -332,6 +332,7 @@ set(GLSL_SRC
   shaders/compositor/compositor_alpha_crop.glsl
   shaders/compositor/compositor_bilateral_blur.glsl
   shaders/compositor/compositor_blur.glsl
+  shaders/compositor/compositor_blur_variable_size.glsl
   shaders/compositor/compositor_bokeh_image.glsl
   shaders/compositor/compositor_box_mask.glsl
   shaders/compositor/compositor_convert.glsl
@@ -613,6 +614,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_blur_variable_size_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_variable_size.glsl b/source/blender/gpu/shaders/compositor/compositor_blur_variable_size.glsl
new file mode 100644
index 00000000000..e7e5aac12a5
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_blur_variable_size.glsl
@@ -0,0 +1,60 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+/* 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, float radius)
+{
+  /* The center zero texel is always assigned a unit weight regardless of the corresponding weight
+   * in the weights texture. That's to guarantee that at last the center pixel will be accumulated
+   * even if the weights texture is zero at its center. */
+  if (texel == ivec2(0)) {
+    return vec4(1.0);
+  }
+
+  /* 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;
+  }
+
+  float center_size = texture_load(size_tx, texel).x * base_size;
+
+  /* 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
+   * pixel and the size of the candidate pixel are less than both the x and y distances of the
+   * candidate pixel. */
+  vec4 accumulated_color = vec4(0.0);
+  vec4 accumulated_weight = vec4(0.0);
+  for (int y = -search_radius; y <= search_radius; y++) {
+    for (int x = -search_radius; x <= search_radius; x++) {
+      float candidate_size = texture_load(size_tx, texel + ivec2(x, y)).x * base_size;
+
+      /* 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
+       * "either" in the aforementioned description. */
+      float size = min(center_size, candidate_size);
+      if (max(abs(x), abs(y)) > size) {
+        continue;
+      }
+
+      vec4 weight = load_weight(ivec2(x, y), size);
+      accumulated_color += texture_load(input_tx, 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_variable_size_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_blur_variable_size_info.hh
new file mode 100644
index 00000000000..05b6385fd1e
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_blur_variable_size_info.hh
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_blur_variable_size)
+    .local_group_size(16, 16)
+    .push_constant(Type::FLOAT, "base_size")
+    .push_constant(Type::INT, "search_radius")
+    .sampler(0, ImageType::FLOAT_2D, "input_tx")
+    .sampler(1, ImageType::FLOAT_2D, "weights_tx")
+    .sampler(2, ImageType::FLOAT_2D, "size_tx")
+    .sampler(3, ImageType::FLOAT_2D, "mask_tx")
+    .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+    .compute_source("compositor_blur_variable_size.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 731c559dfdc..a581d87a463 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
@@ -70,10 +70,20 @@ class BokehBlurOperation : public NodeOperation {
       return;
     }
 
+    if (get_input("Size").is_single_value() || !get_variable_size()) {
+      execute_constant_size();
+    }
+    else {
+      execute_variable_size();
+    }
+  }
+
+  void execute_constant_size()
+  {
     GPUShader *shader = shader_manager().get("compositor_blur");
     GPU_shader_bind(shader);
 
-    GPU_shader_uniform_1i(shader, "radius", compute_blur_radius());
+    GPU_shader_uniform_1i(shader, "radius", int(compute_blur_radius()));
     GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds());
 
     const Result &input_image = get_input("Image");
@@ -88,7 +98,7 @@ class BokehBlurOperation : public NodeOperation {
     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);
+      domain.size += int2(int(compute_blur_radius()) * 2);
     }
 
     Result &output_image = get_result("Image");
@@ -104,7 +114,42 @@ class BokehBlurOperation : public NodeOperation {
     input_mask.unbind_as_texture();
   }
 
-  int compute_blur_radius()
+  void execute_variable_size()
+  {
+    GPUShader *shader = shader_manager().get("compositor_blur_variable_size");
+    GPU_shader_bind(shader);
+
+    GPU_shader_uniform_1f(shader, "base_size", compute_blur_radius());
+    GPU_shader_uniform_1i(shader, "search_radius", get_max_size());
+
+    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_size = get_input("Size");
+    input_size.bind_as_texture(shader, "size_tx");
+
+    const Result &input_mask = get_input("Bounding box");
+    input_mask.bind_as_texture(shader, "mask_tx");
+
+    const Domain domain = compute_domain();
+    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_size.unbind_as_texture();
+    input_mask.unbind_as_texture();
+  }
+
+  float compute_blur_radius()
   {
     const int2 image_size = get_input("Image").domain().size;
     const int max_size = math::max(image_size.x, image_size.y);
@@ -124,7 +169,7 @@ class BokehBlurOperation : public NodeOperation {
       return true;
     }
 
-    if (compute_blur_radius() == 0) {
+    if (compute_blur_radius() == 0.0f) {
       return true;
     }
 
@@ -142,6 +187,16 @@ class BokehBlurOperation : public NodeOperation {
   {
     return bnode().custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS;
   }
+
+  bool get_variable_size()
+  {
+    return bnode().custom1 & CMP_NODEFLAG_BLUR_VARIABLE_SIZE;
+  }
+
+  int get_max_size()
+  {
+    return static_cast<int>(bnode().custom4);
+  }
 };
 
 static NodeOperation *get_compositor_operation(Context &context, DNode node)



More information about the Bf-blender-cvs mailing list