[Bf-blender-cvs] [1854d313218] master: Realtime Compositor: Implement directional blur node

Omar Emara noreply at git.blender.org
Thu Aug 18 12:21:19 CEST 2022


Commit: 1854d313218b9cb1be1a67e256783cda2703db8f
Author: Omar Emara
Date:   Thu Aug 18 12:20:18 2022 +0200
Branches: master
https://developer.blender.org/rB1854d313218b9cb1be1a67e256783cda2703db8f

Realtime Compositor: Implement directional blur node

This patch implements the directional blur node for the realtime compositor.

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

Reviewed By: Clement Foucault

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

M	source/blender/gpu/CMakeLists.txt
A	source/blender/gpu/shaders/compositor/compositor_directional_blur.glsl
A	source/blender/gpu/shaders/compositor/infos/compositor_directional_blur_info.hh
M	source/blender/nodes/composite/nodes/node_composite_directionalblur.cc

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

diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 33a0ccec24b..fd732a09e08 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -318,6 +318,7 @@ set(GLSL_SRC
   shaders/compositor/compositor_bokeh_image.glsl
   shaders/compositor/compositor_box_mask.glsl
   shaders/compositor/compositor_convert.glsl
+  shaders/compositor/compositor_directional_blur.glsl
   shaders/compositor/compositor_edge_filter.glsl
   shaders/compositor/compositor_ellipse_mask.glsl
   shaders/compositor/compositor_filter.glsl
@@ -567,6 +568,7 @@ set(SRC_SHADER_CREATE_INFOS
   shaders/compositor/infos/compositor_bokeh_image_info.hh
   shaders/compositor/infos/compositor_box_mask_info.hh
   shaders/compositor/infos/compositor_convert_info.hh
+  shaders/compositor/infos/compositor_directional_blur_info.hh
   shaders/compositor/infos/compositor_edge_filter_info.hh
   shaders/compositor/infos/compositor_ellipse_mask_info.hh
   shaders/compositor/infos/compositor_filter_info.hh
diff --git a/source/blender/gpu/shaders/compositor/compositor_directional_blur.glsl b/source/blender/gpu/shaders/compositor/compositor_directional_blur.glsl
new file mode 100644
index 00000000000..1805cb5a7f5
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_directional_blur.glsl
@@ -0,0 +1,21 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+  ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+  ivec2 input_size = texture_size(input_tx);
+
+  /* Add 0.5 to evaluate the input sampler at the center of the pixel. */
+  vec2 coordinates = vec2(texel) + vec2(0.5);
+
+  /* For each iteration, accumulate the input at the normalize coordinates, hence the divide by
+   * input size, then transform the coordinates for the next iteration. */
+  vec4 accumulated_color = vec4(0.0);
+  for (int i = 0; i < iterations; i++) {
+    accumulated_color += texture(input_tx, coordinates / input_size);
+    coordinates = (mat3(inverse_transformation) * vec3(coordinates, 1.0)).xy;
+  }
+
+  /* Write the accumulated color divided by the number of iterations. */
+  imageStore(output_img, texel, accumulated_color / iterations);
+}
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_directional_blur_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_directional_blur_info.hh
new file mode 100644
index 00000000000..bb9199dcd26
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_directional_blur_info.hh
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_directional_blur)
+    .local_group_size(16, 16)
+    .push_constant(Type::INT, "iterations")
+    .push_constant(Type::MAT4, "inverse_transformation")
+    .sampler(0, ImageType::FLOAT_2D, "input_tx")
+    .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+    .compute_source("compositor_directional_blur.glsl")
+    .do_static_compilation(true);
diff --git a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
index eacba5ad12d..d317c442ab3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
@@ -5,10 +5,18 @@
  * \ingroup cmpnodes
  */
 
+#include "BLI_float3x3.hh"
+#include "BLI_math_base.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_math_vector.hh"
+
 #include "UI_interface.h"
 #include "UI_resources.h"
 
+#include "GPU_shader.h"
+
 #include "COM_node_operation.hh"
+#include "COM_utilities.hh"
 
 #include "node_composite_util.hh"
 
@@ -61,7 +69,119 @@ class DirectionalBlurOperation : 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_directional_blur");
+    GPU_shader_bind(shader);
+
+    /* The number of iterations does not cover the original image, that is, the image with no
+     * transformation. So add an extra iteration for the original image and put that into
+     * consideration in the shader. */
+    GPU_shader_uniform_1i(shader, "iterations", get_iterations() + 1);
+    GPU_shader_uniform_mat3_as_mat4(shader, "inverse_transformation", get_transformation().ptr());
+
+    const Result &input_image = get_input("Image");
+    input_image.bind_as_texture(shader, "input_tx");
+
+    GPU_texture_filter_mode(input_image.texture(), true);
+    GPU_texture_wrap_mode(input_image.texture(), false, false);
+
+    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();
+  }
+
+  /* Get the amount of translation that will be applied on each iteration. The translation is in
+   * the negative x direction rotated in the clock-wise direction, hence the negative sign for the
+   * rotation and translation vector. */
+  float2 get_translation()
+  {
+    const float diagonal_length = math::length(float2(get_input("Image").domain().size));
+    const float translation_amount = diagonal_length * get_node_directional_blur_data().distance;
+    const float3x3 rotation = float3x3::from_rotation(-get_node_directional_blur_data().angle);
+    return rotation * float2(-translation_amount / get_iterations(), 0.0f);
+  }
+
+  /* Get the amount of rotation that will be applied on each iteration. */
+  float get_rotation()
+  {
+    return get_node_directional_blur_data().spin / get_iterations();
+  }
+
+  /* Get the amount of scale that will be applied on each iteration. The scale is identity when the
+   * user supplies 0, so we add 1. */
+  float2 get_scale()
+  {
+    return float2(1.0f + get_node_directional_blur_data().zoom / get_iterations());
+  }
+
+  float2 get_origin()
+  {
+    const float2 center = float2(get_node_directional_blur_data().center_x,
+                                 get_node_directional_blur_data().center_y);
+    return float2(get_input("Image").domain().size) * center;
+  }
+
+  float3x3 get_transformation()
+  {
+    /* Construct the transformation that will be applied on each iteration. */
+    const float3x3 transformation = float3x3::from_translation_rotation_scale(
+        get_translation(), get_rotation(), get_scale());
+    /* Change the origin of the transformation to the user-specified origin. */
+    const float3x3 origin_transformation = float3x3::from_origin_transformation(transformation,
+                                                                                get_origin());
+    /* The shader will transform the coordinates, not the image itself, so take the inverse. */
+    return origin_transformation.inverted();
+  }
+
+  /* The actual number of iterations is 2 to the power of the user supplied iterations. The power
+   * is implemented using a bit shift. But also make sure it doesn't exceed the upper limit which
+   * is the number of diagonal pixels. */
+  int get_iterations()
+  {
+    const int iterations = 2 << (get_node_directional_blur_data().iter - 1);
+    const int upper_limit = math::ceil(math::length(float2(get_input("Image").domain().size)));
+    return math::min(iterations, upper_limit);
+  }
+
+  /* Returns true if the operation does nothing and the input can be passed through. */
+  bool is_identity()
+  {
+    const Result &input = get_input("Image");
+    /* Single value inputs can't be blurred and are returned as is. */
+    if (input.is_single_value()) {
+      return true;
+    }
+
+    /* If any of the following options are non-zero, then the operation is not an identity. */
+    if (get_node_directional_blur_data().distance != 0.0f) {
+      return false;
+    }
+
+    if (get_node_directional_blur_data().spin != 0.0f) {
+      return false;
+    }
+
+    if (get_node_directional_blur_data().zoom != 0.0f) {
+      return false;
+    }
+
+    return true;
+  }
+
+  NodeDBlurData &get_node_directional_blur_data()
+  {
+    return *static_cast<NodeDBlurData *>(bnode().storage);
   }
 };



More information about the Bf-blender-cvs mailing list