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
This commit is contained in:
Benjamin Beilharz
2025-07-31 10:51:59 +02:00
committed by Omar Emara
parent d77c98d1d4
commit 481c524734
5 changed files with 99 additions and 21 deletions

View File

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

View File

@@ -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<NodeDisplaceData *>(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.

View File

@@ -1595,6 +1595,8 @@ typedef struct NodeScaleData {
typedef struct NodeDisplaceData {
short interpolation;
char extension_x;
char extension_y;
} NodeDisplaceData;
typedef struct NodePlaneTrackDeformData {

View File

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

View File

@@ -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<NodeDisplaceData>(__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<CMPExtensionMode>(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<CMPExtensionMode>(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");