Compositor: Add interpolation options to displace node
This commit introduces interpolation methods (Nearest, Bilinear, Bicubic) for the Displace node in the compositor: - Exposes a dropdown selector for interpolation selection. - Introduces different methods for Anisotropic and other interpolation methods. Reference: #119592. Pull Request: https://projects.blender.org/blender/blender/pulls/139802
This commit is contained in:
committed by
Omar Emara
parent
d626bf6311
commit
8b67050575
@@ -27,7 +27,7 @@
|
||||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 7
|
||||
#define BLENDER_FILE_SUBVERSION 8
|
||||
|
||||
/* 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
|
||||
|
||||
@@ -146,6 +146,26 @@ void blo_do_versions_500(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
|
||||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 8)) {
|
||||
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 = MEM_callocN<NodeDisplaceData>(__func__);
|
||||
data->interpolation = CMP_NODE_INTERPOLATION_ANISOTROPIC;
|
||||
node->storage = data;
|
||||
}
|
||||
}
|
||||
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.
|
||||
|
||||
@@ -182,6 +182,7 @@ set(GLSL_SRC
|
||||
shaders/compositor_deriche_gaussian_blur_sum.glsl
|
||||
shaders/compositor_directional_blur.glsl
|
||||
shaders/compositor_displace.glsl
|
||||
shaders/compositor_displace_anisotropic.glsl
|
||||
shaders/compositor_double_edge_mask_compute_boundary.glsl
|
||||
shaders/compositor_double_edge_mask_compute_gradient.glsl
|
||||
shaders/compositor_edge_filter.glsl
|
||||
|
||||
@@ -2,13 +2,9 @@
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gpu_shader_bicubic_sampler_lib.glsl"
|
||||
#include "gpu_shader_compositor_texture_utilities.glsl"
|
||||
|
||||
/* A shared table that stores the displaced coordinates of all pixels in the work group. This is
|
||||
* necessary to avoid recomputing displaced coordinates when computing the gradients necessary for
|
||||
* anisotropic filtering, see the implementation for more information. */
|
||||
shared float2 displaced_coordinates_table[gl_WorkGroupSize.x][gl_WorkGroupSize.y];
|
||||
|
||||
void main()
|
||||
{
|
||||
int2 texel = int2(gl_GlobalInvocationID.xy);
|
||||
@@ -24,39 +20,5 @@ void main()
|
||||
float2 displacement = texture_load(displacement_tx, texel).xy * scale / float2(input_size);
|
||||
float2 displaced_coordinates = coordinates - displacement;
|
||||
|
||||
/* Store the displaced coordinates into the shared table and issue a barrier to later compute the
|
||||
* gradients from the table. */
|
||||
int2 table_index = int2(gl_LocalInvocationID.xy);
|
||||
displaced_coordinates_table[table_index.x][table_index.y] = displaced_coordinates;
|
||||
barrier();
|
||||
|
||||
/* Compute the partial derivative of the displaced coordinates along the x direction using a
|
||||
* finite difference approximation. Odd invocations use a forward finite difference equation
|
||||
* while even invocations use a backward finite difference equation. This is done such that
|
||||
* invocations at the edges of the work group wouldn't need access to pixels that are outside of
|
||||
* the work group.
|
||||
*
|
||||
* The x_step value is 1 for even invocations and when added to the x table index and multiplied
|
||||
* by the result yields a standard forward finite difference equation. The x_step value is -1 for
|
||||
* odd invocations and when added to the x table index and multiplied by the result yields a
|
||||
* standard backward finite difference equation, because multiplication by -1 flips the order of
|
||||
* subtraction.
|
||||
*
|
||||
* Divide by the input size since textureGrad assumes derivatives with respect to texel
|
||||
* coordinates. */
|
||||
int x_step = (table_index.x % 2) * -2 + 1;
|
||||
float2 x_neighbor = displaced_coordinates_table[table_index.x + x_step][table_index.y];
|
||||
float2 x_gradient = (x_neighbor - displaced_coordinates) * x_step / input_size.x;
|
||||
|
||||
/* Compute the partial derivative of the displaced coordinates along the y direction using a
|
||||
* finite difference approximation. See the previous code section for more information. */
|
||||
int y_step = (table_index.y % 2) * -2 + 1;
|
||||
float2 y_neighbor = displaced_coordinates_table[table_index.x][table_index.y + y_step];
|
||||
float2 y_gradient = (y_neighbor - displaced_coordinates) * y_step / input_size.y;
|
||||
|
||||
/* Sample the input using the displaced coordinates passing in the computed gradients in order to
|
||||
* utilize the anisotropic filtering capabilities of the sampler. */
|
||||
float4 displaced_color = textureGrad(input_tx, displaced_coordinates, x_gradient, y_gradient);
|
||||
|
||||
imageStore(output_img, texel, displaced_color);
|
||||
imageStore(output_img, texel, SAMPLER_FUNCTION(input_tx, displaced_coordinates));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gpu_shader_compositor_texture_utilities.glsl"
|
||||
|
||||
/* A shared table that stores the displaced coordinates of all pixels in the work group. This is
|
||||
* necessary to avoid recomputing displaced coordinates when computing the gradients necessary for
|
||||
* anisotropic filtering, see the implementation for more information. */
|
||||
shared float2 displaced_coordinates_table[gl_WorkGroupSize.x][gl_WorkGroupSize.y];
|
||||
|
||||
void main()
|
||||
{
|
||||
int2 texel = int2(gl_GlobalInvocationID.xy);
|
||||
int2 input_size = texture_size(input_tx);
|
||||
|
||||
/* 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. */
|
||||
float2 coordinates = (float2(texel) + float2(0.5f)) / float2(input_size);
|
||||
|
||||
/* Note that the input displacement is in pixel space, so divide by the input size to transform
|
||||
* it into the normalized sampler space. */
|
||||
float2 scale = float2(texture_load(x_scale_tx, texel).x, texture_load(y_scale_tx, texel).x);
|
||||
float2 displacement = texture_load(displacement_tx, texel).xy * scale / float2(input_size);
|
||||
float2 displaced_coordinates = coordinates - displacement;
|
||||
|
||||
/* Store the displaced coordinates into the shared table and issue a barrier to later compute the
|
||||
* gradients from the table. */
|
||||
int2 table_index = int2(gl_LocalInvocationID.xy);
|
||||
displaced_coordinates_table[table_index.x][table_index.y] = displaced_coordinates;
|
||||
barrier();
|
||||
|
||||
/* Compute the partial derivative of the displaced coordinates along the x direction using a
|
||||
* finite difference approximation. Odd invocations use a forward finite difference equation
|
||||
* while even invocations use a backward finite difference equation. This is done such that
|
||||
* invocations at the edges of the work group wouldn't need access to pixels that are outside of
|
||||
* the work group.
|
||||
*
|
||||
* The x_step value is 1 for even invocations and when added to the x table index and multiplied
|
||||
* by the result yields a standard forward finite difference equation. The x_step value is -1 for
|
||||
* odd invocations and when added to the x table index and multiplied by the result yields a
|
||||
* standard backward finite difference equation, because multiplication by -1 flips the order of
|
||||
* subtraction.
|
||||
*
|
||||
* Divide by the input size since textureGrad assumes derivatives with respect to texel
|
||||
* coordinates. */
|
||||
int x_step = (table_index.x % 2) * -2 + 1;
|
||||
float2 x_neighbor = displaced_coordinates_table[table_index.x + x_step][table_index.y];
|
||||
float2 x_gradient = (x_neighbor - displaced_coordinates) * x_step / input_size.x;
|
||||
|
||||
/* Compute the partial derivative of the displaced coordinates along the y direction using a
|
||||
* finite difference approximation. See the previous code section for more information. */
|
||||
int y_step = (table_index.y % 2) * -2 + 1;
|
||||
float2 y_neighbor = displaced_coordinates_table[table_index.x][table_index.y + y_step];
|
||||
float2 y_gradient = (y_neighbor - displaced_coordinates) * y_step / input_size.y;
|
||||
|
||||
/* Sample the input using the displaced coordinates passing in the computed gradients in order to
|
||||
* utilize the anisotropic filtering capabilities of the sampler. */
|
||||
float4 displaced_color = textureGrad(input_tx, displaced_coordinates, x_gradient, y_gradient);
|
||||
|
||||
imageStore(output_img, texel, displaced_color);
|
||||
}
|
||||
@@ -4,13 +4,31 @@
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_displace)
|
||||
GPU_SHADER_CREATE_INFO(compositor_displace_shared)
|
||||
LOCAL_GROUP_SIZE(16, 16)
|
||||
SAMPLER(0, sampler2D, input_tx)
|
||||
SAMPLER(1, sampler2D, displacement_tx)
|
||||
SAMPLER(2, sampler2D, x_scale_tx)
|
||||
SAMPLER(3, sampler2D, y_scale_tx)
|
||||
IMAGE(0, GPU_RGBA16F, write, image2D, output_img)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_displace)
|
||||
ADDITIONAL_INFO(compositor_displace_shared)
|
||||
COMPUTE_SOURCE("compositor_displace.glsl")
|
||||
DEFINE_VALUE("SAMPLER_FUNCTION", "texture")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_displace_bicubic)
|
||||
ADDITIONAL_INFO(compositor_displace_shared)
|
||||
COMPUTE_SOURCE("compositor_displace.glsl")
|
||||
DEFINE_VALUE("SAMPLER_FUNCTION", "texture_bicubic")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_displace_anisotropic)
|
||||
ADDITIONAL_INFO(compositor_displace_shared)
|
||||
COMPUTE_SOURCE("compositor_displace_anisotropic.glsl")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
@@ -1577,6 +1577,10 @@ typedef struct NodeScaleData {
|
||||
short interpolation;
|
||||
} NodeScaleData;
|
||||
|
||||
typedef struct NodeDisplaceData {
|
||||
short interpolation;
|
||||
} NodeDisplaceData;
|
||||
|
||||
typedef struct NodePlaneTrackDeformData {
|
||||
char tracking_object[64];
|
||||
char plane_track_name[64];
|
||||
|
||||
@@ -8066,6 +8066,19 @@ static void def_cmp_dilate_erode(BlenderRNA * /*brna*/, StructRNA *srna)
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
}
|
||||
|
||||
static void def_cmp_displace(BlenderRNA * /*brna*/, StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
RNA_def_struct_sdna_from(srna, "NodeDisplaceData", "storage");
|
||||
|
||||
prop = RNA_def_property(srna, "interpolation", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "interpolation");
|
||||
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");
|
||||
}
|
||||
|
||||
static void def_cmp_inpaint(BlenderRNA * /*brna*/, StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
@@ -13995,7 +14008,7 @@ static void rna_def_nodes(BlenderRNA *brna)
|
||||
define("CompositorNode", "CompositorNodeDespeckle", def_cmp_despeckle);
|
||||
define("CompositorNode", "CompositorNodeDiffMatte", def_cmp_diff_matte);
|
||||
define("CompositorNode", "CompositorNodeDilateErode", def_cmp_dilate_erode);
|
||||
define("CompositorNode", "CompositorNodeDisplace");
|
||||
define("CompositorNode", "CompositorNodeDisplace", def_cmp_displace);
|
||||
define("CompositorNode", "CompositorNodeDistanceMatte", def_cmp_distance_matte);
|
||||
define("CompositorNode", "CompositorNodeDoubleEdgeMask", def_cmp_double_edge_mask);
|
||||
define("CompositorNode", "CompositorNodeEllipseMask", def_cmp_ellipsemask);
|
||||
|
||||
@@ -6,21 +6,34 @@
|
||||
* \ingroup cmpnodes
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BKE_node.hh"
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
#include "RNA_types.hh"
|
||||
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "COM_domain.hh"
|
||||
#include "COM_node_operation.hh"
|
||||
#include "COM_utilities.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
#include "node_composite_util.hh"
|
||||
|
||||
/* **************** Displace ******************** */
|
||||
|
||||
namespace blender::nodes::node_composite_displace_cc {
|
||||
|
||||
NODE_STORAGE_FUNCS(NodeDisplaceData)
|
||||
|
||||
static void cmp_node_displace_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Color>("Image")
|
||||
@@ -46,6 +59,18 @@ static void cmp_node_displace_declare(NodeDeclarationBuilder &b)
|
||||
b.add_output<decl::Color>("Image");
|
||||
}
|
||||
|
||||
static void cmp_node_init_displace(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeDisplaceData *data = MEM_callocN<NodeDisplaceData>(__func__);
|
||||
data->interpolation = CMP_NODE_INTERPOLATION_ANISOTROPIC;
|
||||
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);
|
||||
}
|
||||
|
||||
using namespace blender::compositor;
|
||||
|
||||
class DisplaceOperation : public NodeOperation {
|
||||
@@ -71,12 +96,20 @@ class DisplaceOperation : public NodeOperation {
|
||||
|
||||
void execute_gpu()
|
||||
{
|
||||
GPUShader *shader = context().get_shader("compositor_displace");
|
||||
GPUShader *shader = context().get_shader(this->get_shader_name());
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
const Result &input_image = get_input("Image");
|
||||
GPU_texture_mipmap_mode(input_image, true, true);
|
||||
GPU_texture_anisotropic_filter(input_image, true);
|
||||
const Interpolation interpolation = this->get_interpolation();
|
||||
if (interpolation == Interpolation::Anisotropic) {
|
||||
GPU_texture_anisotropic_filter(input_image, true);
|
||||
GPU_texture_mipmap_mode(input_image, true, true);
|
||||
}
|
||||
else {
|
||||
const bool use_bilinear = ELEM(
|
||||
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);
|
||||
input_image.bind_as_texture(shader, "input_tx");
|
||||
|
||||
@@ -109,18 +142,76 @@ class DisplaceOperation : public NodeOperation {
|
||||
const Result &x_scale = get_input("X Scale");
|
||||
const Result &y_scale = get_input("Y Scale");
|
||||
|
||||
const Interpolation interpolation = this->get_interpolation();
|
||||
const Domain domain = compute_domain();
|
||||
Result &output = get_result("Image");
|
||||
output.allocate_texture(domain);
|
||||
|
||||
/* In order to perform EWA sampling, we need to compute the partial derivative of the displaced
|
||||
* coordinates along the x and y directions using a finite difference approximation. But in
|
||||
* order to avoid loading multiple neighboring displacement values for each pixel, we operate
|
||||
* on the image in 2x2 blocks of pixels, where the derivatives are computed horizontally and
|
||||
* vertically across the 2x2 block such that odd texels use a forward finite difference
|
||||
* equation while even invocations use a backward finite difference equation. */
|
||||
const int2 size = domain.size;
|
||||
|
||||
if (interpolation == Interpolation::Anisotropic) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void compute_interpolation(const Interpolation &interpolation,
|
||||
const int2 &size,
|
||||
const Result &image,
|
||||
Result &output,
|
||||
const Result &input_displacement,
|
||||
const Result &x_scale,
|
||||
const Result &y_scale) 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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* In order to perform EWA sampling, we need to compute the partial derivative of the
|
||||
* displaced coordinates along the x and y directions using a finite difference
|
||||
* approximation. But in order to avoid loading multiple neighboring displacement values for
|
||||
* each pixel, we operate on the image in 2x2 blocks of pixels, where the derivatives are
|
||||
* computed horizontally and vertically across the 2x2 block such that odd texels use a
|
||||
* forward finite difference equation while even invocations use a backward finite difference
|
||||
* equation. */
|
||||
void compute_anisotropic(const int2 &size,
|
||||
const Result &image,
|
||||
Result &output,
|
||||
const Result &input_displacement,
|
||||
const Result &x_scale,
|
||||
const Result &y_scale) const
|
||||
{
|
||||
auto compute_anisotropic_pixel = [&](const int2 &texel,
|
||||
const float2 &coordinates,
|
||||
const float2 &x_gradient,
|
||||
const float2 &y_gradient) {
|
||||
/* Sample the input using the displaced coordinates passing in the computed gradients in
|
||||
* order to utilize the anisotropic filtering capabilities of the sampler. */
|
||||
output.store_pixel(texel, image.sample_ewa_zero(coordinates, x_gradient, y_gradient));
|
||||
};
|
||||
parallel_for(math::divide_ceil(size, int2(2)), [&](const int2 base_texel) {
|
||||
/* Compute each of the pixels in the 2x2 block, making sure to exempt out of bounds right
|
||||
* and upper pixels. */
|
||||
const int x = base_texel.x * 2;
|
||||
const int y = base_texel.y * 2;
|
||||
|
||||
@@ -129,24 +220,14 @@ class DisplaceOperation : public NodeOperation {
|
||||
const int2 upper_left_texel = int2(x, y + 1);
|
||||
const int2 upper_right_texel = int2(x + 1, y + 1);
|
||||
|
||||
auto compute_coordinates = [&](const int2 &texel) -> float2 {
|
||||
/* Add 0.5 to evaluate the 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. */
|
||||
float2 coordinates = (float2(texel) + float2(0.5f)) / float2(size);
|
||||
|
||||
/* Note that the input displacement is in pixel space, so divide by the input size to
|
||||
* transform it into the normalized sampler space. */
|
||||
float2 scale = float2(x_scale.load_pixel_extended<float, true>(texel),
|
||||
y_scale.load_pixel_extended<float, true>(texel));
|
||||
float2 displacement = input_displacement.load_pixel_extended<float3, true>(texel).xy() *
|
||||
scale / float2(size);
|
||||
return coordinates - displacement;
|
||||
};
|
||||
|
||||
const float2 lower_left_coordinates = compute_coordinates(lower_left_texel);
|
||||
const float2 lower_right_coordinates = compute_coordinates(lower_right_texel);
|
||||
const float2 upper_left_coordinates = compute_coordinates(upper_left_texel);
|
||||
const float2 upper_right_coordinates = compute_coordinates(upper_right_texel);
|
||||
const float2 lower_left_coordinates = compute_coordinates(
|
||||
lower_left_texel, size, input_displacement, x_scale, y_scale);
|
||||
const float2 lower_right_coordinates = compute_coordinates(
|
||||
lower_right_texel, size, input_displacement, x_scale, y_scale);
|
||||
const float2 upper_left_coordinates = compute_coordinates(
|
||||
upper_left_texel, size, input_displacement, x_scale, y_scale);
|
||||
const float2 upper_right_coordinates = compute_coordinates(
|
||||
upper_right_texel, size, input_displacement, x_scale, y_scale);
|
||||
|
||||
/* Compute the partial derivatives using finite difference. Divide by the input size since
|
||||
* sample_ewa_zero assumes derivatives with respect to texel coordinates. */
|
||||
@@ -156,33 +237,68 @@ class DisplaceOperation : public NodeOperation {
|
||||
const float2 upper_x_gradient = (upper_right_coordinates - upper_left_coordinates) / size.x;
|
||||
|
||||
/* Computes one of the 2x2 pixels given its texel location, coordinates, and gradients. */
|
||||
auto compute_pixel = [&](const int2 &texel,
|
||||
const float2 &coordinates,
|
||||
const float2 &x_gradient,
|
||||
const float2 &y_gradient) {
|
||||
/* Sample the input using the displaced coordinates passing in the computed gradients in
|
||||
* order to utilize the anisotropic filtering capabilities of the sampler. */
|
||||
float4 displaced_color = image.sample_ewa_zero(coordinates, x_gradient, y_gradient);
|
||||
output.store_pixel(texel, displaced_color);
|
||||
};
|
||||
|
||||
/* Compute each of the pixels in the 2x2 block, making sure to exempt out of bounds right
|
||||
* and upper pixels. */
|
||||
compute_pixel(lower_left_texel, lower_left_coordinates, lower_x_gradient, left_y_gradient);
|
||||
compute_anisotropic_pixel(
|
||||
lower_left_texel, lower_left_coordinates, lower_x_gradient, left_y_gradient);
|
||||
if (lower_right_texel.x != size.x) {
|
||||
compute_pixel(
|
||||
compute_anisotropic_pixel(
|
||||
lower_right_texel, lower_right_coordinates, lower_x_gradient, right_y_gradient);
|
||||
}
|
||||
if (upper_left_texel.y != size.y) {
|
||||
compute_pixel(upper_left_texel, upper_left_coordinates, upper_x_gradient, left_y_gradient);
|
||||
compute_anisotropic_pixel(
|
||||
upper_left_texel, upper_left_coordinates, upper_x_gradient, left_y_gradient);
|
||||
}
|
||||
if (upper_right_texel.x != size.x && upper_right_texel.y != size.y) {
|
||||
compute_pixel(
|
||||
compute_anisotropic_pixel(
|
||||
upper_right_texel, upper_right_coordinates, upper_x_gradient, right_y_gradient);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
float2 compute_coordinates(const int2 &texel,
|
||||
const int2 &size,
|
||||
const Result &input_displacement,
|
||||
const Result &x_scale,
|
||||
const Result &y_scale) const
|
||||
{
|
||||
/* Add 0.5 to evaluate the 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. */
|
||||
float2 coordinates = (float2(texel) + float2(0.5f)) / float2(size);
|
||||
|
||||
/* Note that the input displacement is in pixel space, so divide by the input size to
|
||||
* transform it into the normalized sampler space. */
|
||||
float2 scale = float2(x_scale.load_pixel_extended<float, true>(texel),
|
||||
y_scale.load_pixel_extended<float, true>(texel));
|
||||
float2 displacement = input_displacement.load_pixel_extended<float3, true>(texel).xy() *
|
||||
scale / float2(size);
|
||||
return coordinates - displacement;
|
||||
}
|
||||
|
||||
const char *get_shader_name() const
|
||||
{
|
||||
if (this->get_interpolation() == Interpolation::Anisotropic) {
|
||||
return "compositor_displace_anisotropic";
|
||||
}
|
||||
return "compositor_displace";
|
||||
}
|
||||
|
||||
Interpolation get_interpolation() const
|
||||
{
|
||||
switch (node_storage(bnode()).interpolation) {
|
||||
case CMP_NODE_INTERPOLATION_ANISOTROPIC:
|
||||
return Interpolation::Anisotropic;
|
||||
case CMP_NODE_INTERPOLATION_NEAREST:
|
||||
return Interpolation::Nearest;
|
||||
case CMP_NODE_INTERPOLATION_BILINEAR:
|
||||
return Interpolation::Bilinear;
|
||||
case CMP_NODE_INTERPOLATION_BICUBIC:
|
||||
return Interpolation::Bicubic;
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return Interpolation::Nearest;
|
||||
}
|
||||
|
||||
bool is_identity()
|
||||
{
|
||||
const Result &input_image = get_input("Image");
|
||||
@@ -228,6 +344,10 @@ static void register_node_type_cmp_displace()
|
||||
ntype.enum_name_legacy = "DISPLACE";
|
||||
ntype.nclass = NODE_CLASS_DISTORT;
|
||||
ntype.declare = file_ns::cmp_node_displace_declare;
|
||||
ntype.draw_buttons = file_ns::cmp_buts_displace;
|
||||
ntype.initfunc = file_ns::cmp_node_init_displace;
|
||||
blender::bke::node_type_storage(
|
||||
ntype, "NodeDisplaceData", node_free_standard_storage, node_copy_standard_storage);
|
||||
ntype.get_compositor_operation = file_ns::get_compositor_operation;
|
||||
|
||||
blender::bke::node_register_type(ntype);
|
||||
|
||||
Reference in New Issue
Block a user