Compositor: Expose interpolation in Corner Pin node

This patch exposes an interpolation option to the Corner Pin node,
allowing to choose between Nearest, Bilinear, Bicubic, and Anisotropic.

Pull Request: https://projects.blender.org/blender/blender/pulls/123206
This commit is contained in:
Maxime-Cots
2025-04-08 12:13:47 +02:00
committed by Omar Emara
parent 2a36b613ca
commit a4a4dc7da5
10 changed files with 210 additions and 23 deletions

View File

@@ -27,7 +27,7 @@
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 17
#define BLENDER_FILE_SUBVERSION 18
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@@ -6684,6 +6684,19 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
version_set_uv_face_overlay_defaults(bmain);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 18)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_COMPOSIT) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type_legacy == CMP_NODE_CORNERPIN) {
node->custom1 = CMP_NODE_CORNER_PIN_INTERPOLATION_ANISOTROPIC;
}
}
}
}
FOREACH_NODETREE_END;
}
/* Always run this versioning; meshes are written with the legacy format which always needs to
* be converted to the new format on file load. Can be moved to a subversion check in a larger
* breaking release. */

View File

@@ -233,10 +233,11 @@ set(GLSL_SRC
shaders/compositor_parallel_reduction.glsl
shaders/compositor_pixel_coordinates.glsl
shaders/compositor_pixelate.glsl
shaders/compositor_plane_deform.glsl
shaders/compositor_plane_deform_anisotropic.glsl
shaders/compositor_plane_deform_mask.glsl
shaders/compositor_plane_deform_motion_blur.glsl
shaders/compositor_plane_deform_motion_blur_mask.glsl
shaders/compositor_plane_deform.glsl
shaders/compositor_premultiply_alpha.glsl
shaders/compositor_projector_lens_distortion.glsl
shaders/compositor_read_input.glsl

View File

@@ -409,6 +409,9 @@ class Result {
/* Identical to sample_nearest_extended but with bilinear interpolation. */
float4 sample_bilinear_extended(const float2 &coordinates) const;
/* Identical to sample_nearest_extended but with cubic interpolation. */
float4 sample_cubic_extended(const float2 &coordinates) const;
float4 sample_nearest_wrap(const float2 &coordinates, bool wrap_x, bool wrap_y) const;
float4 sample_bilinear_wrap(const float2 &coordinates, bool wrap_x, bool wrap_y) const;
float4 sample_cubic_wrap(const float2 &coordinates, bool wrap_x, bool wrap_y) const;
@@ -756,6 +759,28 @@ BLI_INLINE_METHOD float4 Result::sample_bilinear_extended(const float2 &coordina
return pixel_value;
}
BLI_INLINE_METHOD float4 Result::sample_cubic_extended(const float2 &coordinates) const
{
float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
if (is_single_value_) {
this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
return pixel_value;
}
const int2 size = domain_.size;
const float2 texel_coordinates = (coordinates * float2(size)) - 0.5f;
const float *buffer = static_cast<const float *>(this->cpu_data().data());
math::interpolate_cubic_bspline_fl(buffer,
pixel_value,
size.x,
size.y,
this->channels_count(),
texel_coordinates.x,
texel_coordinates.y);
return pixel_value;
}
/**
* Given a Result as the userdata argument, sample it at the given coordinates using extended
* boundary condition and write the result to the result argument.

View File

@@ -2,6 +2,7 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_bicubic_sampler_lib.glsl"
#include "gpu_shader_compositor_texture_utilities.glsl"
void main()
@@ -19,14 +20,7 @@ void main()
}
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. Divide by the
* output size since textureGrad assumes derivatives with respect to texel coordinates. */
vec2 x_gradient = (homography_matrix[0].xy / transformed_coordinates.z) / output_size.x;
vec2 y_gradient = (homography_matrix[1].xy / transformed_coordinates.z) / output_size.y;
vec4 sampled_color = textureGrad(input_tx, projected_coordinates, x_gradient, y_gradient);
vec4 sampled_color = SAMPLER_FUNCTION(input_tx, projected_coordinates);
/* Premultiply the mask value as an alpha. */
vec4 plane_color = sampled_color * texture_load(mask_tx, texel).x;

View File

@@ -0,0 +1,35 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_compositor_texture_utilities.glsl"
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
vec2 output_size = vec2(imageSize(output_img));
vec2 coordinates = (vec2(texel) + vec2(0.5)) / output_size;
vec3 transformed_coordinates = to_float3x3(homography_matrix) * vec3(coordinates, 1.0);
/* Point is at infinity and will be zero when sampled, so early exit. */
if (transformed_coordinates.z == 0.0) {
imageStore(output_img, texel, vec4(0.0));
return;
}
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. Divide by the
* output size since textureGrad assumes derivatives with respect to texel coordinates. */
vec2 x_gradient = (homography_matrix[0].xy / transformed_coordinates.z) / output_size.x;
vec2 y_gradient = (homography_matrix[1].xy / transformed_coordinates.z) / output_size.y;
vec4 sampled_color = textureGrad(input_tx, projected_coordinates, x_gradient, y_gradient);
/* Premultiply the mask value as an alpha. */
vec4 plane_color = sampled_color * texture_load(mask_tx, texel).x;
imageStore(output_img, texel, plane_color);
}

View File

@@ -12,16 +12,34 @@ COMPUTE_SOURCE("compositor_plane_deform_mask.glsl")
DO_STATIC_COMPILATION()
GPU_SHADER_CREATE_END()
GPU_SHADER_CREATE_INFO(compositor_plane_deform)
GPU_SHADER_CREATE_INFO(compositor_plane_deform_shared)
LOCAL_GROUP_SIZE(16, 16)
PUSH_CONSTANT(MAT4, homography_matrix)
SAMPLER(0, FLOAT_2D, input_tx)
SAMPLER(1, FLOAT_2D, mask_tx)
IMAGE(0, GPU_RGBA16F, WRITE, FLOAT_2D, output_img)
GPU_SHADER_CREATE_END()
GPU_SHADER_CREATE_INFO(compositor_plane_deform)
ADDITIONAL_INFO(compositor_plane_deform_shared)
DEFINE_VALUE("SAMPLER_FUNCTION", "texture")
COMPUTE_SOURCE("compositor_plane_deform.glsl")
DO_STATIC_COMPILATION()
GPU_SHADER_CREATE_END()
GPU_SHADER_CREATE_INFO(compositor_plane_deform_bicubic)
ADDITIONAL_INFO(compositor_plane_deform_shared)
DEFINE_VALUE("SAMPLER_FUNCTION", "texture_bicubic")
COMPUTE_SOURCE("compositor_plane_deform.glsl")
DO_STATIC_COMPILATION()
GPU_SHADER_CREATE_END()
GPU_SHADER_CREATE_INFO(compositor_plane_deform_anisotropic)
ADDITIONAL_INFO(compositor_plane_deform_shared)
COMPUTE_SOURCE("compositor_plane_deform_anisotropic.glsl")
DO_STATIC_COMPILATION()
GPU_SHADER_CREATE_END()
GPU_SHADER_CREATE_INFO(compositor_plane_deform_motion_blur_mask)
LOCAL_GROUP_SIZE(16, 16)
PUSH_CONSTANT(INT, number_of_motion_blur_samples)

View File

@@ -2931,6 +2931,14 @@ typedef enum CMPNodeInterpolation {
CMP_NODE_INTERPOLATION_BICUBIC = 2,
} CMPNodeInterpolation;
/* CornerPin node interpolation option. */
typedef enum CMPNodeCornerPinInterpolation {
CMP_NODE_CORNER_PIN_INTERPOLATION_NEAREST = 0,
CMP_NODE_CORNER_PIN_INTERPOLATION_BILINEAR = 1,
CMP_NODE_CORNER_PIN_INTERPOLATION_BICUBIC = 2,
CMP_NODE_CORNER_PIN_INTERPOLATION_ANISOTROPIC = 3,
} CMPNodeCornerPinInterpolation;
/* Stabilize 2D node. Stored in custom2. */
typedef enum CMPNodeStabilizeInverse {
CMP_NODE_STABILIZE_FLAG_INVERSE = 1,

View File

@@ -9191,6 +9191,42 @@ static void def_cmp_colorcorrection(BlenderRNA * /*brna*/, StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_cmp_cornerpin(BlenderRNA * /*brna*/, StructRNA *srna)
{
static const EnumPropertyItem cmp_cornerpin_interpolation_items[] = {
{CMP_NODE_CORNER_PIN_INTERPOLATION_NEAREST,
"NEAREST",
0,
"Nearest",
"Use Nearest interpolation"},
{CMP_NODE_CORNER_PIN_INTERPOLATION_BILINEAR,
"BILINEAR",
0,
"Bilinear",
"Use Bilinear interpolation"},
{CMP_NODE_CORNER_PIN_INTERPOLATION_BICUBIC,
"BICUBIC",
0,
"Bicubic",
"Use Cubic B-Spline interpolation"},
{CMP_NODE_CORNER_PIN_INTERPOLATION_ANISOTROPIC,
"ANISOTROPIC",
0,
"Anisotropic",
"Use Anisotropic interpolation"},
{0, nullptr, 0, nullptr, nullptr},
};
PropertyRNA *prop;
prop = RNA_def_property(srna, "interpolation", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "custom1");
RNA_def_property_enum_items(prop, cmp_cornerpin_interpolation_items);
RNA_def_property_enum_default(prop, CMP_NODE_CORNER_PIN_INTERPOLATION_ANISOTROPIC);
RNA_def_property_ui_text(prop, "Interpolation", "Interpolation method");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_cmp_viewer(BlenderRNA * /*brna*/, StructRNA *srna)
{
PropertyRNA *prop;
@@ -12544,7 +12580,7 @@ static void rna_def_nodes(BlenderRNA *brna)
define("CompositorNode", "CompositorNodeCombYUVA");
define("CompositorNode", "CompositorNodeComposite", def_cmp_composite);
define("CompositorNode", "CompositorNodeConvertColorSpace", def_cmp_convert_color_space);
define("CompositorNode", "CompositorNodeCornerPin");
define("CompositorNode", "CompositorNodeCornerPin", def_cmp_cornerpin);
define("CompositorNode", "CompositorNodeCrop", def_cmp_crop);
define("CompositorNode", "CompositorNodeCryptomatte", def_cmp_cryptomatte_legacy);
define("CompositorNode", "CompositorNodeCryptomatteV2", def_cmp_cryptomatte);

View File

@@ -16,6 +16,8 @@
#include "BKE_tracking.h"
#include "UI_interface.hh"
#include "COM_algorithm_smaa.hh"
#include "COM_node_operation.hh"
#include "COM_utilities.hh"
@@ -53,6 +55,16 @@ static void cmp_node_cornerpin_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>("Plane");
}
static void node_composit_init_cornerpin(bNodeTree * /*ntree*/, bNode *node)
{
node->custom1 = CMP_NODE_CORNER_PIN_INTERPOLATION_ANISOTROPIC;
}
static void node_composit_buts_cornerpin(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "interpolation", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
using namespace blender::compositor;
class CornerPinOperation : public NodeOperation {
@@ -106,14 +118,23 @@ class CornerPinOperation : public NodeOperation {
void compute_plane_gpu(const float3x3 &homography_matrix, Result &plane_mask)
{
GPUShader *shader = context().get_shader("compositor_plane_deform");
GPUShader *shader = this->context().get_shader(this->get_realization_shader_name());
GPU_shader_bind(shader);
GPU_shader_uniform_mat3_as_mat4(shader, "homography_matrix", homography_matrix.ptr());
Result &input_image = get_input("Image");
GPU_texture_mipmap_mode(input_image, true, true);
GPU_texture_anisotropic_filter(input_image, true);
/* The texture sampler should use bilinear interpolation for both the bilinear and bicubic
* cases, as the logic used by the bicubic realization shader expects textures to use bilinear
* interpolation. */
const CMPNodeCornerPinInterpolation interpolation = this->get_interpolation();
const bool use_bilinear = ELEM(interpolation,
CMP_NODE_CORNER_PIN_INTERPOLATION_BICUBIC,
CMP_NODE_CORNER_PIN_INTERPOLATION_BILINEAR);
const bool use_anisotropic = interpolation == CMP_NODE_CORNER_PIN_INTERPOLATION_ANISOTROPIC;
GPU_texture_filter_mode(input_image, use_bilinear);
GPU_texture_anisotropic_filter(input_image, use_anisotropic);
GPU_texture_extend_mode(input_image, GPU_SAMPLER_EXTEND_MODE_EXTEND);
input_image.bind_as_texture(shader, "input_tx");
@@ -150,17 +171,30 @@ class CornerPinOperation : public NodeOperation {
output.store_pixel(texel, float4(0.0f));
return;
}
float2 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. Divide by the
* output size since sample_ewa assumes derivatives with respect to texel coordinates. */
float2 x_gradient = (homography_matrix[0].xy() / transformed_coordinates.z) / size.x;
float2 y_gradient = (homography_matrix[1].xy() / transformed_coordinates.z) / size.y;
float4 sampled_color = input.sample_ewa_extended(
projected_coordinates, x_gradient, y_gradient);
float4 sampled_color;
switch (this->get_interpolation()) {
case CMP_NODE_CORNER_PIN_INTERPOLATION_BICUBIC:
sampled_color = input.sample_cubic_extended(projected_coordinates);
break;
case CMP_NODE_CORNER_PIN_INTERPOLATION_BILINEAR:
sampled_color = input.sample_bilinear_extended(projected_coordinates);
break;
case CMP_NODE_CORNER_PIN_INTERPOLATION_NEAREST:
sampled_color = input.sample_nearest_extended(projected_coordinates);
break;
case CMP_NODE_CORNER_PIN_INTERPOLATION_ANISOTROPIC:
/* 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. Divide by the
* output size since sample_ewa assumes derivatives with respect to texel coordinates. */
float2 x_gradient = (homography_matrix[0].xy() / transformed_coordinates.z) / size.x;
float2 y_gradient = (homography_matrix[1].xy() / transformed_coordinates.z) / size.y;
sampled_color = input.sample_ewa_extended(projected_coordinates, x_gradient, y_gradient);
break;
}
/* Premultiply the mask value as an alpha. */
float4 plane_color = sampled_color * plane_mask.load_pixel<float>(texel);
@@ -250,6 +284,27 @@ class CornerPinOperation : public NodeOperation {
BKE_tracking_homography_between_two_quads(corners, identity_corners, homography_matrix.ptr());
return homography_matrix;
}
const char *get_realization_shader_name() const
{
switch (this->get_interpolation()) {
case CMP_NODE_CORNER_PIN_INTERPOLATION_NEAREST:
case CMP_NODE_CORNER_PIN_INTERPOLATION_BILINEAR:
return "compositor_plane_deform";
case CMP_NODE_CORNER_PIN_INTERPOLATION_BICUBIC:
return "compositor_plane_deform_bicubic";
case CMP_NODE_CORNER_PIN_INTERPOLATION_ANISOTROPIC:
return "compositor_plane_deform_anisotropic";
}
BLI_assert_unreachable();
return "compositor_plane_deform_anisotropic";
}
CMPNodeCornerPinInterpolation get_interpolation() const
{
return static_cast<CMPNodeCornerPinInterpolation>(bnode().custom1);
}
};
static NodeOperation *get_compositor_operation(Context &context, DNode node)
@@ -271,6 +326,8 @@ void register_node_type_cmp_cornerpin()
ntype.enum_name_legacy = "CORNERPIN";
ntype.nclass = NODE_CLASS_DISTORT;
ntype.declare = file_ns::cmp_node_cornerpin_declare;
ntype.initfunc = file_ns::node_composit_init_cornerpin;
ntype.draw_buttons = file_ns::node_composit_buts_cornerpin;
ntype.get_compositor_operation = file_ns::get_compositor_operation;
blender::bke::node_register_type(ntype);