diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index c51547af1b5..92b3c6cab4e 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -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 diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 4d0b4883c5a..6a37fc3568b 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -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. */ diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 74bd8f0767b..05c465997ca 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -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 diff --git a/source/blender/compositor/COM_result.hh b/source/blender/compositor/COM_result.hh index 8c6a5829afd..8050829586d 100644 --- a/source/blender/compositor/COM_result.hh +++ b/source/blender/compositor/COM_result.hh @@ -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(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. diff --git a/source/blender/compositor/shaders/compositor_plane_deform.glsl b/source/blender/compositor/shaders/compositor_plane_deform.glsl index 145a735e1d0..e29a35ce1b3 100644 --- a/source/blender/compositor/shaders/compositor_plane_deform.glsl +++ b/source/blender/compositor/shaders/compositor_plane_deform.glsl @@ -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; diff --git a/source/blender/compositor/shaders/compositor_plane_deform_anisotropic.glsl b/source/blender/compositor/shaders/compositor_plane_deform_anisotropic.glsl new file mode 100644 index 00000000000..145a735e1d0 --- /dev/null +++ b/source/blender/compositor/shaders/compositor_plane_deform_anisotropic.glsl @@ -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); +} diff --git a/source/blender/compositor/shaders/infos/compositor_plane_deform_info.hh b/source/blender/compositor/shaders/infos/compositor_plane_deform_info.hh index 1aacbc11796..299b6e86abd 100644 --- a/source/blender/compositor/shaders/infos/compositor_plane_deform_info.hh +++ b/source/blender/compositor/shaders/infos/compositor_plane_deform_info.hh @@ -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) diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 241aec6fb0e..905b31dc829 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -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, diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 2fdea82145d..d211c264341 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -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); diff --git a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc index 788bce5e499..fcddb84fcb6 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc @@ -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("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(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(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);