From 481c524734bcd4dfc86303ce97405c9d242163ad Mon Sep 17 00:00:00 2001 From: Benjamin Beilharz Date: Thu, 31 Jul 2025 10:51:59 +0200 Subject: [PATCH] Compositor: Add extension modes to displacement node The node uses anisotropic sampling (which is currently not supported for the extension mode), so extension modes are only rendered in the node if any other interpolation method, but anisotropic is selected. Pull Request: https://projects.blender.org/blender/blender/pulls/143367 --- .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_500.cc | 20 +++++ source/blender/makesdna/DNA_node_types.h | 2 + .../blender/makesrna/intern/rna_nodetree.cc | 12 +++ .../nodes/node_composite_displace.cc | 84 ++++++++++++++----- 5 files changed, 99 insertions(+), 21 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 7cd87b1f5c8..717d9785ce1 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 48 +#define BLENDER_FILE_SUBVERSION 49 /* 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_500.cc b/source/blender/blenloader/intern/versioning_500.cc index eab6c122ad1..57aa32f7e7b 100644 --- a/source/blender/blenloader/intern/versioning_500.cc +++ b/source/blender/blenloader/intern/versioning_500.cc @@ -2019,6 +2019,26 @@ void blo_do_versions_500(FileData * /*fd*/, Library * /*lib*/, Main *bmain) } } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 49)) { + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type != NTREE_COMPOSIT) { + continue; + } + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type_legacy != CMP_NODE_DISPLACE) { + continue; + } + if (node->storage == nullptr) { + continue; + } + NodeDisplaceData *data = static_cast(node->storage); + data->extension_x = CMP_NODE_EXTENSION_MODE_CLIP; + data->extension_y = CMP_NODE_EXTENSION_MODE_CLIP; + } + FOREACH_NODETREE_END; + } + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 910ab86d229..d63d260eba9 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1595,6 +1595,8 @@ typedef struct NodeScaleData { typedef struct NodeDisplaceData { short interpolation; + char extension_x; + char extension_y; } NodeDisplaceData; typedef struct NodePlaneTrackDeformData { diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 60355be3a06..6aed63cf1f5 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -6682,6 +6682,18 @@ static void def_cmp_displace(BlenderRNA * /*brna*/, StructRNA *srna) RNA_def_property_enum_items(prop, cmp_interpolation_items); RNA_def_property_ui_text(prop, "Interpolation", "Interpolation method"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "extension_x", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "extension_x"); + RNA_def_property_enum_items(prop, cmp_extension_mode_items); + RNA_def_property_ui_text(prop, "X Extension Mode", "The extension mode applied to the X axis"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "extension_y", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "extension_y"); + RNA_def_property_enum_items(prop, cmp_extension_mode_items); + RNA_def_property_ui_text(prop, "Y Extension Mode", "The extension mode applied to the Y axis"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } static void def_cmp_scale(BlenderRNA * /*brna*/, StructRNA *srna) diff --git a/source/blender/nodes/composite/nodes/node_composite_displace.cc b/source/blender/nodes/composite/nodes/node_composite_displace.cc index 668084d90d2..be75411a3e7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_displace.cc +++ b/source/blender/nodes/composite/nodes/node_composite_displace.cc @@ -13,8 +13,10 @@ #include "BLI_assert.h" #include "BLI_math_vector.hh" #include "BLI_math_vector_types.hh" +#include "BLI_utildefines.h" #include "DNA_node_types.h" +#include "RNA_access.hh" #include "RNA_types.hh" #include "GPU_shader.hh" @@ -64,12 +66,22 @@ static void cmp_node_init_displace(bNodeTree * /*ntree*/, bNode *node) { NodeDisplaceData *data = MEM_callocN(__func__); data->interpolation = CMP_NODE_INTERPOLATION_ANISOTROPIC; + data->extension_x = CMP_NODE_EXTENSION_MODE_CLIP; + data->extension_y = CMP_NODE_EXTENSION_MODE_CLIP; node->storage = data; } static void cmp_buts_displace(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { - layout->prop(ptr, "interpolation", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiLayout &column_interpolation_extension_modes = layout->column(true); + + column_interpolation_extension_modes.prop( + ptr, "interpolation", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + if (RNA_enum_get(ptr, "interpolation") != CMP_NODE_INTERPOLATION_ANISOTROPIC) { + uiLayout &row = column_interpolation_extension_modes.row(true); + row.prop(ptr, "extension_x", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + row.prop(ptr, "extension_y", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + } } using namespace blender::compositor; @@ -98,6 +110,8 @@ class DisplaceOperation : public NodeOperation { void execute_gpu() { const Interpolation interpolation = this->get_interpolation(); + const ExtensionMode extension_x = this->get_extension_mode_x(); + const ExtensionMode extension_y = this->get_extension_mode_y(); GPUShader *shader = context().get_shader(this->get_shader_name(interpolation)); GPU_shader_bind(shader); @@ -111,7 +125,8 @@ class DisplaceOperation : public NodeOperation { interpolation, Interpolation::Bilinear, Interpolation::Bicubic); GPU_texture_filter_mode(input_image, use_bilinear); } - GPU_texture_extend_mode(input_image, GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER); + GPU_texture_extend_mode_x(input_image, map_extension_mode_to_extend_mode(extension_x)); + GPU_texture_extend_mode_y(input_image, map_extension_mode_to_extend_mode(extension_y)); input_image.bind_as_texture(shader, "input_tx"); const Result &input_displacement = get_input("Vector"); @@ -144,6 +159,8 @@ class DisplaceOperation : public NodeOperation { const Result &y_scale = get_input("Y Scale"); const Interpolation interpolation = this->get_interpolation(); + const ExtensionMode extension_x = this->get_extension_mode_x(); + const ExtensionMode extension_y = this->get_extension_mode_y(); const Domain domain = compute_domain(); Result &output = get_result("Image"); output.allocate_texture(domain); @@ -154,8 +171,15 @@ class DisplaceOperation : public NodeOperation { this->compute_anisotropic(size, image, output, input_displacement, x_scale, y_scale); } else { - this->compute_interpolation( - interpolation, size, image, output, input_displacement, x_scale, y_scale); + this->compute_interpolation(interpolation, + size, + image, + output, + input_displacement, + x_scale, + y_scale, + extension_x, + extension_y); } } @@ -165,26 +189,16 @@ class DisplaceOperation : public NodeOperation { Result &output, const Result &input_displacement, const Result &x_scale, - const Result &y_scale) const + const Result &y_scale, + const ExtensionMode &extension_mode_x, + const ExtensionMode &extension_mode_y) const { parallel_for(size, [&](const int2 base_texel) { const float2 coordinates = compute_coordinates( base_texel, size, input_displacement, x_scale, y_scale); - switch (interpolation) { - /* The anisotropic case requires gradient computation and is handled separately. */ - case Interpolation::Anisotropic: - BLI_assert_unreachable(); - break; - case Interpolation::Nearest: - output.store_pixel(base_texel, image.sample_nearest_zero(coordinates)); - break; - case Interpolation::Bilinear: - output.store_pixel(base_texel, image.sample_bilinear_zero(coordinates)); - break; - case Interpolation::Bicubic: - output.store_pixel(base_texel, image.sample_cubic_wrap(coordinates, false, false)); - break; - } + output.store_pixel( + base_texel, + image.sample(coordinates, interpolation, extension_mode_x, extension_mode_y)); }); } @@ -307,6 +321,36 @@ class DisplaceOperation : public NodeOperation { return Interpolation::Nearest; } + ExtensionMode get_extension_mode_x() + { + switch (static_cast(node_storage(bnode()).extension_x)) { + case CMP_NODE_EXTENSION_MODE_CLIP: + return ExtensionMode::Clip; + case CMP_NODE_EXTENSION_MODE_REPEAT: + return ExtensionMode::Repeat; + case CMP_NODE_EXTENSION_MODE_EXTEND: + return ExtensionMode::Extend; + } + + BLI_assert_unreachable(); + return ExtensionMode::Clip; + } + + ExtensionMode get_extension_mode_y() + { + switch (static_cast(node_storage(bnode()).extension_y)) { + case CMP_NODE_EXTENSION_MODE_CLIP: + return ExtensionMode::Clip; + case CMP_NODE_EXTENSION_MODE_REPEAT: + return ExtensionMode::Repeat; + case CMP_NODE_EXTENSION_MODE_EXTEND: + return ExtensionMode::Extend; + } + + BLI_assert_unreachable(); + return ExtensionMode::Clip; + } + bool is_identity() { const Result &input_image = get_input("Image");