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
This commit is contained in:
Omar Emara
2023-04-27 07:18:19 +02:00
committed by Omar Emara
parent e0a3212176
commit fa2dbceadd
4 changed files with 123 additions and 10 deletions

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_input<decl::Color>(N_("Image"))
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
.compositor_domain_priority(0);
b.add_input<decl::Vector>(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<decl::Vector>(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<decl::Vector>(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<decl::Vector>(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<decl::Color>(N_("Image"));
b.add_output<decl::Float>(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);
}