From fa2dbceadde8c34f731063819234e9db2c0904ef Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Thu, 27 Apr 2023 07:18:19 +0200 Subject: [PATCH] Realtime Compositor: Implement Corner Pin node This patch implements the Corner Pin node for the realtime compositor. This is different from the existing compositor in that single value inputs produce single value outputs, instead of assuming the size of the render. Pull Request: https://projects.blender.org/blender/blender/pulls/107363 --- .../realtime_compositor/CMakeLists.txt | 2 + .../shaders/compositor_plane_deform.glsl | 24 +++++ .../infos/compositor_plane_deform_info.hh | 12 +++ .../nodes/node_composite_cornerpin.cc | 95 +++++++++++++++++-- 4 files changed, 123 insertions(+), 10 deletions(-) create mode 100644 source/blender/compositor/realtime_compositor/shaders/compositor_plane_deform.glsl create mode 100644 source/blender/compositor/realtime_compositor/shaders/infos/compositor_plane_deform_info.hh diff --git a/source/blender/compositor/realtime_compositor/CMakeLists.txt b/source/blender/compositor/realtime_compositor/CMakeLists.txt index 914d6c3e220..f3a94610d1e 100644 --- a/source/blender/compositor/realtime_compositor/CMakeLists.txt +++ b/source/blender/compositor/realtime_compositor/CMakeLists.txt @@ -129,6 +129,7 @@ set(GLSL_SRC shaders/compositor_morphological_step.glsl shaders/compositor_normalize.glsl shaders/compositor_parallel_reduction.glsl + shaders/compositor_plane_deform.glsl shaders/compositor_projector_lens_distortion.glsl shaders/compositor_read_pass.glsl shaders/compositor_realize_on_domain.glsl @@ -227,6 +228,7 @@ set(SRC_SHADER_CREATE_INFOS shaders/infos/compositor_morphological_step_info.hh shaders/infos/compositor_normalize_info.hh shaders/infos/compositor_parallel_reduction_info.hh + shaders/infos/compositor_plane_deform_info.hh shaders/infos/compositor_projector_lens_distortion_info.hh shaders/infos/compositor_read_pass_info.hh shaders/infos/compositor_realize_on_domain_info.hh diff --git a/source/blender/compositor/realtime_compositor/shaders/compositor_plane_deform.glsl b/source/blender/compositor/realtime_compositor/shaders/compositor_plane_deform.glsl new file mode 100644 index 00000000000..17d258048d9 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/shaders/compositor_plane_deform.glsl @@ -0,0 +1,24 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + /* Add 0.5 to evaluate the input sampler at the center of the pixel and divide by the image size + * to get the coordinates into the sampler's expected [0, 1] range. */ + vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(texture_size(input_tx)); + + vec3 transformed_coordinates = mat3(homography_matrix) * vec3(coordinates, 1.0); + vec2 projected_coordinates = transformed_coordinates.xy / transformed_coordinates.z; + + /* The derivatives of the projected coordinates with respect to x and y are the first and + * second columns respectively, divided by the z projection factor as can be shown by + * differentiating the above matrix multiplication with respect to x and y. */ + vec2 x_gradient = homography_matrix[0].xy / transformed_coordinates.z; + vec2 y_gradient = homography_matrix[1].xy / transformed_coordinates.z; + + vec4 sampled_color = textureGrad(input_tx, projected_coordinates, x_gradient, y_gradient); + + imageStore(output_img, texel, sampled_color); + imageStore(mask_img, texel, sampled_color.aaaa); +} diff --git a/source/blender/compositor/realtime_compositor/shaders/infos/compositor_plane_deform_info.hh b/source/blender/compositor/realtime_compositor/shaders/infos/compositor_plane_deform_info.hh new file mode 100644 index 00000000000..d19512b2cde --- /dev/null +++ b/source/blender/compositor/realtime_compositor/shaders/infos/compositor_plane_deform_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_plane_deform) + .local_group_size(16, 16) + .push_constant(Type::MAT4, "homography_matrix") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "mask_img") + .compute_source("compositor_plane_deform.glsl") + .do_static_compilation(true); diff --git a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc index 25cfa166cef..e4416fab5f2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc @@ -5,9 +5,19 @@ * \ingroup cmpnodes */ +#include "BLI_math_geom.h" +#include "BLI_math_matrix_types.hh" +#include "BLI_math_vector_types.hh" + #include "BLT_translation.h" +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "BKE_tracking.h" + #include "COM_node_operation.hh" +#include "COM_utilities.hh" #include "node_composite_util.hh" @@ -15,23 +25,29 @@ namespace blender::nodes::node_composite_cornerpin_cc { static void cmp_node_cornerpin_declare(NodeDeclarationBuilder &b) { - b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_input(N_("Upper Left")) .default_value({0.0f, 1.0f, 0.0f}) .min(0.0f) - .max(1.0f); + .max(1.0f) + .compositor_expects_single_value(); b.add_input(N_("Upper Right")) .default_value({1.0f, 1.0f, 0.0f}) .min(0.0f) - .max(1.0f); + .max(1.0f) + .compositor_expects_single_value(); b.add_input(N_("Lower Left")) .default_value({0.0f, 0.0f, 0.0f}) .min(0.0f) - .max(1.0f); + .max(1.0f) + .compositor_expects_single_value(); b.add_input(N_("Lower Right")) .default_value({1.0f, 0.0f, 0.0f}) .min(0.0f) - .max(1.0f); + .max(1.0f) + .compositor_expects_single_value(); b.add_output(N_("Image")); b.add_output(N_("Plane")); } @@ -44,9 +60,70 @@ class CornerPinOperation : public NodeOperation { void execute() override { - get_input("Image").pass_through(get_result("Image")); - get_result("Plane").allocate_invalid(); - context().set_info_message("Viewport compositor setup not fully supported"); + const float3x3 homography_matrix = compute_homography_matrix(); + + Result &input_image = get_input("Image"); + Result &output_image = get_result("Image"); + Result &output_mask = get_result("Plane"); + if (input_image.is_single_value() || homography_matrix == float3x3::identity()) { + if (output_image.should_compute()) { + input_image.pass_through(output_image); + } + if (output_mask.should_compute()) { + output_mask.allocate_single_value(); + output_mask.set_float_value(1.0f); + } + return; + } + + GPUShader *shader = shader_manager().get("compositor_plane_deform"); + GPU_shader_bind(shader); + + GPU_shader_uniform_mat3_as_mat4(shader, "homography_matrix", homography_matrix.ptr()); + + GPU_texture_mipmap_mode(input_image.texture(), true, true); + GPU_texture_anisotropic_filter(input_image.texture(), true); + GPU_texture_extend_mode(input_image.texture(), GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER); + input_image.bind_as_texture(shader, "input_tx"); + + const Domain domain = compute_domain(); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + output_mask.allocate_texture(domain); + output_mask.bind_as_image(shader, "mask_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + input_image.unbind_as_texture(); + output_image.unbind_as_image(); + output_mask.unbind_as_image(); + GPU_shader_unbind(); + } + + float3x3 compute_homography_matrix() + { + float2 lower_left = get_input("Lower Left").get_vector_value_default(float4(0.0f)).xy(); + float2 lower_right = get_input("Lower Right").get_vector_value_default(float4(0.0f)).xy(); + float2 upper_right = get_input("Upper Right").get_vector_value_default(float4(0.0f)).xy(); + float2 upper_left = get_input("Upper Left").get_vector_value_default(float4(0.0f)).xy(); + + /* The inputs are invalid because the plane is not convex, fallback to an identity operation in + * that case. */ + if (!is_quad_convex_v2(lower_left, lower_right, upper_right, upper_left)) { + return float3x3::identity(); + } + + /* Compute a 2D projection matrix that projects from the corners of the image in normalized + * coordinates into the corners of the input plane. */ + float3x3 homography_matrix; + float corners[4][2] = {{lower_left.x, lower_left.y}, + {lower_right.x, lower_right.y}, + {upper_right.x, upper_right.y}, + {upper_left.x, upper_left.y}}; + float identity_corners[4][2] = {{0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f}}; + BKE_tracking_homography_between_two_quads(corners, identity_corners, homography_matrix.ptr()); + return homography_matrix; } }; @@ -66,8 +143,6 @@ void register_node_type_cmp_cornerpin() cmp_node_type_base(&ntype, CMP_NODE_CORNERPIN, "Corner Pin", NODE_CLASS_DISTORT); ntype.declare = file_ns::cmp_node_cornerpin_declare; ntype.get_compositor_operation = file_ns::get_compositor_operation; - ntype.realtime_compositor_unsupported_message = N_( - "Node not supported in the Viewport compositor"); nodeRegisterType(&ntype); }