diff options
author | Omar Emara <mail@OmarEmara.dev> | 2022-08-18 13:00:14 +0300 |
---|---|---|
committer | Omar Emara <mail@OmarEmara.dev> | 2022-08-18 13:00:14 +0300 |
commit | b828d453e93c30e81d3cb0d5aa3edc553ea9a333 (patch) | |
tree | fd0604a2b2563d8a8f0f88ab6a131e2f92734eb3 /source | |
parent | 6cb0a122dfcd7d3e75e8bc0d12e09b75b97cee99 (diff) |
Realtime Compositor: Implement filter node
This patch implements the filter node for the realtime compositor.
Differential Revision: https://developer.blender.org/D15661
Reviewed By: Clement Foucault
Diffstat (limited to 'source')
6 files changed, 178 insertions, 1 deletions
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index bbc27a26835..33a0ccec24b 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -318,7 +318,9 @@ set(GLSL_SRC shaders/compositor/compositor_bokeh_image.glsl shaders/compositor/compositor_box_mask.glsl shaders/compositor/compositor_convert.glsl + shaders/compositor/compositor_edge_filter.glsl shaders/compositor/compositor_ellipse_mask.glsl + shaders/compositor/compositor_filter.glsl shaders/compositor/compositor_flip.glsl shaders/compositor/compositor_image_crop.glsl shaders/compositor/compositor_projector_lens_distortion.glsl @@ -565,7 +567,9 @@ 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_edge_filter_info.hh shaders/compositor/infos/compositor_ellipse_mask_info.hh + shaders/compositor/infos/compositor_filter_info.hh shaders/compositor/infos/compositor_flip_info.hh shaders/compositor/infos/compositor_image_crop_info.hh shaders/compositor/infos/compositor_projector_lens_distortion_info.hh diff --git a/source/blender/gpu/shaders/compositor/compositor_edge_filter.glsl b/source/blender/gpu/shaders/compositor/compositor_edge_filter.glsl new file mode 100644 index 00000000000..67e27c22602 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_edge_filter.glsl @@ -0,0 +1,31 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + /* Compute the dot product between the 3x3 window around the pixel and the edge detection kernel + * in the X direction and Y direction. The Y direction kernel is computed by transposing the + * given X direction kernel. */ + vec3 color_x = vec3(0); + vec3 color_y = vec3(0); + for (int j = 0; j < 3; j++) { + for (int i = 0; i < 3; i++) { + vec3 color = texture_load(input_tx, texel + ivec2(i - 1, j - 1)).rgb; + color_x += color * kernel[j][i]; + color_y += color * kernel[i][j]; + } + } + + /* Compute the channel-wise magnitude of the 2D vector composed from the X and Y edge detection + * filter results. */ + vec3 magnitude = sqrt(color_x * color_x + color_y * color_y); + + /* Mix the channel-wise magnitude with the original color at the center of the kernel using the + * input factor. */ + vec4 color = texture_load(input_tx, texel); + magnitude = mix(color.rgb, magnitude, texture_load(factor_tx, texel).x); + + /* Store the channel-wise magnitude with the original alpha of the input. */ + imageStore(output_img, texel, vec4(magnitude, color.a)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_filter.glsl b/source/blender/gpu/shaders/compositor/compositor_filter.glsl new file mode 100644 index 00000000000..e501c563dda --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_filter.glsl @@ -0,0 +1,20 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + /* Compute the dot product between the 3x3 window around the pixel and the filter kernel. */ + vec4 color = vec4(0); + for (int j = 0; j < 3; j++) { + for (int i = 0; i < 3; i++) { + color += texture_load(input_tx, texel + ivec2(i - 1, j - 1)) * kernel[j][i]; + } + } + + /* Mix with the original color at the center of the kernel using the input factor. */ + color = mix(texture_load(input_tx, texel), color, texture_load(factor_tx, texel).x); + + /* Store the color making sure it is not negative. */ + imageStore(output_img, texel, max(color, 0.0)); +} diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_edge_filter_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_edge_filter_info.hh new file mode 100644 index 00000000000..916ec62bdba --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_edge_filter_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_edge_filter) + .local_group_size(16, 16) + .push_constant(Type::MAT4, "kernel") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .sampler(1, ImageType::FLOAT_2D, "factor_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_edge_filter.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_filter_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_filter_info.hh new file mode 100644 index 00000000000..9d565cf4b8a --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_filter_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_filter) + .local_group_size(16, 16) + .push_constant(Type::MAT4, "kernel") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .sampler(1, ImageType::FLOAT_2D, "factor_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_filter.glsl") + .do_static_compilation(true); diff --git a/source/blender/nodes/composite/nodes/node_composite_filter.cc b/source/blender/nodes/composite/nodes/node_composite_filter.cc index 854cf684806..c071becac54 100644 --- a/source/blender/nodes/composite/nodes/node_composite_filter.cc +++ b/source/blender/nodes/composite/nodes/node_composite_filter.cc @@ -5,6 +5,8 @@ * \ingroup cmpnodes */ +#include "BLI_float3x3.hh" + #include "UI_interface.h" #include "UI_resources.h" @@ -36,7 +38,103 @@ class FilterOperation : public NodeOperation { void execute() override { - get_input("Image").pass_through(get_result("Image")); + GPUShader *shader = shader_manager().get(get_shader_name()); + GPU_shader_bind(shader); + + GPU_shader_uniform_mat3_as_mat4(shader, "kernel", get_filter_kernel().ptr()); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + const Result &factor = get_input("Fac"); + factor.bind_as_texture(shader, "factor_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); + + input_image.unbind_as_texture(); + factor.unbind_as_texture(); + output_image.unbind_as_image(); + GPU_shader_unbind(); + } + + int get_filter_method() + { + return bnode().custom1; + } + + float3x3 get_filter_kernel() + { + /* Initialize the kernels as arrays of rows with the top row first. Edge detection kernels + * return the kernel in the X direction, while the kernel in the Y direction will be computed + * inside the shader by transposing the kernel in the X direction. */ + switch (get_filter_method()) { + case CMP_FILT_SOFT: { + const float kernel[3][3] = {{1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f}, + {2.0f / 16.0f, 4.0f / 16.0f, 2.0f / 16.0f}, + {1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f}}; + return float3x3(kernel); + } + case CMP_FILT_SHARP_BOX: { + const float kernel[3][3] = { + {-1.0f, -1.0f, -1.0f}, {-1.0f, 9.0f, -1.0f}, {-1.0f, -1.0f, -1.0f}}; + return float3x3(kernel); + } + case CMP_FILT_LAPLACE: { + const float kernel[3][3] = {{-1.0f / 8.0f, -1.0f / 8.0f, -1.0f / 8.0f}, + {-1.0f / 8.0f, 1.0f, -1.0f / 8.0f}, + {-1.0f / 8.0f, -1.0f / 8.0f, -1.0f / 8.0f}}; + return float3x3(kernel); + } + case CMP_FILT_SOBEL: { + const float kernel[3][3] = {{1.0f, 0.0f, -1.0f}, {2.0f, 0.0f, -2.0f}, {1.0f, 0.0f, -1.0f}}; + return float3x3(kernel); + } + case CMP_FILT_PREWITT: { + const float kernel[3][3] = {{1.0f, 0.0f, -1.0f}, {1.0f, 0.0f, -1.0f}, {1.0f, 0.0f, -1.0f}}; + return float3x3(kernel); + } + case CMP_FILT_KIRSCH: { + const float kernel[3][3] = { + {5.0f, -3.0f, -2.0f}, {5.0f, -3.0f, -2.0f}, {5.0f, -3.0f, -2.0f}}; + return float3x3(kernel); + } + case CMP_FILT_SHADOW: { + const float kernel[3][3] = {{1.0f, 2.0f, 1.0f}, {0.0f, 1.0f, 0.0f}, {-1.0f, -2.0f, -1.0f}}; + return float3x3(kernel); + } + case CMP_FILT_SHARP_DIAMOND: { + const float kernel[3][3] = { + {0.0f, -1.0f, 0.0f}, {-1.0f, 5.0f, -1.0f}, {0.0f, -1.0f, 0.0f}}; + return float3x3(kernel); + } + default: { + const float kernel[3][3] = {{0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0f}}; + return float3x3(kernel); + } + } + } + + const char *get_shader_name() + { + switch (get_filter_method()) { + case CMP_FILT_LAPLACE: + case CMP_FILT_SOBEL: + case CMP_FILT_PREWITT: + case CMP_FILT_KIRSCH: + return "compositor_edge_filter"; + case CMP_FILT_SOFT: + case CMP_FILT_SHARP_BOX: + case CMP_FILT_SHADOW: + case CMP_FILT_SHARP_DIAMOND: + default: + return "compositor_filter"; + } } }; |