Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/nodes/composite/nodes')
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_alpha_over.cc68
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_antialiasing.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc73
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_blur.cc419
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bokehblur.cc121
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bokehimage.cc66
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_boxmask.cc99
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_brightness.cc45
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_channel_matte.cc96
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc63
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_color_matte.cc62
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_color_spill.cc112
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_colorbalance.cc69
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc81
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_composite.cc129
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_cornerpin.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_crop.cc168
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc58
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_curves.cc248
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_defocus.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_denoise.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_despeckle.cc72
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_diff_matte.cc56
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_dilate.cc486
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_directionalblur.cc140
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_displace.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_distance_matte.cc72
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc99
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_exposure.cc31
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_filter.cc130
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_flip.cc64
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_gamma.cc32
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_glare.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc49
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_huecorrect.cc67
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_id_mask.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_image.cc277
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_inpaint.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_invert.cc55
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_keying.cc22
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc42
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_lensdist.cc204
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_levels.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_luma_matte.cc55
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_map_range.cc67
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_map_uv.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_map_value.cc60
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_mask.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_math.cc67
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_mixrgb.cc125
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_movieclip.cc190
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_normal.cc42
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_normalize.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_output_file.cc27
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_pixelate.cc49
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc53
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_posterize.cc35
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_premulkey.cc39
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_rgb.cc30
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_rotate.cc54
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_scale.cc167
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_scene_time.cc36
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc124
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc77
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc77
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc63
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc126
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc76
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_setalpha.cc42
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_split_viewer.cc71
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sunbeams.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_switch.cc28
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_switchview.cc22
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_texture.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_tonemap.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_trackpos.cc53
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_transform.cc77
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_translate.cc72
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc152
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_value.cc26
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_vec_blur.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_viewer.cc129
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_zcombine.cc21
87 files changed, 6423 insertions, 188 deletions
diff --git a/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc
index d392b810bc1..12f81da3d1c 100644
--- a/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc
@@ -8,17 +8,32 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** ALPHAOVER ******************** */
namespace blender::nodes::node_composite_alpha_over_cc {
+NODE_STORAGE_FUNCS(NodeTwoFloats)
+
static void cmp_node_alphaover_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Image"), "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(2);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Image"), "Image_001")
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -36,6 +51,52 @@ static void node_composit_buts_alphaover(uiLayout *layout, bContext *UNUSED(C),
uiItemR(col, ptr, "premul", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class AlphaOverShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float premultiply_factor = get_premultiply_factor();
+ if (premultiply_factor != 0.0f) {
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_alpha_over_mixed",
+ inputs,
+ outputs,
+ GPU_uniform(&premultiply_factor));
+ return;
+ }
+
+ if (get_use_premultiply()) {
+ GPU_stack_link(material, &bnode(), "node_composite_alpha_over_key", inputs, outputs);
+ return;
+ }
+
+ GPU_stack_link(material, &bnode(), "node_composite_alpha_over_premultiply", inputs, outputs);
+ }
+
+ bool get_use_premultiply()
+ {
+ return bnode().custom1;
+ }
+
+ float get_premultiply_factor()
+ {
+ return node_storage(bnode()).x;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new AlphaOverShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_alpha_over_cc
void register_node_type_cmp_alphaover()
@@ -50,6 +111,7 @@ void register_node_type_cmp_alphaover()
node_type_init(&ntype, file_ns::node_alphaover_init);
node_type_storage(
&ntype, "NodeTwoFloats", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc
index f45b678fc50..55fe3366526 100644
--- a/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Anti-Aliasing (SMAA 1x) ******************** */
@@ -42,6 +44,23 @@ static void node_composit_buts_antialiasing(uiLayout *layout, bContext *UNUSED(C
uiItemR(col, ptr, "corner_rounding", 0, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class AntiAliasingOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new AntiAliasingOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_antialiasing_cc
void register_node_type_cmp_antialiasing()
@@ -58,6 +77,7 @@ void register_node_type_cmp_antialiasing()
node_type_init(&ntype, file_ns::node_composit_init_antialiasing);
node_type_storage(
&ntype, "NodeAntiAliasingData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc
index ad4a1f701d6..ac9a6c89aa4 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc
@@ -5,19 +5,32 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** BILATERALBLUR ******************** */
namespace blender::nodes::node_composite_bilateralblur_cc {
+NODE_STORAGE_FUNCS(NodeBilateralBlurData)
+
static void cmp_node_bilateralblur_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Determinator")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Determinator"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -42,6 +55,61 @@ static void node_composit_buts_bilateralblur(uiLayout *layout,
uiItemR(col, ptr, "sigma_space", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BilateralBlurOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ const Result &input_image = get_input("Image");
+ /* Single value inputs can't be blurred and are returned as is. */
+ if (input_image.is_single_value()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_bilateral_blur");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1i(shader, "radius", get_blur_radius());
+ GPU_shader_uniform_1f(shader, "threshold", get_threshold());
+
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Result &determinator_image = get_input("Determinator");
+ determinator_image.bind_as_texture(shader, "determinator_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ input_image.unbind_as_texture();
+ determinator_image.unbind_as_texture();
+ }
+
+ int get_blur_radius()
+ {
+ return math::ceil(node_storage(bnode()).iter + node_storage(bnode()).sigma_space);
+ }
+
+ float get_threshold()
+ {
+ return node_storage(bnode()).sigma_color;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BilateralBlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_bilateralblur_cc
void register_node_type_cmp_bilateralblur()
@@ -56,6 +124,7 @@ void register_node_type_cmp_bilateralblur()
node_type_init(&ntype, file_ns::node_composit_init_bilateralblur);
node_type_storage(
&ntype, "NodeBilateralBlurData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_blur.cc b/source/blender/nodes/composite/nodes/node_composite_blur.cc
index 7beffe15c8e..630f18361e3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_blur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_blur.cc
@@ -5,17 +5,36 @@
* \ingroup cmpnodes
*/
+#include <cstdint>
+
+#include "BLI_array.hh"
+#include "BLI_assert.h"
+#include "BLI_index_range.hh"
+#include "BLI_math_base.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_math_vector.hh"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "RE_pipeline.h"
+
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** BLUR ******************** */
namespace blender::nodes::node_composite_blur_cc {
+NODE_STORAGE_FUNCS(NodeBlurData)
+
static void cmp_node_blur_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
@@ -71,6 +90,405 @@ static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), Point
uiItemR(col, ptr, "use_extended_bounds", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+/* A helper class that computes and caches a 1D GPU texture containing the weights of the separable
+ * filter of the given type and radius. The filter is assumed to be symmetric, because the filter
+ * functions are all even functions. Consequently, only the positive half of the filter is computed
+ * and the shader takes that into consideration. */
+class SymmetricSeparableBlurWeights {
+ private:
+ float radius_ = 1.0f;
+ int type_ = R_FILTER_GAUSS;
+ GPUTexture *texture_ = nullptr;
+
+ public:
+ ~SymmetricSeparableBlurWeights()
+ {
+ if (texture_) {
+ GPU_texture_free(texture_);
+ }
+ }
+
+ /* Check if a texture containing the weights was already computed for the given filter type and
+ * radius. If such texture exists, do nothing, otherwise, free the already computed texture and
+ * recompute it with the given filter type and radius. */
+ void update(float radius, int type)
+ {
+ if (texture_ && type == type_ && radius == radius_) {
+ return;
+ }
+
+ if (texture_) {
+ GPU_texture_free(texture_);
+ }
+
+ /* The size of filter is double the radius plus 1, but since the filter is symmetric, we only
+ * compute half of it and no doubling happens. We add 1 to make sure the filter size is always
+ * odd and there is a center weight. */
+ const int size = math::ceil(radius) + 1;
+ Array<float> weights(size);
+
+ float sum = 0.0f;
+
+ /* First, compute the center weight. */
+ const float center_weight = RE_filter_value(type, 0.0f);
+ weights[0] = center_weight;
+ sum += center_weight;
+
+ /* Second, compute the other weights in the positive direction, making sure to add double the
+ * weight to the sum of weights because the filter is symmetric and we only loop over half of
+ * it. Skip the center weight already computed by dropping the front index. */
+ const float scale = radius > 0.0f ? 1.0f / radius : 0.0f;
+ for (const int i : weights.index_range().drop_front(1)) {
+ const float weight = RE_filter_value(type, i * scale);
+ weights[i] = weight;
+ sum += weight * 2.0f;
+ }
+
+ /* Finally, normalize the weights. */
+ for (const int i : weights.index_range()) {
+ weights[i] /= sum;
+ }
+
+ texture_ = GPU_texture_create_1d("Weights", size, 1, GPU_R16F, weights.data());
+
+ type_ = type;
+ radius_ = radius;
+ }
+
+ void bind_as_texture(GPUShader *shader, const char *texture_name)
+ {
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name);
+ GPU_texture_bind(texture_, texture_image_unit);
+ }
+
+ void unbind_as_texture()
+ {
+ GPU_texture_unbind(texture_);
+ }
+};
+
+/* A helper class that computes and caches a 2D GPU texture containing the weights of the filter of
+ * the given type and radius. The filter is assumed to be symmetric, because the filter functions
+ * are evaluated on the normalized distance to the center. Consequently, only the upper right
+ * quadrant are computed and the shader takes that into consideration. */
+class SymmetricBlurWeights {
+ private:
+ int type_ = R_FILTER_GAUSS;
+ float2 radius_ = float2(1.0f);
+ GPUTexture *texture_ = nullptr;
+
+ public:
+ ~SymmetricBlurWeights()
+ {
+ if (texture_) {
+ GPU_texture_free(texture_);
+ }
+ }
+
+ /* Check if a texture containing the weights was already computed for the given filter type and
+ * radius. If such texture exists, do nothing, otherwise, free the already computed texture and
+ * recompute it with the given filter type and radius. */
+ void update(float2 radius, int type)
+ {
+ if (texture_ && type == type_ && radius == radius_) {
+ return;
+ }
+
+ if (texture_) {
+ GPU_texture_free(texture_);
+ }
+
+ /* The full size of filter is double the radius plus 1, but since the filter is symmetric, we
+ * only compute a single quadrant of it and so no doubling happens. We add 1 to make sure the
+ * filter size is always odd and there is a center weight. */
+ const float2 scale = math::safe_divide(float2(1.0f), radius);
+ const int2 size = int2(math::ceil(radius)) + int2(1);
+ Array<float> weights(size.x * size.y);
+
+ float sum = 0.0f;
+
+ /* First, compute the center weight. */
+ const float center_weight = RE_filter_value(type, 0.0f);
+ weights[0] = center_weight;
+ sum += center_weight;
+
+ /* Then, compute the weights along the positive x axis, making sure to add double the weight to
+ * the sum of weights because the filter is symmetric and we only loop over the positive half
+ * of the x axis. Skip the center weight already computed by dropping the front index. */
+ for (const int x : IndexRange(size.x).drop_front(1)) {
+ const float weight = RE_filter_value(type, x * scale.x);
+ weights[x] = weight;
+ sum += weight * 2.0f;
+ }
+
+ /* Then, compute the weights along the positive y axis, making sure to add double the weight to
+ * the sum of weights because the filter is symmetric and we only loop over the positive half
+ * of the y axis. Skip the center weight already computed by dropping the front index. */
+ for (const int y : IndexRange(size.y).drop_front(1)) {
+ const float weight = RE_filter_value(type, y * scale.y);
+ weights[size.x * y] = weight;
+ sum += weight * 2.0f;
+ }
+
+ /* Then, compute the other weights in the upper right quadrant, making sure to add quadruple
+ * the weight to the sum of weights because the filter is symmetric and we only loop over one
+ * quadrant of it. Skip the weights along the y and x axis already computed by dropping the
+ * front index. */
+ for (const int y : IndexRange(size.y).drop_front(1)) {
+ for (const int x : IndexRange(size.x).drop_front(1)) {
+ const float weight = RE_filter_value(type, math::length(float2(x, y) * scale));
+ weights[size.x * y + x] = weight;
+ sum += weight * 4.0f;
+ }
+ }
+
+ /* Finally, normalize the weights. */
+ for (const int y : IndexRange(size.y)) {
+ for (const int x : IndexRange(size.x)) {
+ weights[size.x * y + x] /= sum;
+ }
+ }
+
+ texture_ = GPU_texture_create_2d("Weights", size.x, size.y, 1, GPU_R16F, weights.data());
+
+ type_ = type;
+ radius_ = radius;
+ }
+
+ void bind_as_texture(GPUShader *shader, const char *texture_name)
+ {
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name);
+ GPU_texture_bind(texture_, texture_image_unit);
+ }
+
+ void unbind_as_texture()
+ {
+ GPU_texture_unbind(texture_);
+ }
+};
+
+class BlurOperation : public NodeOperation {
+ private:
+ /* Cached symmetric blur weights. */
+ SymmetricBlurWeights blur_weights_;
+ /* Cached symmetric blur weights for the separable horizontal pass. */
+ SymmetricSeparableBlurWeights blur_horizontal_weights_;
+ /* Cached symmetric blur weights for the separable vertical pass. */
+ SymmetricSeparableBlurWeights blur_vertical_weights_;
+
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ if (is_identity()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ if (use_separable_filter()) {
+ GPUTexture *horizontal_pass_result = execute_separable_blur_horizontal_pass();
+ execute_separable_blur_vertical_pass(horizontal_pass_result);
+ }
+ else {
+ execute_blur();
+ }
+ }
+
+ void execute_blur()
+ {
+ GPUShader *shader = shader_manager().get("compositor_symmetric_blur");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds());
+ GPU_shader_uniform_1b(shader, "gamma_correct", node_storage(bnode()).gamma);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ blur_weights_.update(compute_blur_radius(), node_storage(bnode()).filtertype);
+ blur_weights_.bind_as_texture(shader, "weights_tx");
+
+ Domain domain = compute_domain();
+ if (get_extend_bounds()) {
+ /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */
+ domain.size += int2(math::ceil(compute_blur_radius())) * 2;
+ }
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ input_image.unbind_as_texture();
+ blur_weights_.unbind_as_texture();
+ }
+
+ GPUTexture *execute_separable_blur_horizontal_pass()
+ {
+ GPUShader *shader = shader_manager().get("compositor_symmetric_separable_blur");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds());
+ GPU_shader_uniform_1b(shader, "gamma_correct_input", node_storage(bnode()).gamma);
+ GPU_shader_uniform_1b(shader, "gamma_uncorrect_output", false);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ blur_horizontal_weights_.update(compute_blur_radius().x, node_storage(bnode()).filtertype);
+ blur_horizontal_weights_.bind_as_texture(shader, "weights_tx");
+
+ Domain domain = compute_domain();
+ if (get_extend_bounds()) {
+ domain.size.x += static_cast<int>(math::ceil(compute_blur_radius().x)) * 2;
+ }
+
+ /* We allocate an output image of a transposed size, that is, with a height equivalent to the
+ * width of the input and vice versa. This is done as a performance optimization. The shader
+ * will blur the image horizontally and write it to the intermediate output transposed. Then
+ * the vertical pass will execute the same horizontal blur shader, but since its input is
+ * transposed, it will effectively do a vertical blur and write to the output transposed,
+ * effectively undoing the transposition in the horizontal pass. This is done to improve
+ * spatial cache locality in the shader and to avoid having two separate shaders for each blur
+ * pass. */
+ const int2 transposed_domain = int2(domain.size.y, domain.size.x);
+
+ GPUTexture *horizontal_pass_result = texture_pool().acquire_color(transposed_domain);
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(horizontal_pass_result, image_unit);
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ input_image.unbind_as_texture();
+ blur_horizontal_weights_.unbind_as_texture();
+ GPU_texture_image_unbind(horizontal_pass_result);
+
+ return horizontal_pass_result;
+ }
+
+ void execute_separable_blur_vertical_pass(GPUTexture *horizontal_pass_result)
+ {
+ GPUShader *shader = shader_manager().get("compositor_symmetric_separable_blur");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds());
+ GPU_shader_uniform_1b(shader, "gamma_correct_input", false);
+ GPU_shader_uniform_1b(shader, "gamma_uncorrect_output", node_storage(bnode()).gamma);
+
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(horizontal_pass_result, texture_image_unit);
+
+ blur_vertical_weights_.update(compute_blur_radius().y, node_storage(bnode()).filtertype);
+ blur_vertical_weights_.bind_as_texture(shader, "weights_tx");
+
+ Domain domain = compute_domain();
+ if (get_extend_bounds()) {
+ /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */
+ domain.size += int2(math::ceil(compute_blur_radius())) * 2;
+ }
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ /* Notice that the domain is transposed, see the note on the horizontal pass method for more
+ * information on the reasoning behind this. */
+ compute_dispatch_threads_at_least(shader, int2(domain.size.y, domain.size.x));
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ blur_vertical_weights_.unbind_as_texture();
+ GPU_texture_unbind(horizontal_pass_result);
+ }
+
+ float2 compute_blur_radius()
+ {
+ const float size = math::clamp(get_input("Size").get_float_value_default(1.0f), 0.0f, 1.0f);
+
+ if (!node_storage(bnode()).relative) {
+ return float2(node_storage(bnode()).sizex, node_storage(bnode()).sizey) * size;
+ }
+
+ int2 image_size = get_input("Image").domain().size;
+ switch (node_storage(bnode()).aspect) {
+ case CMP_NODE_BLUR_ASPECT_Y:
+ image_size.y = image_size.x;
+ break;
+ case CMP_NODE_BLUR_ASPECT_X:
+ image_size.x = image_size.y;
+ break;
+ default:
+ BLI_assert(node_storage(bnode()).aspect == CMP_NODE_BLUR_ASPECT_NONE);
+ break;
+ }
+
+ return float2(image_size) * get_size_factor() * size;
+ }
+
+ /* Returns true if the operation does nothing and the input can be passed through. */
+ bool is_identity()
+ {
+ const Result &input = get_input("Image");
+ /* Single value inputs can't be blurred and are returned as is. */
+ if (input.is_single_value()) {
+ return true;
+ }
+
+ /* Zero blur radius. The operation does nothing and the input can be passed through. */
+ if (compute_blur_radius() == float2(0.0)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /* The blur node can operate with different filter types, evaluated on the normalized distance to
+ * the center of the filter. Some of those filters are separable and can be computed as such. If
+ * the bokeh member is disabled in the node, then the filter is always computed as separable even
+ * if it is not in fact separable, in which case, the used filter is a cheaper approximation to
+ * the actual filter. If the bokeh member is enabled, then the filter is computed as separable if
+ * it is in fact separable and as a normal 2D filter otherwise. */
+ bool use_separable_filter()
+ {
+ if (!node_storage(bnode()).bokeh) {
+ return true;
+ }
+
+ /* Both Box and Gaussian filters are separable. The rest is not. */
+ switch (node_storage(bnode()).filtertype) {
+ case R_FILTER_BOX:
+ case R_FILTER_GAUSS:
+ case R_FILTER_FAST_GAUSS:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ float2 get_size_factor()
+ {
+ return float2(node_storage(bnode()).percentx, node_storage(bnode()).percenty) / 100.0f;
+ }
+
+ bool get_extend_bounds()
+ {
+ return bnode().custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_blur_cc
void register_node_type_cmp_blur()
@@ -86,6 +504,7 @@ void register_node_type_cmp_blur()
node_type_init(&ntype, file_ns::node_composit_init_blur);
node_type_storage(
&ntype, "NodeBlurData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
index a936bafe671..9c0617ee8c3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
@@ -5,9 +5,17 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.hh"
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** BLUR ******************** */
@@ -16,10 +24,22 @@ namespace blender::nodes::node_composite_bokehblur_cc {
static void cmp_node_bokehblur_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_input<decl::Color>(N_("Bokeh")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Size")).default_value(1.0f).min(0.0f).max(10.0f);
- b.add_input<decl::Float>(N_("Bounding box")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({0.8f, 0.8f, 0.8f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Bokeh"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_skip_realization();
+ b.add_input<decl::Float>(N_("Size"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(10.0f)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("Bounding box"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(2);
b.add_output<decl::Color>(N_("Image"));
}
@@ -37,6 +57,98 @@ static void node_composit_buts_bokehblur(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "use_extended_bounds", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BokehBlurOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ if (is_identity()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_blur");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1i(shader, "radius", compute_blur_radius());
+ GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds());
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Result &input_weights = get_input("Bokeh");
+ input_weights.bind_as_texture(shader, "weights_tx");
+
+ const Result &input_mask = get_input("Bounding box");
+ input_mask.bind_as_texture(shader, "mask_tx");
+
+ Domain domain = compute_domain();
+ if (get_extend_bounds()) {
+ /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */
+ domain.size += int2(compute_blur_radius() * 2);
+ }
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ input_image.unbind_as_texture();
+ input_weights.unbind_as_texture();
+ input_mask.unbind_as_texture();
+ }
+
+ int compute_blur_radius()
+ {
+ const int2 image_size = get_input("Image").domain().size;
+ const int max_size = math::max(image_size.x, image_size.y);
+
+ /* The [0, 10] range of the size is arbitrary and is merely in place to avoid very long
+ * computations of the bokeh blur. */
+ const float size = math::clamp(get_input("Size").get_float_value_default(1.0f), 0.0f, 10.0f);
+
+ /* The 100 divisor is arbitrary and was chosen using visual judgment. */
+ return size * (max_size / 100.0f);
+ }
+
+ bool is_identity()
+ {
+ const Result &input = get_input("Image");
+ if (input.is_single_value()) {
+ return true;
+ }
+
+ if (compute_blur_radius() == 0) {
+ return true;
+ }
+
+ /* This input is, in fact, a boolean mask. If it is zero, no blurring will take place.
+ * Otherwise, the blurring will take place ignoring the value of the input entirely. */
+ const Result &bounding_box = get_input("Bounding box");
+ if (bounding_box.is_single_value() && bounding_box.get_float_value() == 0.0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ bool get_extend_bounds()
+ {
+ return bnode().custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BokehBlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_bokehblur_cc
void register_node_type_cmp_bokehblur()
@@ -49,6 +161,7 @@ void register_node_type_cmp_bokehblur()
ntype.declare = file_ns::cmp_node_bokehblur_declare;
ntype.draw_buttons = file_ns::node_composit_buts_bokehblur;
node_type_init(&ntype, file_ns::node_composit_init_bokehblur);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
index 8330c56736a..81cc8990d35 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
@@ -5,15 +5,25 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.h"
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** Bokeh image Tools ******************** */
namespace blender::nodes::node_composite_bokehimage_cc {
+NODE_STORAGE_FUNCS(NodeBokehImage)
+
static void cmp_node_bokehimage_declare(NodeDeclarationBuilder &b)
{
b.add_output<decl::Color>(N_("Image"));
@@ -45,6 +55,61 @@ static void node_composit_buts_bokehimage(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "shift", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BokehImageOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUShader *shader = shader_manager().get("compositor_bokeh_image");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1f(shader, "exterior_angle", get_exterior_angle());
+ GPU_shader_uniform_1f(shader, "rotation", get_rotation());
+ GPU_shader_uniform_1f(shader, "roundness", node_storage(bnode()).rounding);
+ GPU_shader_uniform_1f(shader, "catadioptric", node_storage(bnode()).catadioptric);
+ GPU_shader_uniform_1f(shader, "lens_shift", node_storage(bnode()).lensshift);
+
+ Result &output = get_result("Image");
+ const Domain domain = compute_domain();
+ output.allocate_texture(domain);
+ output.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ output.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ Domain compute_domain() override
+ {
+ return Domain(int2(512));
+ }
+
+ /* The exterior angle is the angle between each two consecutive vertices of the regular polygon
+ * from its center. */
+ float get_exterior_angle()
+ {
+ return (M_PI * 2.0f) / node_storage(bnode()).flaps;
+ }
+
+ float get_rotation()
+ {
+ /* Offset the rotation such that the second vertex of the regular polygon lies on the positive
+ * y axis, which is 90 degrees minus the angle that it makes with the positive x axis assuming
+ * the first vertex lies on the positive x axis. */
+ const float offset = M_PI_2 - get_exterior_angle();
+ return node_storage(bnode()).angle - offset;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BokehImageOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_bokehimage_cc
void register_node_type_cmp_bokehimage()
@@ -60,6 +125,7 @@ void register_node_type_cmp_bokehimage()
node_type_init(&ntype, file_ns::node_composit_init_bokehimage);
node_type_storage(
&ntype, "NodeBokehImage", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc
index f39b69c63f2..3cf0932e1b3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc
@@ -5,15 +5,26 @@
* \ingroup cmpnodes
*/
+#include <cmath>
+
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** SCALAR MATH ******************** */
namespace blender::nodes::node_composite_boxmask_cc {
+NODE_STORAGE_FUNCS(NodeBoxMask)
+
static void cmp_node_boxmask_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f);
@@ -48,6 +59,93 @@ static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "mask_type", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BoxMaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUShader *shader = shader_manager().get(get_shader_name());
+ GPU_shader_bind(shader);
+
+ const Domain domain = compute_domain();
+
+ GPU_shader_uniform_2iv(shader, "domain_size", domain.size);
+
+ GPU_shader_uniform_2fv(shader, "location", get_location());
+ GPU_shader_uniform_2fv(shader, "size", get_size() / 2.0f);
+ GPU_shader_uniform_1f(shader, "cos_angle", std::cos(get_angle()));
+ GPU_shader_uniform_1f(shader, "sin_angle", std::sin(get_angle()));
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "base_mask_tx");
+
+ const Result &value = get_input("Value");
+ value.bind_as_texture(shader, "mask_value_tx");
+
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_mask_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_mask.unbind_as_texture();
+ value.unbind_as_texture();
+ output_mask.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ Domain compute_domain() override
+ {
+ if (get_input("Mask").is_single_value()) {
+ return Domain(context().get_output_size());
+ }
+ return get_input("Mask").domain();
+ }
+
+ CMPNodeMaskType get_mask_type()
+ {
+ return (CMPNodeMaskType)bnode().custom1;
+ }
+
+ const char *get_shader_name()
+ {
+ switch (get_mask_type()) {
+ default:
+ case CMP_NODE_MASKTYPE_ADD:
+ return "compositor_box_mask_add";
+ case CMP_NODE_MASKTYPE_SUBTRACT:
+ return "compositor_box_mask_subtract";
+ case CMP_NODE_MASKTYPE_MULTIPLY:
+ return "compositor_box_mask_multiply";
+ case CMP_NODE_MASKTYPE_NOT:
+ return "compositor_box_mask_not";
+ }
+ }
+
+ float2 get_location()
+ {
+ return float2(node_storage(bnode()).x, node_storage(bnode()).y);
+ }
+
+ float2 get_size()
+ {
+ return float2(node_storage(bnode()).width, node_storage(bnode()).height);
+ }
+
+ float get_angle()
+ {
+ return node_storage(bnode()).rotation;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BoxMaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_boxmask_cc
void register_node_type_cmp_boxmask()
@@ -61,6 +159,7 @@ void register_node_type_cmp_boxmask()
ntype.draw_buttons = file_ns::node_composit_buts_boxmask;
node_type_init(&ntype, file_ns::node_composit_init_boxmask);
node_type_storage(&ntype, "NodeBoxMask", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_brightness.cc b/source/blender/nodes/composite/nodes/node_composite_brightness.cc
index 65ed2885d9b..fa22f551de6 100644
--- a/source/blender/nodes/composite/nodes/node_composite_brightness.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_brightness.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Bright and Contrast ******************** */
@@ -16,9 +20,11 @@ namespace blender::nodes::node_composite_brightness_cc {
static void cmp_node_brightcontrast_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Bright")).min(-100.0f).max(100.0f);
- b.add_input<decl::Float>(N_("Contrast")).min(-100.0f).max(100.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Bright")).min(-100.0f).max(100.0f).compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("Contrast")).min(-100.0f).max(100.0f).compositor_domain_priority(2);
b.add_output<decl::Color>(N_("Image"));
}
@@ -34,6 +40,38 @@ static void node_composit_buts_brightcontrast(uiLayout *layout,
uiItemR(layout, ptr, "use_premultiply", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BrightContrastShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float use_premultiply = get_use_premultiply();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_bright_contrast",
+ inputs,
+ outputs,
+ GPU_constant(&use_premultiply));
+ }
+
+ bool get_use_premultiply()
+ {
+ return bnode().custom1;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new BrightContrastShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_brightness_cc
void register_node_type_cmp_brightcontrast()
@@ -46,6 +84,7 @@ void register_node_type_cmp_brightcontrast()
ntype.declare = file_ns::cmp_node_brightcontrast_declare;
ntype.draw_buttons = file_ns::node_composit_buts_brightcontrast;
node_type_init(&ntype, file_ns::node_composit_init_brightcontrast);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc
index 627f07fdfce..3b825017da8 100644
--- a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc
@@ -10,15 +10,23 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Channel Matte Node ********************************* */
namespace blender::nodes::node_composite_channel_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_channel_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -79,6 +87,91 @@ static void node_composit_buts_channel_matte(uiLayout *layout,
col, ptr, "limit_min", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ChannelMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float color_space = get_color_space();
+ const float matte_channel = get_matte_channel();
+ float limit_channels[2];
+ get_limit_channels(limit_channels);
+ const float max_limit = get_max_limit();
+ const float min_limit = get_min_limit();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_channel_matte",
+ inputs,
+ outputs,
+ GPU_constant(&color_space),
+ GPU_constant(&matte_channel),
+ GPU_constant(limit_channels),
+ GPU_uniform(&max_limit),
+ GPU_uniform(&min_limit));
+ }
+
+ /* 1 -> CMP_NODE_CHANNEL_MATTE_CS_RGB
+ * 2 -> CMP_NODE_CHANNEL_MATTE_CS_HSV
+ * 3 -> CMP_NODE_CHANNEL_MATTE_CS_YUV
+ * 4 -> CMP_NODE_CHANNEL_MATTE_CS_YCC */
+ int get_color_space()
+ {
+ return bnode().custom1;
+ }
+
+ /* Get the index of the channel used to generate the matte. */
+ int get_matte_channel()
+ {
+ return bnode().custom2 - 1;
+ }
+
+ /* Get the index of the channel used to compute the limit value. */
+ int get_limit_channel()
+ {
+ return node_storage(bnode()).channel - 1;
+ }
+
+ /* Get the indices of the channels used to compute the limit value. We always assume the limit
+ * algorithm is Max, if it is a single limit channel, store it in both limit channels, because
+ * the maximum of two identical values is the same value. */
+ void get_limit_channels(float limit_channels[2])
+ {
+ if (node_storage(bnode()).algorithm == CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX) {
+ /* If the algorithm is Max, store the indices of the other two channels other than the matte
+ * channel. */
+ limit_channels[0] = (get_matte_channel() + 1) % 3;
+ limit_channels[1] = (get_matte_channel() + 2) % 3;
+ }
+ else {
+ /* If the algorithm is Single, store the index of the limit channel in both channels. */
+ limit_channels[0] = get_limit_channel();
+ limit_channels[1] = get_limit_channel();
+ }
+ }
+
+ float get_max_limit()
+ {
+ return node_storage(bnode()).t1;
+ }
+
+ float get_min_limit()
+ {
+ return node_storage(bnode()).t2;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ChannelMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_channel_matte_cc
void register_node_type_cmp_channel_matte()
@@ -93,6 +186,7 @@ void register_node_type_cmp_channel_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_channel_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc
index 69319c6825d..e5ce87169d4 100644
--- a/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc
@@ -5,21 +5,33 @@
* \ingroup cmpnodes
*/
+#include <cmath>
+
#include "BLI_math_rotation.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Chroma Key ********************************************************** */
namespace blender::nodes::node_composite_chroma_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_chroma_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Key Color"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -51,6 +63,52 @@ static void node_composit_buts_chroma_matte(uiLayout *layout, bContext *UNUSED(C
// uiItemR(col, ptr, "shadow_adjust", UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ChromaMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float acceptance = get_acceptance();
+ const float cutoff = get_cutoff();
+ const float falloff = get_falloff();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_chroma_matte",
+ inputs,
+ outputs,
+ GPU_uniform(&acceptance),
+ GPU_uniform(&cutoff),
+ GPU_uniform(&falloff));
+ }
+
+ float get_acceptance()
+ {
+ return std::tan(node_storage(bnode()).t1) / 2.0f;
+ }
+
+ float get_cutoff()
+ {
+ return node_storage(bnode()).t2;
+ }
+
+ float get_falloff()
+ {
+ return node_storage(bnode()).fstrength;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ChromaMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_chroma_matte_cc
void register_node_type_cmp_chroma_matte()
@@ -65,6 +123,7 @@ void register_node_type_cmp_chroma_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_chroma_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_color_matte.cc b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc
index 474fb1b72f2..08329601f14 100644
--- a/source/blender/nodes/composite/nodes/node_composite_color_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc
@@ -8,16 +8,26 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Color Matte ********************************************************** */
namespace blender::nodes::node_composite_color_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_color_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Key Color"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -50,6 +60,53 @@ static void node_composit_buts_color_matte(uiLayout *layout, bContext *UNUSED(C)
col, ptr, "color_value", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ColorMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float hue_epsilon = get_hue_epsilon();
+ const float saturation_epsilon = get_saturation_epsilon();
+ const float value_epsilon = get_value_epsilon();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_matte",
+ inputs,
+ outputs,
+ GPU_uniform(&hue_epsilon),
+ GPU_uniform(&saturation_epsilon),
+ GPU_uniform(&value_epsilon));
+ }
+
+ float get_hue_epsilon()
+ {
+ /* Divide by 2 because the hue wraps around. */
+ return node_storage(bnode()).t1 / 2.0f;
+ }
+
+ float get_saturation_epsilon()
+ {
+ return node_storage(bnode()).t2;
+ }
+
+ float get_value_epsilon()
+ {
+ return node_storage(bnode()).t3;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_color_matte_cc
void register_node_type_cmp_color_matte()
@@ -64,6 +121,7 @@ void register_node_type_cmp_color_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_color_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_color_spill.cc b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc
index 9ad5dfbaeb2..29401d7b20f 100644
--- a/source/blender/nodes/composite/nodes/node_composite_color_spill.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc
@@ -10,16 +10,29 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Color Spill Suppression ********************************* */
namespace blender::nodes::node_composite_color_spill_cc {
+NODE_STORAGE_FUNCS(NodeColorspill)
+
static void cmp_node_color_spill_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -27,8 +40,8 @@ static void node_composit_init_color_spill(bNodeTree *UNUSED(ntree), bNode *node
{
NodeColorspill *ncs = MEM_cnew<NodeColorspill>(__func__);
node->storage = ncs;
+ node->custom2 = CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_SINGLE;
node->custom1 = 2; /* green channel */
- node->custom2 = 0; /* simple limit algorithm */
ncs->limchan = 0; /* limit by red */
ncs->limscale = 1.0f; /* limit scaling factor */
ncs->unspill = 0; /* do not use unspill */
@@ -80,6 +93,98 @@ static void node_composit_buts_color_spill(uiLayout *layout, bContext *UNUSED(C)
}
}
+using namespace blender::realtime_compositor;
+
+class ColorSpillShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float spill_channel = get_spill_channel();
+ float spill_scale[3];
+ get_spill_scale(spill_scale);
+ float limit_channels[2];
+ get_limit_channels(limit_channels);
+ const float limit_scale = get_limit_scale();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_spill",
+ inputs,
+ outputs,
+ GPU_constant(&spill_channel),
+ GPU_uniform(spill_scale),
+ GPU_constant(limit_channels),
+ GPU_uniform(&limit_scale));
+ }
+
+ /* Get the index of the channel used for spilling. */
+ int get_spill_channel()
+ {
+ return bnode().custom1 - 1;
+ }
+
+ CMPNodeColorSpillLimitAlgorithm get_limit_algorithm()
+ {
+ return (CMPNodeColorSpillLimitAlgorithm)bnode().custom2;
+ }
+
+ void get_spill_scale(float spill_scale[3])
+ {
+ const NodeColorspill &node_color_spill = node_storage(bnode());
+ if (node_color_spill.unspill) {
+ spill_scale[0] = node_color_spill.uspillr;
+ spill_scale[1] = node_color_spill.uspillg;
+ spill_scale[2] = node_color_spill.uspillb;
+ spill_scale[get_spill_channel()] *= -1.0f;
+ }
+ else {
+ spill_scale[0] = 0.0f;
+ spill_scale[1] = 0.0f;
+ spill_scale[2] = 0.0f;
+ spill_scale[get_spill_channel()] = -1.0f;
+ }
+ }
+
+ /* Get the index of the channel used for limiting. */
+ int get_limit_channel()
+ {
+ return node_storage(bnode()).limchan;
+ }
+
+ /* Get the indices of the channels used to compute the limit value. We always assume the limit
+ * algorithm is Average, if it is a single limit channel, store it in both limit channels,
+ * because the average of two identical values is the same value. */
+ void get_limit_channels(float limit_channels[2])
+ {
+ if (get_limit_algorithm() == CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_AVERAGE) {
+ /* If the algorithm is Average, store the indices of the other two channels other than the
+ * spill channel. */
+ limit_channels[0] = (get_spill_channel() + 1) % 3;
+ limit_channels[1] = (get_spill_channel() + 2) % 3;
+ }
+ else {
+ /* If the algorithm is Single, store the index of the limit channel in both channels. */
+ limit_channels[0] = get_limit_channel();
+ limit_channels[1] = get_limit_channel();
+ }
+ }
+
+ float get_limit_scale()
+ {
+ return node_storage(bnode()).limscale;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorSpillShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_color_spill_cc
void register_node_type_cmp_color_spill()
@@ -94,6 +199,7 @@ void register_node_type_cmp_color_spill()
node_type_init(&ntype, file_ns::node_composit_init_color_spill);
node_type_storage(
&ntype, "NodeColorspill", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
index dd081c8fc12..e05fbf00a25 100644
--- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
@@ -10,6 +10,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Color Balance ********************************* */
@@ -44,10 +48,19 @@ void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *UNUSED(ntree), bNode *node)
namespace blender::nodes::node_composite_colorbalance_cc {
+NODE_STORAGE_FUNCS(NodeColorBalance)
+
static void cmp_node_colorbalance_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -71,7 +84,7 @@ static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C
uiItemR(layout, ptr, "correction_method", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
- if (RNA_enum_get(ptr, "correction_method") == 0) {
+ if (RNA_enum_get(ptr, "correction_method") == CMP_NODE_COLOR_BALANCE_LGG) {
split = uiLayoutSplit(layout, 0.0f, false);
col = uiLayoutColumn(split, false);
@@ -116,7 +129,7 @@ static void node_composit_buts_colorbalance_ex(uiLayout *layout,
{
uiItemR(layout, ptr, "correction_method", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
- if (RNA_enum_get(ptr, "correction_method") == 0) {
+ if (RNA_enum_get(ptr, "correction_method") == CMP_NODE_COLOR_BALANCE_LGG) {
uiTemplateColorPicker(layout, ptr, "lift", true, true, false, true);
uiItemR(layout, ptr, "lift", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
@@ -139,6 +152,53 @@ static void node_composit_buts_colorbalance_ex(uiLayout *layout,
}
}
+using namespace blender::realtime_compositor;
+
+class ColorBalanceShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const NodeColorBalance &node_color_balance = node_storage(bnode());
+
+ if (get_color_balance_method() == CMP_NODE_COLOR_BALANCE_LGG) {
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_balance_lgg",
+ inputs,
+ outputs,
+ GPU_uniform(node_color_balance.lift),
+ GPU_uniform(node_color_balance.gamma),
+ GPU_uniform(node_color_balance.gain));
+ return;
+ }
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_balance_asc_cdl",
+ inputs,
+ outputs,
+ GPU_uniform(node_color_balance.offset),
+ GPU_uniform(node_color_balance.power),
+ GPU_uniform(node_color_balance.slope),
+ GPU_uniform(&node_color_balance.offset_basis));
+ }
+
+ CMPNodeColorBalanceMethod get_color_balance_method()
+ {
+ return (CMPNodeColorBalanceMethod)bnode().custom1;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorBalanceShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_colorbalance_cc
void register_node_type_cmp_colorbalance()
@@ -155,6 +215,7 @@ void register_node_type_cmp_colorbalance()
node_type_init(&ntype, file_ns::node_composit_init_colorbalance);
node_type_storage(
&ntype, "NodeColorBalance", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
index 39ecd277cec..92b10fc1877 100644
--- a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
@@ -5,19 +5,33 @@
* \ingroup cmpnodes
*/
+#include "IMB_colormanagement.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Color Correction ********************************* */
namespace blender::nodes::node_composite_colorcorrection_cc {
+NODE_STORAGE_FUNCS(NodeColorCorrection)
+
static void cmp_node_colorcorrection_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Mask")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Mask"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -266,6 +280,68 @@ static void node_composit_buts_colorcorrection_ex(uiLayout *layout,
uiItemR(row, ptr, "midtones_end", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ColorCorrectionShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ float enabled_channels[3];
+ get_enabled_channels(enabled_channels);
+ float luminance_coefficients[3];
+ IMB_colormanagement_get_luminance_coefficients(luminance_coefficients);
+
+ const NodeColorCorrection &node_color_correction = node_storage(bnode());
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_correction",
+ inputs,
+ outputs,
+ GPU_constant(enabled_channels),
+ GPU_uniform(&node_color_correction.startmidtones),
+ GPU_uniform(&node_color_correction.endmidtones),
+ GPU_uniform(&node_color_correction.master.saturation),
+ GPU_uniform(&node_color_correction.master.contrast),
+ GPU_uniform(&node_color_correction.master.gamma),
+ GPU_uniform(&node_color_correction.master.gain),
+ GPU_uniform(&node_color_correction.master.lift),
+ GPU_uniform(&node_color_correction.shadows.saturation),
+ GPU_uniform(&node_color_correction.shadows.contrast),
+ GPU_uniform(&node_color_correction.shadows.gamma),
+ GPU_uniform(&node_color_correction.shadows.gain),
+ GPU_uniform(&node_color_correction.shadows.lift),
+ GPU_uniform(&node_color_correction.midtones.saturation),
+ GPU_uniform(&node_color_correction.midtones.contrast),
+ GPU_uniform(&node_color_correction.midtones.gamma),
+ GPU_uniform(&node_color_correction.midtones.gain),
+ GPU_uniform(&node_color_correction.midtones.lift),
+ GPU_uniform(&node_color_correction.highlights.saturation),
+ GPU_uniform(&node_color_correction.highlights.contrast),
+ GPU_uniform(&node_color_correction.highlights.gamma),
+ GPU_uniform(&node_color_correction.highlights.gain),
+ GPU_uniform(&node_color_correction.highlights.lift),
+ GPU_constant(luminance_coefficients));
+ }
+
+ void get_enabled_channels(float enabled_channels[3])
+ {
+ for (int i = 0; i < 3; i++) {
+ enabled_channels[i] = (bnode().custom1 & (1 << i)) ? 1.0f : 0.0f;
+ }
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorCorrectionShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_colorcorrection_cc
void register_node_type_cmp_colorcorrection()
@@ -282,6 +358,7 @@ void register_node_type_cmp_colorcorrection()
node_type_init(&ntype, file_ns::node_composit_init_colorcorrection);
node_type_storage(
&ntype, "NodeColorCorrection", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_composite.cc b/source/blender/nodes/composite/nodes/node_composite_composite.cc
index d35ce7dc11a..68061bb434d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_composite.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_composite.cc
@@ -5,9 +5,18 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** COMPOSITE ******************** */
@@ -26,6 +35,125 @@ static void node_composit_buts_composite(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "use_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class CompositeOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ const Result &image = get_input("Image");
+ const Result &alpha = get_input("Alpha");
+
+ if (image.is_single_value() && alpha.is_single_value()) {
+ execute_clear();
+ }
+ else if (ignore_alpha()) {
+ execute_ignore_alpha();
+ }
+ else if (!node().input_by_identifier("Alpha")->is_logically_linked()) {
+ execute_copy();
+ }
+ else {
+ execute_set_alpha();
+ }
+ }
+
+ /* Executes when all inputs are single values, in which case, the output texture can just be
+ * cleared to the appropriate color. */
+ void execute_clear()
+ {
+ const Result &image = get_input("Image");
+ const Result &alpha = get_input("Alpha");
+
+ float4 color = image.get_color_value();
+ if (ignore_alpha()) {
+ color.w = 1.0f;
+ }
+ else if (node().input_by_identifier("Alpha")->is_logically_linked()) {
+ color.w = alpha.get_float_value();
+ }
+
+ GPU_texture_clear(context().get_output_texture(), GPU_DATA_FLOAT, color);
+ }
+
+ /* Executes when the alpha channel of the image is ignored. */
+ void execute_ignore_alpha()
+ {
+ GPUShader *shader = shader_manager().get("compositor_convert_color_to_opaque");
+ GPU_shader_bind(shader);
+
+ const Result &image = get_input("Image");
+ image.bind_as_texture(shader, "input_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, compute_domain().size);
+
+ image.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* Executes when the image texture is written with no adjustments and can thus be copied directly
+ * to the output texture. */
+ void execute_copy()
+ {
+ const Result &image = get_input("Image");
+
+ /* Make sure any prior writes to the texture are reflected before copying it. */
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
+
+ GPU_texture_copy(context().get_output_texture(), image.texture());
+ }
+
+ /* Executes when the alpha channel of the image is set as the value of the input alpha. */
+ void execute_set_alpha()
+ {
+ GPUShader *shader = shader_manager().get("compositor_set_alpha");
+ GPU_shader_bind(shader);
+
+ const Result &image = get_input("Image");
+ image.bind_as_texture(shader, "image_tx");
+
+ const Result &alpha = get_input("Alpha");
+ alpha.bind_as_texture(shader, "alpha_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, compute_domain().size);
+
+ image.unbind_as_texture();
+ alpha.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* If true, the alpha channel of the image is set to 1, that is, it becomes opaque. If false, the
+ * alpha channel of the image is retained, but only if the alpha input is not linked. If the
+ * alpha input is linked, it the value of that input will be used as the alpha of the image. */
+ bool ignore_alpha()
+ {
+ return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA;
+ }
+
+ /* The operation domain have the same dimensions of the output without any transformations. */
+ Domain compute_domain() override
+ {
+ return Domain(context().get_output_size());
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CompositeOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_composite_cc
void register_node_type_cmp_composite()
@@ -37,6 +165,7 @@ void register_node_type_cmp_composite()
cmp_node_type_base(&ntype, CMP_NODE_COMPOSITE, "Composite", NODE_CLASS_OUTPUT);
ntype.declare = file_ns::cmp_node_composite_declare;
ntype.draw_buttons = file_ns::node_composit_buts_composite;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.flag |= NODE_PREVIEW;
ntype.no_muting = true;
diff --git a/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc b/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc
index 303248c3852..e36da39cca1 100644
--- a/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc
@@ -14,6 +14,8 @@
#include "IMB_colormanagement.h"
+#include "COM_node_operation.hh"
+
namespace blender::nodes::node_composite_convert_color_space_cc {
static void CMP_NODE_CONVERT_COLOR_SPACE_declare(NodeDeclarationBuilder &b)
@@ -47,6 +49,23 @@ static void node_composit_buts_convert_colorspace(uiLayout *layout,
uiItemR(layout, ptr, "to_color_space", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ConvertColorSpaceOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ConvertColorSpaceOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_convert_color_space_cc
void register_node_type_cmp_convert_color_space(void)
@@ -62,6 +81,7 @@ void register_node_type_cmp_convert_color_space(void)
node_type_init(&ntype, file_ns::node_composit_init_convert_colorspace);
node_type_storage(
&ntype, "NodeConvertColorSpace", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc
index 07da0da0be1..9679701a7cf 100644
--- a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_cornerpin_cc {
@@ -32,6 +34,24 @@ static void cmp_node_cornerpin_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Plane"));
}
+using namespace blender::realtime_compositor;
+
+class CornerPinOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Plane").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CornerPinOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_cornerpin_cc
void register_node_type_cmp_cornerpin()
@@ -42,6 +62,7 @@ void register_node_type_cmp_cornerpin()
cmp_node_type_base(&ntype, CMP_NODE_CORNERPIN, "Corner Pin", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_cornerpin_declare;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_crop.cc b/source/blender/nodes/composite/nodes/node_composite_crop.cc
index 823e1052dd0..13d02a707be 100644
--- a/source/blender/nodes/composite/nodes/node_composite_crop.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_crop.cc
@@ -5,20 +5,35 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.h"
+#include "BLI_math_vec_types.hh"
+
+#include "DNA_node_types.h"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** Crop ******************** */
namespace blender::nodes::node_composite_crop_cc {
+NODE_STORAGE_FUNCS(NodeTwoXYs)
+
static void cmp_node_crop_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -54,6 +69,156 @@ static void node_composit_buts_crop(uiLayout *layout, bContext *UNUSED(C), Point
}
}
+using namespace blender::realtime_compositor;
+
+class CropOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ /* The operation does nothing, so just pass the input through. */
+ if (is_identity()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ if (get_is_image_crop()) {
+ execute_image_crop();
+ }
+ else {
+ execute_alpha_crop();
+ }
+ }
+
+ /* Crop by replacing areas outside of the cropping bounds with zero alpha. The output have the
+ * same domain as the input image. */
+ void execute_alpha_crop()
+ {
+ GPUShader *shader = shader_manager().get("compositor_alpha_crop");
+ GPU_shader_bind(shader);
+
+ int2 lower_bound, upper_bound;
+ compute_cropping_bounds(lower_bound, upper_bound);
+ GPU_shader_uniform_2iv(shader, "lower_bound", lower_bound);
+ GPU_shader_uniform_2iv(shader, "upper_bound", upper_bound);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Domain domain = compute_domain();
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_image.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ /* Crop the image into a new size that matches the cropping bounds. */
+ void execute_image_crop()
+ {
+ int2 lower_bound, upper_bound;
+ compute_cropping_bounds(lower_bound, upper_bound);
+
+ /* The image is cropped into nothing, so just return a single zero value. */
+ if (lower_bound.x == upper_bound.x || lower_bound.y == upper_bound.y) {
+ Result &result = get_result("Image");
+ result.allocate_invalid();
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_image_crop");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_2iv(shader, "lower_bound", lower_bound);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const int2 size = upper_bound - lower_bound;
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(Domain(size, compute_domain().transformation));
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ input_image.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ /* If true, the image should actually be cropped into a new size. Otherwise, if false, the region
+ * outside of the cropping bounds will be set to a zero alpha value. */
+ bool get_is_image_crop()
+ {
+ return bnode().custom1;
+ }
+
+ bool get_is_relative()
+ {
+ return bnode().custom2;
+ }
+
+ /* Returns true if the operation does nothing and the input can be passed through. */
+ bool is_identity()
+ {
+ const Result &input = get_input("Image");
+ /* Single value inputs can't be cropped and are returned as is. */
+ if (input.is_single_value()) {
+ return true;
+ }
+
+ int2 lower_bound, upper_bound;
+ compute_cropping_bounds(lower_bound, upper_bound);
+ const int2 input_size = input.domain().size;
+ /* The cropping bounds cover the whole image, so no cropping happens. */
+ if (lower_bound == int2(0) && upper_bound == input_size) {
+ return true;
+ }
+
+ return false;
+ }
+
+ void compute_cropping_bounds(int2 &lower_bound, int2 &upper_bound)
+ {
+ const NodeTwoXYs &node_two_xys = node_storage(bnode());
+ const int2 input_size = get_input("Image").domain().size;
+
+ if (get_is_relative()) {
+ /* The cropping bounds are relative to the image size. The factors are in the [0, 1] range,
+ * so it is guaranteed that they won't go over the input image size. */
+ lower_bound.x = input_size.x * node_two_xys.fac_x1;
+ lower_bound.y = input_size.y * node_two_xys.fac_y2;
+ upper_bound.x = input_size.x * node_two_xys.fac_x2;
+ upper_bound.y = input_size.y * node_two_xys.fac_y1;
+ }
+ else {
+ /* Make sure the bounds don't go over the input image size. */
+ lower_bound.x = min_ii(node_two_xys.x1, input_size.x);
+ lower_bound.y = min_ii(node_two_xys.y2, input_size.y);
+ upper_bound.x = min_ii(node_two_xys.x2, input_size.x);
+ upper_bound.y = min_ii(node_two_xys.y1, input_size.y);
+ }
+
+ /* Make sure upper bound is actually higher than the lower bound. */
+ lower_bound.x = min_ii(lower_bound.x, upper_bound.x);
+ lower_bound.y = min_ii(lower_bound.y, upper_bound.y);
+ upper_bound.x = max_ii(lower_bound.x, upper_bound.x);
+ upper_bound.y = max_ii(lower_bound.y, upper_bound.y);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CropOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_crop_cc
void register_node_type_cmp_crop()
@@ -67,6 +232,7 @@ void register_node_type_cmp_crop()
ntype.draw_buttons = file_ns::node_composit_buts_crop;
node_type_init(&ntype, file_ns::node_composit_init_crop);
node_type_storage(&ntype, "NodeTwoXYs", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
index 9193f91087a..7e5544381a4 100644
--- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
@@ -17,12 +17,17 @@
#include "BKE_context.h"
#include "BKE_cryptomatte.hh"
#include "BKE_global.h"
+#include "BKE_image.h"
#include "BKE_lib_id.h"
#include "BKE_library.h"
#include "BKE_main.h"
#include "MEM_guardedalloc.h"
+#include "RE_pipeline.h"
+
+#include "COM_node_operation.hh"
+
#include <optional>
/* -------------------------------------------------------------------- */
@@ -102,7 +107,6 @@ static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_no
return session;
}
-extern "C" {
static CryptomatteEntry *cryptomatte_find(const NodeCryptomatte &n, float encoded_hash)
{
LISTBASE_FOREACH (CryptomatteEntry *, entry, &n.entries) {
@@ -296,6 +300,25 @@ static bool node_poll_cryptomatte(bNodeType *UNUSED(ntype),
return false;
}
+using namespace blender::realtime_compositor;
+
+class CryptoMatteOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Matte").allocate_invalid();
+ get_result("Pick").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CryptoMatteOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_cryptomatte_cc
void register_node_type_cmp_cryptomatte()
@@ -313,6 +336,8 @@ void register_node_type_cmp_cryptomatte()
ntype.poll = file_ns::node_poll_cryptomatte;
node_type_storage(
&ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
+
nodeRegisterType(&ntype);
}
@@ -347,7 +372,7 @@ int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node)
return 1;
}
-namespace blender::nodes::node_composite_cryptomatte_cc {
+namespace blender::nodes::node_composite_legacy_cryptomatte_cc {
static void node_init_cryptomatte_legacy(bNodeTree *ntree, bNode *node)
{
@@ -362,22 +387,43 @@ static void node_init_cryptomatte_legacy(bNodeTree *ntree, bNode *node)
ntreeCompositCryptomatteAddSocket(ntree, node);
}
-} // namespace blender::nodes::node_composite_cryptomatte_cc
+using namespace blender::realtime_compositor;
+
+class CryptoMatteOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("image").pass_through(get_result("Image"));
+ get_result("Matte").allocate_invalid();
+ get_result("Pick").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CryptoMatteOperation(context, node);
+}
+
+} // namespace blender::nodes::node_composite_legacy_cryptomatte_cc
void register_node_type_cmp_cryptomatte_legacy()
{
- namespace legacy_file_ns = blender::nodes::node_composite_cryptomatte_cc;
+ namespace legacy_file_ns = blender::nodes::node_composite_legacy_cryptomatte_cc;
namespace file_ns = blender::nodes::node_composite_cryptomatte_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_CRYPTOMATTE_LEGACY, "Cryptomatte", NODE_CLASS_MATTE);
node_type_socket_templates(&ntype, nullptr, file_ns::cmp_node_cryptomatte_out);
- node_type_init(&ntype, file_ns::node_init_cryptomatte_legacy);
+ node_type_init(&ntype, legacy_file_ns::node_init_cryptomatte_legacy);
node_type_storage(
&ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte);
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_operation = legacy_file_ns::get_compositor_operation;
+
nodeRegisterType(&ntype);
}
/** \} */
-}
diff --git a/source/blender/nodes/composite/nodes/node_composite_curves.cc b/source/blender/nodes/composite/nodes/node_composite_curves.cc
index fff0d467f75..bf45e219730 100644
--- a/source/blender/nodes/composite/nodes/node_composite_curves.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc
@@ -5,14 +5,23 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.h"
+
+#include "BKE_colortools.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_node_operation.hh"
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** CURVE Time ******************** */
-namespace blender::nodes::node_composite_curves_cc {
+namespace blender::nodes::node_composite_time_curves_cc {
static void cmp_node_time_declare(NodeDeclarationBuilder &b)
{
@@ -27,11 +36,65 @@ static void node_composit_init_curves_time(bNodeTree *UNUSED(ntree), bNode *node
node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
}
-} // namespace blender::nodes::node_composite_curves_cc
+using namespace blender::realtime_compositor;
+
+class TimeCurveOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &result = get_result("Fac");
+ result.allocate_single_value();
+
+ CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping());
+ BKE_curvemapping_init(curve_mapping);
+ const float time = BKE_curvemapping_evaluateF(curve_mapping, 0, compute_normalized_time());
+ result.set_float_value(clamp_f(time, 0.0f, 1.0f));
+ }
+
+ const CurveMapping *get_curve_mapping()
+ {
+ return static_cast<const CurveMapping *>(bnode().storage);
+ }
+
+ int get_start_time()
+ {
+ return bnode().custom1;
+ }
+
+ int get_end_time()
+ {
+ return bnode().custom2;
+ }
+
+ float compute_normalized_time()
+ {
+ const int frame_number = context().get_frame_number();
+ if (frame_number < get_start_time()) {
+ return 0.0f;
+ }
+ if (frame_number > get_end_time()) {
+ return 1.0f;
+ }
+ if (get_start_time() == get_end_time()) {
+ return 0.0f;
+ }
+ return static_cast<float>(frame_number - get_start_time()) /
+ static_cast<float>(get_end_time() - get_start_time());
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TimeCurveOperation(context, node);
+}
+
+} // namespace blender::nodes::node_composite_time_curves_cc
void register_node_type_cmp_curve_time()
{
- namespace file_ns = blender::nodes::node_composite_curves_cc;
+ namespace file_ns = blender::nodes::node_composite_time_curves_cc;
static bNodeType ntype;
@@ -40,17 +103,22 @@ void register_node_type_cmp_curve_time()
node_type_size(&ntype, 200, 140, 320);
node_type_init(&ntype, file_ns::node_composit_init_curves_time);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
/* **************** CURVE VEC ******************** */
-namespace blender::nodes::node_composite_curves_cc {
+namespace blender::nodes::node_composite_vector_curves_cc {
static void cmp_node_curve_vec_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Vector>(N_("Vector")).default_value({0.0f, 0.0f, 0.0f}).min(-1.0f).max(1.0f);
+ b.add_input<decl::Vector>(N_("Vector"))
+ .default_value({0.0f, 0.0f, 0.0f})
+ .min(-1.0f)
+ .max(1.0f)
+ .compositor_domain_priority(0);
b.add_output<decl::Vector>(N_("Vector"));
}
@@ -64,11 +132,63 @@ static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA
uiTemplateCurveMapping(layout, ptr, "mapping", 'v', false, false, false, false);
}
-} // namespace blender::nodes::node_composite_curves_cc
+using namespace blender::realtime_compositor;
+
+class VectorCurvesShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping());
+
+ BKE_curvemapping_init(curve_mapping);
+ float *band_values;
+ int band_size;
+ BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
+ float band_layer;
+ GPUNodeLink *band_texture = GPU_color_band(material, band_size, band_values, &band_layer);
+
+ float start_slopes[CM_TOT];
+ float end_slopes[CM_TOT];
+ BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes);
+ float range_minimums[CM_TOT];
+ BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
+ float range_dividers[CM_TOT];
+ BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
+
+ GPU_stack_link(material,
+ &bnode(),
+ "curves_vector",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(range_minimums),
+ GPU_uniform(range_dividers),
+ GPU_uniform(start_slopes),
+ GPU_uniform(end_slopes));
+ }
+
+ const CurveMapping *get_curve_mapping()
+ {
+ return static_cast<const CurveMapping *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new VectorCurvesShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_vector_curves_cc
void register_node_type_cmp_curve_vec()
{
- namespace file_ns = blender::nodes::node_composite_curves_cc;
+ namespace file_ns = blender::nodes::node_composite_vector_curves_cc;
static bNodeType ntype;
@@ -78,19 +198,26 @@ void register_node_type_cmp_curve_vec()
node_type_size(&ntype, 200, 140, 320);
node_type_init(&ntype, file_ns::node_composit_init_curve_vec);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** CURVE RGB ******************** */
-namespace blender::nodes::node_composite_curves_cc {
+namespace blender::nodes::node_composite_rgb_curves_cc {
static void cmp_node_rgbcurves_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(-1.0f).max(1.0f).subtype(
- PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(-1.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_input<decl::Color>(N_("Black Level")).default_value({0.0f, 0.0f, 0.0f, 1.0f});
b.add_input<decl::Color>(N_("White Level")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_output<decl::Color>(N_("Image"));
@@ -101,11 +228,105 @@ static void node_composit_init_curve_rgb(bNodeTree *UNUSED(ntree), bNode *node)
node->storage = BKE_curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f);
}
-} // namespace blender::nodes::node_composite_curves_cc
+using namespace blender::realtime_compositor;
+
+class RGBCurvesShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping());
+
+ BKE_curvemapping_init(curve_mapping);
+ float *band_values;
+ int band_size;
+ BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
+ float band_layer;
+ GPUNodeLink *band_texture = GPU_color_band(material, band_size, band_values, &band_layer);
+
+ float start_slopes[CM_TOT];
+ float end_slopes[CM_TOT];
+ BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes);
+ float range_minimums[CM_TOT];
+ BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
+ float range_dividers[CM_TOT];
+ BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
+
+ if (curve_mapping->tone == CURVE_TONE_FILMLIKE) {
+ GPU_stack_link(material,
+ &bnode(),
+ "curves_film_like",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(&range_minimums[3]),
+ GPU_uniform(&range_dividers[3]),
+ GPU_uniform(&start_slopes[3]),
+ GPU_uniform(&end_slopes[3]));
+ return;
+ }
+
+ const float min = 0.0f;
+ const float max = 1.0f;
+ GPU_link(material,
+ "clamp_value",
+ get_input_link("Fac"),
+ GPU_constant(&min),
+ GPU_constant(&max),
+ &get_input("Fac").link);
+
+ /* If the RGB curves do nothing, use a function that skips RGB computations. */
+ if (BKE_curvemapping_is_map_identity(curve_mapping, 0) &&
+ BKE_curvemapping_is_map_identity(curve_mapping, 1) &&
+ BKE_curvemapping_is_map_identity(curve_mapping, 2)) {
+ GPU_stack_link(material,
+ &bnode(),
+ "curves_combined_only",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(&range_minimums[3]),
+ GPU_uniform(&range_dividers[3]),
+ GPU_uniform(&start_slopes[3]),
+ GPU_uniform(&end_slopes[3]));
+ return;
+ }
+
+ GPU_stack_link(material,
+ &bnode(),
+ "curves_combined_rgb",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(range_minimums),
+ GPU_uniform(range_dividers),
+ GPU_uniform(start_slopes),
+ GPU_uniform(end_slopes));
+ }
+
+ const CurveMapping *get_curve_mapping()
+ {
+ return static_cast<const CurveMapping *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new RGBCurvesShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_rgb_curves_cc
void register_node_type_cmp_curve_rgb()
{
- namespace file_ns = blender::nodes::node_composite_curves_cc;
+ namespace file_ns = blender::nodes::node_composite_rgb_curves_cc;
static bNodeType ntype;
@@ -114,6 +335,7 @@ void register_node_type_cmp_curve_rgb()
node_type_size(&ntype, 200, 140, 320);
node_type_init(&ntype, file_ns::node_composit_init_curve_rgb);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_defocus.cc b/source/blender/nodes/composite/nodes/node_composite_defocus.cc
index 83dd397ff1f..94b4908a1bd 100644
--- a/source/blender/nodes/composite/nodes/node_composite_defocus.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_defocus.cc
@@ -12,6 +12,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* ************ Defocus Node ****************** */
@@ -81,6 +83,23 @@ static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA
uiItemR(sub, ptr, "z_scale", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DefocusOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DefocusOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_defocus_cc
void register_node_type_cmp_defocus()
@@ -94,6 +113,7 @@ void register_node_type_cmp_defocus()
ntype.draw_buttons = file_ns::node_composit_buts_defocus;
node_type_init(&ntype, file_ns::node_composit_init_defocus);
node_type_storage(&ntype, "NodeDefocus", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_denoise.cc b/source/blender/nodes/composite/nodes/node_composite_denoise.cc
index 051a2580ef9..0452e7cd943 100644
--- a/source/blender/nodes/composite/nodes/node_composite_denoise.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_denoise.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_denoise_cc {
@@ -52,6 +54,23 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "use_hdr", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DenoiseOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DenoiseOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_denoise_cc
void register_node_type_cmp_denoise()
@@ -65,6 +84,7 @@ void register_node_type_cmp_denoise()
ntype.draw_buttons = file_ns::node_composit_buts_denoise;
node_type_init(&ntype, file_ns::node_composit_init_denonise);
node_type_storage(&ntype, "NodeDenoise", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_despeckle.cc b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc
index 66a18cfa369..aa6725b8750 100644
--- a/source/blender/nodes/composite/nodes/node_composite_despeckle.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc
@@ -8,6 +8,11 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** FILTER ******************** */
@@ -16,8 +21,15 @@ namespace blender::nodes::node_composite_despeckle_cc {
static void cmp_node_despeckle_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -36,6 +48,61 @@ static void node_composit_buts_despeckle(uiLayout *layout, bContext *UNUSED(C),
uiItemR(col, ptr, "threshold_neighbor", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DespeckleOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ const Result &input_image = get_input("Image");
+ /* Single value inputs can't be despeckled and are returned as is. */
+ if (input_image.is_single_value()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_despeckle");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1f(shader, "threshold", get_threshold());
+ GPU_shader_uniform_1f(shader, "neighbor_threshold", get_neighbor_threshold());
+
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Result &factor_image = get_input("Fac");
+ factor_image.bind_as_texture(shader, "factor_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ input_image.unbind_as_texture();
+ factor_image.unbind_as_texture();
+ }
+
+ float get_threshold()
+ {
+ return bnode().custom3;
+ }
+
+ float get_neighbor_threshold()
+ {
+ return bnode().custom4;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DespeckleOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_despeckle_cc
void register_node_type_cmp_despeckle()
@@ -49,6 +116,7 @@ void register_node_type_cmp_despeckle()
ntype.draw_buttons = file_ns::node_composit_buts_despeckle;
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_despeckle);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc
index 20dd61a9725..8912d00a9be 100644
--- a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc
@@ -8,18 +8,28 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* channel Difference Matte ********************************* */
namespace blender::nodes::node_composite_diff_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_diff_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image 1")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Image 2")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image 1"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Image 2"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
- b.add_output<decl::Color>(N_("Matte"));
+ b.add_output<decl::Float>(N_("Matte"));
}
static void node_composit_init_diff_matte(bNodeTree *UNUSED(ntree), bNode *node)
@@ -40,6 +50,45 @@ static void node_composit_buts_diff_matte(uiLayout *layout, bContext *UNUSED(C),
uiItemR(col, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DifferenceMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float tolerance = get_tolerance();
+ const float falloff = get_falloff();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_difference_matte",
+ inputs,
+ outputs,
+ GPU_uniform(&tolerance),
+ GPU_uniform(&falloff));
+ }
+
+ float get_tolerance()
+ {
+ return node_storage(bnode()).t1;
+ }
+
+ float get_falloff()
+ {
+ return node_storage(bnode()).t2;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new DifferenceMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_diff_matte_cc
void register_node_type_cmp_diff_matte()
@@ -54,6 +103,7 @@ void register_node_type_cmp_diff_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_diff_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_dilate.cc b/source/blender/nodes/composite/nodes/node_composite_dilate.cc
index 9bdb9ae0837..551dfacb276 100644
--- a/source/blender/nodes/composite/nodes/node_composite_dilate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_dilate.cc
@@ -5,17 +5,36 @@
* \ingroup cmpnodes
*/
+#include <cmath>
+
+#include "BLI_array.hh"
+#include "BLI_assert.h"
+#include "BLI_math_base.hh"
+
+#include "DNA_scene_types.h"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "RE_pipeline.h"
+
+#include "GPU_shader.h"
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** Dilate/Erode ******************** */
namespace blender::nodes::node_composite_dilate_cc {
+NODE_STORAGE_FUNCS(NodeDilateErode)
+
static void cmp_node_dilate_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f);
@@ -34,15 +53,477 @@ static void node_composit_buts_dilateerode(uiLayout *layout, bContext *UNUSED(C)
uiItemR(layout, ptr, "mode", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
uiItemR(layout, ptr, "distance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
switch (RNA_enum_get(ptr, "mode")) {
- case CMP_NODE_DILATEERODE_DISTANCE_THRESH:
+ case CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD:
uiItemR(layout, ptr, "edge", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
break;
- case CMP_NODE_DILATEERODE_DISTANCE_FEATHER:
+ case CMP_NODE_DILATE_ERODE_DISTANCE_FEATHER:
uiItemR(layout, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
break;
}
}
+using namespace blender::realtime_compositor;
+
+/* Computes a falloff that is equal to 1 at an input of zero and decrease to zero at an input of 1,
+ * with the rate of decrease depending on the falloff type. */
+static float compute_distance_falloff(float x, int falloff_type)
+{
+ x = 1.0f - x;
+
+ switch (falloff_type) {
+ case PROP_SMOOTH:
+ return 3.0f * x * x - 2.0f * x * x * x;
+ case PROP_SPHERE:
+ return std::sqrt(2.0f * x - x * x);
+ case PROP_ROOT:
+ return std::sqrt(x);
+ case PROP_SHARP:
+ return x * x;
+ case PROP_INVSQUARE:
+ return x * (2.0f - x);
+ case PROP_LIN:
+ return x;
+ default:
+ BLI_assert_unreachable();
+ return x;
+ }
+}
+
+/* A helper class that computes and caches 1D GPU textures containing the weights of the separable
+ * Gaussian filter of the given radius as well as an inverse distance falloff of the given type and
+ * radius. The weights and falloffs are symmetric, because the Gaussian and falloff functions are
+ * all even functions. Consequently, only the positive half of the filter is computed and the
+ * shader takes that into consideration. */
+class SymmetricSeparableMorphologicalDistanceFeatherWeights {
+ private:
+ int radius_ = 1;
+ int falloff_type_ = PROP_SMOOTH;
+ GPUTexture *weights_texture_ = nullptr;
+ GPUTexture *distance_falloffs_texture_ = nullptr;
+
+ public:
+ ~SymmetricSeparableMorphologicalDistanceFeatherWeights()
+ {
+ if (weights_texture_) {
+ GPU_texture_free(weights_texture_);
+ }
+
+ if (distance_falloffs_texture_) {
+ GPU_texture_free(distance_falloffs_texture_);
+ }
+ }
+
+ /* Check if textures containing the weights and distance falloffs were already computed for the
+ * given distance falloff type and radius. If such textures exists, do nothing, otherwise, free
+ * the already computed textures and recompute it with the given distance falloff type and
+ * radius. */
+ void update(int radius, int falloff_type)
+ {
+ if (weights_texture_ && distance_falloffs_texture_ && falloff_type == falloff_type_ &&
+ radius == radius_) {
+ return;
+ }
+
+ radius_ = radius;
+ falloff_type_ = falloff_type;
+
+ compute_weights();
+ compute_distance_falloffs();
+ }
+
+ void compute_weights()
+ {
+ if (weights_texture_) {
+ GPU_texture_free(weights_texture_);
+ }
+
+ /* The size of filter is double the radius plus 1, but since the filter is symmetric, we only
+ * compute half of it and no doubling happens. We add 1 to make sure the filter size is always
+ * odd and there is a center weight. */
+ const int size = radius_ + 1;
+ Array<float> weights(size);
+
+ float sum = 0.0f;
+
+ /* First, compute the center weight. */
+ const float center_weight = RE_filter_value(R_FILTER_GAUSS, 0.0f);
+ weights[0] = center_weight;
+ sum += center_weight;
+
+ /* Second, compute the other weights in the positive direction, making sure to add double the
+ * weight to the sum of weights because the filter is symmetric and we only loop over half of
+ * it. Skip the center weight already computed by dropping the front index. */
+ const float scale = radius_ > 0.0f ? 1.0f / radius_ : 0.0f;
+ for (const int i : weights.index_range().drop_front(1)) {
+ const float weight = RE_filter_value(R_FILTER_GAUSS, i * scale);
+ weights[i] = weight;
+ sum += weight * 2.0f;
+ }
+
+ /* Finally, normalize the weights. */
+ for (const int i : weights.index_range()) {
+ weights[i] /= sum;
+ }
+
+ weights_texture_ = GPU_texture_create_1d("Weights", size, 1, GPU_R16F, weights.data());
+ }
+
+ void compute_distance_falloffs()
+ {
+ if (distance_falloffs_texture_) {
+ GPU_texture_free(distance_falloffs_texture_);
+ }
+
+ /* The size of the distance falloffs is double the radius plus 1, but since the falloffs are
+ * symmetric, we only compute half of them and no doubling happens. We add 1 to make sure the
+ * falloffs size is always odd and there is a center falloff. */
+ const int size = radius_ + 1;
+ Array<float> falloffs(size);
+
+ /* Compute the distance falloffs in the positive direction only, because the falloffs are
+ * symmetric. */
+ const float scale = radius_ > 0.0f ? 1.0f / radius_ : 0.0f;
+ for (const int i : falloffs.index_range()) {
+ falloffs[i] = compute_distance_falloff(i * scale, falloff_type_);
+ }
+
+ distance_falloffs_texture_ = GPU_texture_create_1d(
+ "Distance Factors", size, 1, GPU_R16F, falloffs.data());
+ }
+
+ void bind_weights_as_texture(GPUShader *shader, const char *texture_name)
+ {
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name);
+ GPU_texture_bind(weights_texture_, texture_image_unit);
+ }
+
+ void unbind_weights_as_texture()
+ {
+ GPU_texture_unbind(weights_texture_);
+ }
+
+ void bind_distance_falloffs_as_texture(GPUShader *shader, const char *texture_name)
+ {
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name);
+ GPU_texture_bind(distance_falloffs_texture_, texture_image_unit);
+ }
+
+ void unbind_distance_falloffs_as_texture()
+ {
+ GPU_texture_unbind(distance_falloffs_texture_);
+ }
+};
+
+class DilateErodeOperation : public NodeOperation {
+ private:
+ /* Cached symmetric blur weights and distance falloffs for the distance feature method. */
+ SymmetricSeparableMorphologicalDistanceFeatherWeights distance_feather_weights_;
+
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ if (is_identity()) {
+ get_input("Mask").pass_through(get_result("Mask"));
+ return;
+ }
+
+ switch (get_method()) {
+ case CMP_NODE_DILATE_ERODE_STEP:
+ execute_step();
+ return;
+ case CMP_NODE_DILATE_ERODE_DISTANCE:
+ execute_distance();
+ return;
+ case CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD:
+ execute_distance_threshold();
+ return;
+ case CMP_NODE_DILATE_ERODE_DISTANCE_FEATHER:
+ execute_distance_feather();
+ return;
+ default:
+ BLI_assert_unreachable();
+ return;
+ }
+ }
+
+ /* ----------------------------
+ * Step Morphological Operator.
+ * ---------------------------- */
+
+ void execute_step()
+ {
+ GPUTexture *horizontal_pass_result = execute_step_horizontal_pass();
+ execute_step_vertical_pass(horizontal_pass_result);
+ }
+
+ GPUTexture *execute_step_horizontal_pass()
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_step_shader_name());
+ GPU_shader_bind(shader);
+
+ /* Pass the absolute value of the distance. We have specialized shaders for each sign. */
+ GPU_shader_uniform_1i(shader, "radius", math::abs(get_distance()));
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "input_tx");
+
+ /* We allocate an output image of a transposed size, that is, with a height equivalent to the
+ * width of the input and vice versa. This is done as a performance optimization. The shader
+ * will process the image horizontally and write it to the intermediate output transposed. Then
+ * the vertical pass will execute the same horizontal pass shader, but since its input is
+ * transposed, it will effectively do a vertical pass and write to the output transposed,
+ * effectively undoing the transposition in the horizontal pass. This is done to improve
+ * spatial cache locality in the shader and to avoid having two separate shaders for each of
+ * the passes. */
+ const Domain domain = compute_domain();
+ const int2 transposed_domain = int2(domain.size.y, domain.size.x);
+
+ GPUTexture *horizontal_pass_result = texture_pool().acquire_color(transposed_domain);
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(horizontal_pass_result, image_unit);
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ input_mask.unbind_as_texture();
+ GPU_texture_image_unbind(horizontal_pass_result);
+
+ return horizontal_pass_result;
+ }
+
+ void execute_step_vertical_pass(GPUTexture *horizontal_pass_result)
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_step_shader_name());
+ GPU_shader_bind(shader);
+
+ /* Pass the absolute value of the distance. We have specialized shaders for each sign. */
+ GPU_shader_uniform_1i(shader, "radius", math::abs(get_distance()));
+
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(horizontal_pass_result, texture_image_unit);
+
+ const Domain domain = compute_domain();
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_img");
+
+ /* Notice that the domain is transposed, see the note on the horizontal pass method for more
+ * information on the reasoning behind this. */
+ compute_dispatch_threads_at_least(shader, int2(domain.size.y, domain.size.x));
+
+ GPU_shader_unbind();
+ output_mask.unbind_as_image();
+ GPU_texture_unbind(horizontal_pass_result);
+ }
+
+ const char *get_morphological_step_shader_name()
+ {
+ if (get_distance() > 0) {
+ return "compositor_morphological_step_dilate";
+ }
+ return "compositor_morphological_step_erode";
+ }
+
+ /* --------------------------------
+ * Distance Morphological Operator.
+ * -------------------------------- */
+
+ void execute_distance()
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_distance_shader_name());
+ GPU_shader_bind(shader);
+
+ /* Pass the absolute value of the distance. We have specialized shaders for each sign. */
+ GPU_shader_uniform_1i(shader, "radius", math::abs(get_distance()));
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "input_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_mask.unbind_as_image();
+ input_mask.unbind_as_texture();
+ }
+
+ const char *get_morphological_distance_shader_name()
+ {
+ if (get_distance() > 0) {
+ return "compositor_morphological_distance_dilate";
+ }
+ return "compositor_morphological_distance_erode";
+ }
+
+ /* ------------------------------------------
+ * Distance Threshold Morphological Operator.
+ * ------------------------------------------ */
+
+ void execute_distance_threshold()
+ {
+ GPUShader *shader = shader_manager().get("compositor_morphological_distance_threshold");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1f(shader, "inset", get_inset());
+ GPU_shader_uniform_1i(shader, "radius", get_morphological_distance_threshold_radius());
+ GPU_shader_uniform_1i(shader, "distance", get_distance());
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "input_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_mask.unbind_as_image();
+ input_mask.unbind_as_texture();
+ }
+
+ /* See the discussion in the implementation for more information. */
+ int get_morphological_distance_threshold_radius()
+ {
+ return static_cast<int>(math::ceil(get_inset())) + math::abs(get_distance());
+ }
+
+ /* ----------------------------------------
+ * Distance Feather Morphological Operator.
+ * ---------------------------------------- */
+
+ void execute_distance_feather()
+ {
+ GPUTexture *horizontal_pass_result = execute_distance_feather_horizontal_pass();
+ execute_distance_feather_vertical_pass(horizontal_pass_result);
+ }
+
+ GPUTexture *execute_distance_feather_horizontal_pass()
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_distance_feather_shader_name());
+ GPU_shader_bind(shader);
+
+ const Result &input_image = get_input("Mask");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ distance_feather_weights_.update(math::abs(get_distance()), node_storage(bnode()).falloff);
+ distance_feather_weights_.bind_weights_as_texture(shader, "weights_tx");
+ distance_feather_weights_.bind_distance_falloffs_as_texture(shader, "falloffs_tx");
+
+ /* We allocate an output image of a transposed size, that is, with a height equivalent to the
+ * width of the input and vice versa. This is done as a performance optimization. The shader
+ * will process the image horizontally and write it to the intermediate output transposed. Then
+ * the vertical pass will execute the same horizontal pass shader, but since its input is
+ * transposed, it will effectively do a vertical pass and write to the output transposed,
+ * effectively undoing the transposition in the horizontal pass. This is done to improve
+ * spatial cache locality in the shader and to avoid having two separate shaders for each of
+ * the passes. */
+ const Domain domain = compute_domain();
+ const int2 transposed_domain = int2(domain.size.y, domain.size.x);
+
+ GPUTexture *horizontal_pass_result = texture_pool().acquire_color(transposed_domain);
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(horizontal_pass_result, image_unit);
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ input_image.unbind_as_texture();
+ distance_feather_weights_.unbind_weights_as_texture();
+ distance_feather_weights_.unbind_distance_falloffs_as_texture();
+ GPU_texture_image_unbind(horizontal_pass_result);
+
+ return horizontal_pass_result;
+ }
+
+ void execute_distance_feather_vertical_pass(GPUTexture *horizontal_pass_result)
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_distance_feather_shader_name());
+ GPU_shader_bind(shader);
+
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(horizontal_pass_result, texture_image_unit);
+
+ distance_feather_weights_.update(math::abs(get_distance()), node_storage(bnode()).falloff);
+ distance_feather_weights_.bind_weights_as_texture(shader, "weights_tx");
+ distance_feather_weights_.bind_distance_falloffs_as_texture(shader, "falloffs_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_image = get_result("Mask");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ /* Notice that the domain is transposed, see the note on the horizontal pass method for more
+ * information on the reasoning behind this. */
+ compute_dispatch_threads_at_least(shader, int2(domain.size.y, domain.size.x));
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ distance_feather_weights_.unbind_weights_as_texture();
+ distance_feather_weights_.unbind_distance_falloffs_as_texture();
+ GPU_texture_unbind(horizontal_pass_result);
+ }
+
+ const char *get_morphological_distance_feather_shader_name()
+ {
+ if (get_distance() > 0) {
+ return "compositor_morphological_distance_feather_dilate";
+ }
+ return "compositor_morphological_distance_feather_erode";
+ }
+
+ /* ---------------
+ * Common Methods.
+ * --------------- */
+
+ bool is_identity()
+ {
+ const Result &input = get_input("Mask");
+ if (input.is_single_value()) {
+ return true;
+ }
+
+ if (get_method() == CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD && get_inset() != 0.0f) {
+ return false;
+ }
+
+ if (get_distance() == 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ int get_distance()
+ {
+ return bnode().custom2;
+ }
+
+ float get_inset()
+ {
+ return bnode().custom3;
+ }
+
+ CMPNodeDilateErodeMethod get_method()
+ {
+ return (CMPNodeDilateErodeMethod)bnode().custom1;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DilateErodeOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_dilate_cc
void register_node_type_cmp_dilateerode()
@@ -57,6 +538,7 @@ void register_node_type_cmp_dilateerode()
node_type_init(&ntype, file_ns::node_composit_init_dilateerode);
node_type_storage(
&ntype, "NodeDilateErode", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
index 3d82ab04fc9..6e6bec70283 100644
--- a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
@@ -5,16 +5,30 @@
* \ingroup cmpnodes
*/
+#include "BLI_float3x3.hh"
+#include "BLI_math_base.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_math_vector.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_directionalblur_cc {
+NODE_STORAGE_FUNCS(NodeDBlurData)
+
static void cmp_node_directional_blur_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -51,6 +65,129 @@ static void node_composit_buts_dblur(uiLayout *layout, bContext *UNUSED(C), Poin
uiItemR(layout, ptr, "zoom", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DirectionalBlurOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ if (is_identity()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_directional_blur");
+ GPU_shader_bind(shader);
+
+ /* The number of iterations does not cover the original image, that is, the image with no
+ * transformation. So add an extra iteration for the original image and put that into
+ * consideration in the shader. */
+ GPU_shader_uniform_1i(shader, "iterations", get_iterations() + 1);
+ GPU_shader_uniform_mat3_as_mat4(shader, "inverse_transformation", get_transformation().ptr());
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ GPU_texture_filter_mode(input_image.texture(), true);
+ GPU_texture_wrap_mode(input_image.texture(), false, false);
+
+ const Domain domain = compute_domain();
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ input_image.unbind_as_texture();
+ }
+
+ /* Get the amount of translation that will be applied on each iteration. The translation is in
+ * the negative x direction rotated in the clock-wise direction, hence the negative sign for the
+ * rotation and translation vector. */
+ float2 get_translation()
+ {
+ const float diagonal_length = math::length(float2(get_input("Image").domain().size));
+ const float translation_amount = diagonal_length * node_storage(bnode()).distance;
+ const float3x3 rotation = float3x3::from_rotation(-node_storage(bnode()).angle);
+ return rotation * float2(-translation_amount / get_iterations(), 0.0f);
+ }
+
+ /* Get the amount of rotation that will be applied on each iteration. */
+ float get_rotation()
+ {
+ return node_storage(bnode()).spin / get_iterations();
+ }
+
+ /* Get the amount of scale that will be applied on each iteration. The scale is identity when the
+ * user supplies 0, so we add 1. */
+ float2 get_scale()
+ {
+ return float2(1.0f + node_storage(bnode()).zoom / get_iterations());
+ }
+
+ float2 get_origin()
+ {
+ const float2 center = float2(node_storage(bnode()).center_x, node_storage(bnode()).center_y);
+ return float2(get_input("Image").domain().size) * center;
+ }
+
+ float3x3 get_transformation()
+ {
+ /* Construct the transformation that will be applied on each iteration. */
+ const float3x3 transformation = float3x3::from_translation_rotation_scale(
+ get_translation(), get_rotation(), get_scale());
+ /* Change the origin of the transformation to the user-specified origin. */
+ const float3x3 origin_transformation = float3x3::from_origin_transformation(transformation,
+ get_origin());
+ /* The shader will transform the coordinates, not the image itself, so take the inverse. */
+ return origin_transformation.inverted();
+ }
+
+ /* The actual number of iterations is 2 to the power of the user supplied iterations. The power
+ * is implemented using a bit shift. But also make sure it doesn't exceed the upper limit which
+ * is the number of diagonal pixels. */
+ int get_iterations()
+ {
+ const int iterations = 2 << (node_storage(bnode()).iter - 1);
+ const int upper_limit = math::ceil(math::length(float2(get_input("Image").domain().size)));
+ return math::min(iterations, upper_limit);
+ }
+
+ /* Returns true if the operation does nothing and the input can be passed through. */
+ bool is_identity()
+ {
+ const Result &input = get_input("Image");
+ /* Single value inputs can't be blurred and are returned as is. */
+ if (input.is_single_value()) {
+ return true;
+ }
+
+ /* If any of the following options are non-zero, then the operation is not an identity. */
+ if (node_storage(bnode()).distance != 0.0f) {
+ return false;
+ }
+
+ if (node_storage(bnode()).spin != 0.0f) {
+ return false;
+ }
+
+ if (node_storage(bnode()).zoom != 0.0f) {
+ return false;
+ }
+
+ return true;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DirectionalBlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_directionalblur_cc
void register_node_type_cmp_dblur()
@@ -65,6 +202,7 @@ void register_node_type_cmp_dblur()
node_type_init(&ntype, file_ns::node_composit_init_dblur);
node_type_storage(
&ntype, "NodeDBlurData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_displace.cc b/source/blender/nodes/composite/nodes/node_composite_displace.cc
index 0b0d42cbb08..1049f2fa4a9 100644
--- a/source/blender/nodes/composite/nodes/node_composite_displace.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_displace.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Displace ******************** */
@@ -24,6 +26,23 @@ static void cmp_node_displace_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class DisplaceOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DisplaceOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_displace_cc
void register_node_type_cmp_displace()
@@ -34,6 +53,7 @@ void register_node_type_cmp_displace()
cmp_node_type_base(&ntype, CMP_NODE_DISPLACE, "Displace", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_displace_declare;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc
index a8646d8498e..6a786571f43 100644
--- a/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc
@@ -8,16 +8,26 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* channel Distance Matte ********************************* */
namespace blender::nodes::node_composite_distance_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_distance_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Key Color"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -26,7 +36,7 @@ static void node_composit_init_distance_matte(bNodeTree *UNUSED(ntree), bNode *n
{
NodeChroma *c = MEM_cnew<NodeChroma>(__func__);
node->storage = c;
- c->channel = 1;
+ c->channel = CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_RGBA;
c->t1 = 0.1f;
c->t2 = 0.1f;
}
@@ -48,6 +58,61 @@ static void node_composit_buts_distance_matte(uiLayout *layout,
uiItemR(col, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DistanceMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float tolerance = get_tolerance();
+ const float falloff = get_falloff();
+
+ if (get_color_space() == CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_RGBA) {
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_distance_matte_rgba",
+ inputs,
+ outputs,
+ GPU_uniform(&tolerance),
+ GPU_uniform(&falloff));
+ return;
+ }
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_distance_matte_ycca",
+ inputs,
+ outputs,
+ GPU_uniform(&tolerance),
+ GPU_uniform(&falloff));
+ }
+
+ CMPNodeDistanceMatteColorSpace get_color_space()
+ {
+ return (CMPNodeDistanceMatteColorSpace)node_storage(bnode()).channel;
+ }
+
+ float get_tolerance()
+ {
+ return node_storage(bnode()).t1;
+ }
+
+ float get_falloff()
+ {
+ return node_storage(bnode()).t2;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new DistanceMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_distance_matte_cc
void register_node_type_cmp_distance_matte()
@@ -62,6 +127,7 @@ void register_node_type_cmp_distance_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_distance_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc b/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc
index 9dc2b223618..fec7879ed78 100644
--- a/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Double Edge Mask ******************** */
@@ -35,6 +37,23 @@ static void node_composit_buts_double_edge_mask(uiLayout *layout,
uiItemR(col, ptr, "edge_mode", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DoubleEdgeMaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Inner Mask").pass_through(get_result("Mask"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DoubleEdgeMaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_double_edge_mask_cc
void register_node_type_cmp_doubleedgemask()
@@ -46,6 +65,7 @@ void register_node_type_cmp_doubleedgemask()
cmp_node_type_base(&ntype, CMP_NODE_DOUBLEEDGEMASK, "Double Edge Mask", NODE_CLASS_MATTE);
ntype.declare = file_ns::cmp_node_double_edge_mask_declare;
ntype.draw_buttons = file_ns::node_composit_buts_double_edge_mask;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc
index 4da6a0a442e..7c031b354e5 100644
--- a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc
@@ -5,15 +5,26 @@
* \ingroup cmpnodes
*/
+#include <cmath>
+
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** SCALAR MATH ******************** */
namespace blender::nodes::node_composite_ellipsemask_cc {
+NODE_STORAGE_FUNCS(NodeEllipseMask)
+
static void cmp_node_ellipsemask_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f);
@@ -46,6 +57,93 @@ static void node_composit_buts_ellipsemask(uiLayout *layout, bContext *UNUSED(C)
uiItemR(layout, ptr, "mask_type", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class EllipseMaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUShader *shader = shader_manager().get(get_shader_name());
+ GPU_shader_bind(shader);
+
+ const Domain domain = compute_domain();
+
+ GPU_shader_uniform_2iv(shader, "domain_size", domain.size);
+
+ GPU_shader_uniform_2fv(shader, "location", get_location());
+ GPU_shader_uniform_2fv(shader, "radius", get_size() / 2.0f);
+ GPU_shader_uniform_1f(shader, "cos_angle", std::cos(get_angle()));
+ GPU_shader_uniform_1f(shader, "sin_angle", std::sin(get_angle()));
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "base_mask_tx");
+
+ const Result &value = get_input("Value");
+ value.bind_as_texture(shader, "mask_value_tx");
+
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_mask_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_mask.unbind_as_texture();
+ value.unbind_as_texture();
+ output_mask.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ Domain compute_domain() override
+ {
+ if (get_input("Mask").is_single_value()) {
+ return Domain(context().get_output_size());
+ }
+ return get_input("Mask").domain();
+ }
+
+ CMPNodeMaskType get_mask_type()
+ {
+ return (CMPNodeMaskType)bnode().custom1;
+ }
+
+ const char *get_shader_name()
+ {
+ switch (get_mask_type()) {
+ default:
+ case CMP_NODE_MASKTYPE_ADD:
+ return "compositor_ellipse_mask_add";
+ case CMP_NODE_MASKTYPE_SUBTRACT:
+ return "compositor_ellipse_mask_subtract";
+ case CMP_NODE_MASKTYPE_MULTIPLY:
+ return "compositor_ellipse_mask_multiply";
+ case CMP_NODE_MASKTYPE_NOT:
+ return "compositor_ellipse_mask_not";
+ }
+ }
+
+ float2 get_location()
+ {
+ return float2(node_storage(bnode()).x, node_storage(bnode()).y);
+ }
+
+ float2 get_size()
+ {
+ return float2(node_storage(bnode()).width, node_storage(bnode()).height);
+ }
+
+ float get_angle()
+ {
+ return node_storage(bnode()).rotation;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new EllipseMaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_ellipsemask_cc
void register_node_type_cmp_ellipsemask()
@@ -61,6 +159,7 @@ void register_node_type_cmp_ellipsemask()
node_type_init(&ntype, file_ns::node_composit_init_ellipsemask);
node_type_storage(
&ntype, "NodeEllipseMask", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_exposure.cc b/source/blender/nodes/composite/nodes/node_composite_exposure.cc
index 881cfc11058..19b93680ff6 100644
--- a/source/blender/nodes/composite/nodes/node_composite_exposure.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_exposure.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Exposure ******************** */
@@ -13,11 +17,33 @@ namespace blender::nodes::node_composite_exposure_cc {
static void cmp_node_exposure_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Exposure")).min(-10.0f).max(10.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Exposure")).min(-10.0f).max(10.0f).compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class ExposureShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_exposure", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ExposureShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_exposure_cc
void register_node_type_cmp_exposure()
@@ -28,6 +54,7 @@ void register_node_type_cmp_exposure()
cmp_node_type_base(&ntype, CMP_NODE_EXPOSURE, "Exposure", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_exposure_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_filter.cc b/source/blender/nodes/composite/nodes/node_composite_filter.cc
index c343c21feb2..bd7b443e17e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_filter.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_filter.cc
@@ -5,9 +5,14 @@
* \ingroup cmpnodes
*/
+#include "BLI_float3x3.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** FILTER ******************** */
@@ -16,8 +21,15 @@ namespace blender::nodes::node_composite_filter_cc {
static void cmp_node_filter_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -26,6 +38,119 @@ static void node_composit_buts_filter(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class FilterOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUShader *shader = shader_manager().get(get_shader_name());
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_mat3_as_mat4(shader, "kernel", get_filter_kernel().ptr());
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Result &factor = get_input("Fac");
+ factor.bind_as_texture(shader, "factor_tx");
+
+ const Domain domain = compute_domain();
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_image.unbind_as_texture();
+ factor.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ CMPNodeFilterMethod get_filter_method()
+ {
+ return (CMPNodeFilterMethod)bnode().custom1;
+ }
+
+ float3x3 get_filter_kernel()
+ {
+ /* Initialize the kernels as arrays of rows with the top row first. Edge detection kernels
+ * return the kernel in the X direction, while the kernel in the Y direction will be computed
+ * inside the shader by transposing the kernel in the X direction. */
+ switch (get_filter_method()) {
+ case CMP_NODE_FILTER_SOFT: {
+ const float kernel[3][3] = {{1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f},
+ {2.0f / 16.0f, 4.0f / 16.0f, 2.0f / 16.0f},
+ {1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_SHARP_BOX: {
+ const float kernel[3][3] = {
+ {-1.0f, -1.0f, -1.0f}, {-1.0f, 9.0f, -1.0f}, {-1.0f, -1.0f, -1.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_LAPLACE: {
+ const float kernel[3][3] = {{-1.0f / 8.0f, -1.0f / 8.0f, -1.0f / 8.0f},
+ {-1.0f / 8.0f, 1.0f, -1.0f / 8.0f},
+ {-1.0f / 8.0f, -1.0f / 8.0f, -1.0f / 8.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_SOBEL: {
+ const float kernel[3][3] = {{1.0f, 0.0f, -1.0f}, {2.0f, 0.0f, -2.0f}, {1.0f, 0.0f, -1.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_PREWITT: {
+ const float kernel[3][3] = {{1.0f, 0.0f, -1.0f}, {1.0f, 0.0f, -1.0f}, {1.0f, 0.0f, -1.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_KIRSCH: {
+ const float kernel[3][3] = {
+ {5.0f, -3.0f, -2.0f}, {5.0f, -3.0f, -2.0f}, {5.0f, -3.0f, -2.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_SHADOW: {
+ const float kernel[3][3] = {{1.0f, 2.0f, 1.0f}, {0.0f, 1.0f, 0.0f}, {-1.0f, -2.0f, -1.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_SHARP_DIAMOND: {
+ const float kernel[3][3] = {
+ {0.0f, -1.0f, 0.0f}, {-1.0f, 5.0f, -1.0f}, {0.0f, -1.0f, 0.0f}};
+ return float3x3(kernel);
+ }
+ default: {
+ const float kernel[3][3] = {{0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0f}};
+ return float3x3(kernel);
+ }
+ }
+ }
+
+ const char *get_shader_name()
+ {
+ switch (get_filter_method()) {
+ case CMP_NODE_FILTER_LAPLACE:
+ case CMP_NODE_FILTER_SOBEL:
+ case CMP_NODE_FILTER_PREWITT:
+ case CMP_NODE_FILTER_KIRSCH:
+ return "compositor_edge_filter";
+ case CMP_NODE_FILTER_SOFT:
+ case CMP_NODE_FILTER_SHARP_BOX:
+ case CMP_NODE_FILTER_SHADOW:
+ case CMP_NODE_FILTER_SHARP_DIAMOND:
+ default:
+ return "compositor_filter";
+ }
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new FilterOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_filter_cc
void register_node_type_cmp_filter()
@@ -39,6 +164,7 @@ void register_node_type_cmp_filter()
ntype.draw_buttons = file_ns::node_composit_buts_filter;
ntype.labelfunc = node_filter_label;
ntype.flag |= NODE_PREVIEW;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_flip.cc b/source/blender/nodes/composite/nodes/node_composite_flip.cc
index 37b9a2d020d..aaa2b565ed2 100644
--- a/source/blender/nodes/composite/nodes/node_composite_flip.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_flip.cc
@@ -5,9 +5,18 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+#include "BLI_utildefines.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** Flip ******************** */
@@ -16,7 +25,9 @@ namespace blender::nodes::node_composite_flip_cc {
static void cmp_node_flip_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -25,6 +36,56 @@ static void node_composit_buts_flip(uiLayout *layout, bContext *UNUSED(C), Point
uiItemR(layout, ptr, "axis", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class FlipOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+
+ /* Can't flip a single value, pass it through to the output. */
+ if (input.is_single_value()) {
+ input.pass_through(result);
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_flip");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1b(
+ shader, "flip_x", ELEM(get_flip_mode(), CMP_NODE_FLIP_X, CMP_NODE_FLIP_X_Y));
+ GPU_shader_uniform_1b(
+ shader, "flip_y", ELEM(get_flip_mode(), CMP_NODE_FLIP_Y, CMP_NODE_FLIP_X_Y));
+
+ input.bind_as_texture(shader, "input_tx");
+
+ const Domain domain = compute_domain();
+
+ result.allocate_texture(domain);
+ result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input.unbind_as_texture();
+ result.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ CMPNodeFlipMode get_flip_mode()
+ {
+ return (CMPNodeFlipMode)bnode().custom1;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new FlipOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_flip_cc
void register_node_type_cmp_flip()
@@ -36,6 +97,7 @@ void register_node_type_cmp_flip()
cmp_node_type_base(&ntype, CMP_NODE_FLIP, "Flip", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_flip_declare;
ntype.draw_buttons = file_ns::node_composit_buts_flip;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_gamma.cc b/source/blender/nodes/composite/nodes/node_composite_gamma.cc
index b4b8502e915..660d8068231 100644
--- a/source/blender/nodes/composite/nodes/node_composite_gamma.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_gamma.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Gamma Tools ******************** */
@@ -13,15 +17,38 @@ namespace blender::nodes::node_composite_gamma_cc {
static void cmp_node_gamma_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_input<decl::Float>(N_("Gamma"))
.default_value(1.0f)
.min(0.001f)
.max(10.0f)
- .subtype(PROP_UNSIGNED);
+ .subtype(PROP_UNSIGNED)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class GammaShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_gamma", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new GammaShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_gamma_cc
void register_node_type_cmp_gamma()
@@ -32,6 +59,7 @@ void register_node_type_cmp_gamma()
cmp_node_type_base(&ntype, CMP_NODE_GAMMA, "Gamma", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_gamma_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_glare.cc b/source/blender/nodes/composite/nodes/node_composite_glare.cc
index 7f21d30cfa6..33577d5caf8 100644
--- a/source/blender/nodes/composite/nodes/node_composite_glare.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_glare.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_glare_cc {
@@ -75,6 +77,23 @@ static void node_composit_buts_glare(uiLayout *layout, bContext *UNUSED(C), Poin
}
}
+using namespace blender::realtime_compositor;
+
+class GlareOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new GlareOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_glare_cc
void register_node_type_cmp_glare()
@@ -88,6 +107,7 @@ void register_node_type_cmp_glare()
ntype.draw_buttons = file_ns::node_composit_buts_glare;
node_type_init(&ntype, file_ns::node_composit_init_glare);
node_type_storage(&ntype, "NodeGlare", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc b/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc
index 08a048829df..091864a06f7 100644
--- a/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Hue Saturation ******************** */
@@ -13,22 +17,56 @@ namespace blender::nodes::node_composite_hue_sat_val_cc {
static void cmp_node_huesatval_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Hue")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Hue"))
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
b.add_input<decl::Float>(N_("Saturation"))
.default_value(1.0f)
.min(0.0f)
.max(2.0f)
- .subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(2);
b.add_input<decl::Float>(N_("Value"))
.default_value(1.0f)
.min(0.0f)
.max(2.0f)
- .subtype(PROP_FACTOR);
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(3);
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(4);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class HueSaturationValueShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_hue_saturation_value", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new HueSaturationValueShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_hue_sat_val_cc
void register_node_type_cmp_hue_sat()
@@ -39,6 +77,7 @@ void register_node_type_cmp_hue_sat()
cmp_node_type_base(&ntype, CMP_NODE_HUE_SAT, "Hue Saturation Value", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_huesatval_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
index bb5e6bf06a8..6333860a19b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
@@ -5,14 +5,29 @@
* \ingroup cmpnodes
*/
+#include "BKE_colortools.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
+#include "BKE_colortools.h"
+
namespace blender::nodes::node_composite_huecorrect_cc {
static void cmp_node_huecorrect_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -33,6 +48,53 @@ static void node_composit_init_huecorrect(bNodeTree *UNUSED(ntree), bNode *node)
cumapping->cur = 1;
}
+using namespace blender::realtime_compositor;
+
+class HueCorrectShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping());
+
+ BKE_curvemapping_init(curve_mapping);
+ float *band_values;
+ int band_size;
+ BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
+ float band_layer;
+ GPUNodeLink *band_texture = GPU_color_band(material, band_size, band_values, &band_layer);
+
+ float range_minimums[CM_TOT];
+ BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
+ float range_dividers[CM_TOT];
+ BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_hue_correct",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(range_minimums),
+ GPU_uniform(range_dividers));
+ }
+
+ const CurveMapping *get_curve_mapping()
+ {
+ return static_cast<const CurveMapping *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new HueCorrectShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_huecorrect_cc
void register_node_type_cmp_huecorrect()
@@ -46,6 +108,7 @@ void register_node_type_cmp_huecorrect()
node_type_size(&ntype, 320, 140, 500);
node_type_init(&ntype, file_ns::node_composit_init_huecorrect);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_id_mask.cc b/source/blender/nodes/composite/nodes/node_composite_id_mask.cc
index 25ab9aa63fc..ac8456cb931 100644
--- a/source/blender/nodes/composite/nodes/node_composite_id_mask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_id_mask.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** ID Mask ******************** */
@@ -26,6 +28,23 @@ static void node_composit_buts_id_mask(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "use_antialiasing", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class IDMaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("ID value").pass_through(get_result("Alpha"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new IDMaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_id_mask_cc
void register_node_type_cmp_idmask()
@@ -37,6 +56,7 @@ void register_node_type_cmp_idmask()
cmp_node_type_base(&ntype, CMP_NODE_ID_MASK, "ID Mask", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_idmask_declare;
ntype.draw_buttons = file_ns::node_composit_buts_id_mask;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_image.cc b/source/blender/nodes/composite/nodes/node_composite_image.cc
index d071e9f13db..4d1eff0b940 100644
--- a/source/blender/nodes/composite/nodes/node_composite_image.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_image.cc
@@ -8,23 +8,34 @@
#include "node_composite_util.hh"
#include "BLI_linklist.h"
+#include "BLI_math_vec_types.hh"
#include "BLI_utildefines.h"
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_image.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_scene.h"
+#include "DEG_depsgraph_query.h"
+
#include "DNA_scene_types.h"
#include "RE_engine.h"
+#include "RE_pipeline.h"
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
/* **************** IMAGE (and RenderResult, multilayer image) ******************** */
static bNodeSocketTemplate cmp_node_rlayers_out[] = {
@@ -431,6 +442,215 @@ static void node_composit_copy_image(bNodeTree *UNUSED(dest_ntree),
}
}
+using namespace blender::realtime_compositor;
+
+class ImageOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ if (!is_valid()) {
+ allocate_invalid();
+ return;
+ }
+
+ update_image_frame_number();
+
+ for (const bNodeSocket *output : this->node()->output_sockets()) {
+ compute_output(output->identifier);
+ }
+ }
+
+ /* Returns true if the node results can be computed, otherwise, returns false. */
+ bool is_valid()
+ {
+ Image *image = get_image();
+ ImageUser *image_user = get_image_user();
+ if (!image || !image_user) {
+ return false;
+ }
+
+ if (BKE_image_is_multilayer(image)) {
+ if (!image->rr) {
+ return false;
+ }
+
+ RenderLayer *render_layer = get_render_layer();
+ if (!render_layer) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /* Allocate all needed outputs as invalid. This should be called when is_valid returns false. */
+ void allocate_invalid()
+ {
+ for (const bNodeSocket *output : this->node()->output_sockets()) {
+ if (!should_compute_output(output->identifier)) {
+ continue;
+ }
+
+ Result &result = get_result(output->identifier);
+ result.allocate_invalid();
+ }
+ }
+
+ /* Compute the effective frame number of the image if it was animated and invalidate the cached
+ * GPU texture if the computed frame number is different. */
+ void update_image_frame_number()
+ {
+ BKE_image_user_frame_calc(get_image(), get_image_user(), context().get_frame_number());
+ }
+
+ void compute_output(StringRef identifier)
+ {
+ if (!should_compute_output(identifier)) {
+ return;
+ }
+
+ ImageUser image_user = compute_image_user_for_output(identifier);
+ GPUTexture *image_texture = BKE_image_get_gpu_texture(get_image(), &image_user, nullptr);
+
+ const int2 size = int2(GPU_texture_width(image_texture), GPU_texture_height(image_texture));
+ Result &result = get_result(identifier);
+ result.allocate_texture(Domain(size));
+
+ GPUShader *shader = shader_manager().get(get_shader_name(identifier));
+ GPU_shader_bind(shader);
+
+ const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(image_texture, input_unit);
+
+ result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ GPU_shader_unbind();
+ GPU_texture_unbind(image_texture);
+ result.unbind_as_image();
+ }
+
+ /* Get a copy of the image user that is appropriate to retrieve the image buffer for the output
+ * with the given identifier. This essentially sets the appropriate pass and view indices that
+ * corresponds to the output. */
+ ImageUser compute_image_user_for_output(StringRef identifier)
+ {
+ ImageUser image_user = *get_image_user();
+
+ /* Set the needed view. */
+ image_user.view = get_view_index();
+
+ /* Set the needed pass. */
+ if (BKE_image_is_multilayer(get_image())) {
+ image_user.pass = get_pass_index(get_pass_name(identifier));
+ BKE_image_multilayer_index(get_image()->rr, &image_user);
+ }
+ else {
+ BKE_image_multiview_index(get_image(), &image_user);
+ }
+
+ return image_user;
+ }
+
+ /* Get the shader that should be used to compute the output with the given identifier. The
+ * shaders just copy the retrieved image textures into the results except for the alpha output,
+ * which extracts the alpha and writes it to the result instead. Note that a call to a host
+ * texture copy doesn't work because results are stored in a different half float formats. */
+ const char *get_shader_name(StringRef identifier)
+ {
+ if (identifier == "Alpha") {
+ return "compositor_extract_alpha_from_color";
+ }
+ else if (get_result(identifier).type() == ResultType::Color) {
+ return "compositor_convert_color_to_half_color";
+ }
+ else {
+ return "compositor_convert_float_to_half_float";
+ }
+ }
+
+ Image *get_image()
+ {
+ return (Image *)bnode().id;
+ }
+
+ ImageUser *get_image_user()
+ {
+ return static_cast<ImageUser *>(bnode().storage);
+ }
+
+ /* Get the render layer selected in the node assuming the image is a multilayer image. */
+ RenderLayer *get_render_layer()
+ {
+ const ListBase *layers = &get_image()->rr->layers;
+ return static_cast<RenderLayer *>(BLI_findlink(layers, get_image_user()->layer));
+ }
+
+ /* Get the name of the pass corresponding to the output with the given identifier assuming the
+ * image is a multilayer image. */
+ const char *get_pass_name(StringRef identifier)
+ {
+ DOutputSocket output = node().output_by_identifier(identifier);
+ return static_cast<NodeImageLayer *>(output->storage)->pass_name;
+ }
+
+ /* Get the index of the pass with the given name in the selected render layer's passes list
+ * assuming the image is a multilayer image. */
+ int get_pass_index(const char *name)
+ {
+ return BLI_findstringindex(&get_render_layer()->passes, name, offsetof(RenderPass, name));
+ }
+
+ /* Get the index of the view selected in the node. If the image is not a multi-view image or only
+ * has a single view, then zero is returned. Otherwise, if the image is a multi-view image, the
+ * index of the selected view is returned. However, note that the value of the view member of the
+ * image user is not the actual index of the view. More specifically, the index 0 is reserved to
+ * denote the special mode of operation "All", which dynamically selects the view whose name
+ * matches the view currently being rendered. It follows that the views are then indexed starting
+ * from 1. So for non zero view values, the actual index of the view is the value of the view
+ * member of the image user minus 1. */
+ int get_view_index()
+ {
+ /* The image is not a multi-view image, so just return zero. */
+ if (!BKE_image_is_multiview(get_image())) {
+ return 0;
+ }
+
+ const ListBase *views = &get_image()->rr->views;
+ /* There is only one view and its index is 0. */
+ if (BLI_listbase_count_at_most(views, 2) < 2) {
+ return 0;
+ }
+
+ const int view = get_image_user()->view;
+ /* The view is not zero, which means it is manually specified and the actual index is then the
+ * view value minus 1. */
+ if (view != 0) {
+ return view - 1;
+ }
+
+ /* Otherwise, the view value is zero, denoting the special mode of operation "All", which finds
+ * the index of the view whose name matches the view currently being rendered. */
+ const char *view_name = context().get_view_name().data();
+ const int matched_view = BLI_findstringindex(views, view_name, offsetof(RenderView, name));
+
+ /* No view matches the view currently being rendered, so fallback to the first view. */
+ if (matched_view == -1) {
+ return 0;
+ }
+
+ return matched_view;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ImageOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_image_cc
void register_node_type_cmp_image()
@@ -444,6 +664,7 @@ void register_node_type_cmp_image()
node_type_storage(
&ntype, "ImageUser", file_ns::node_composit_free_image, file_ns::node_composit_copy_image);
node_type_update(&ntype, file_ns::cmp_node_image_update);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.labelfunc = node_image_label;
ntype.flag |= NODE_PREVIEW;
@@ -467,7 +688,7 @@ const char *node_cmp_rlayers_sock_to_pass(int sock_index)
return (STREQ(name, "Alpha")) ? RE_PASSNAME_COMBINED : name;
}
-namespace blender::nodes::node_composite_image_cc {
+namespace blender::nodes::node_composite_render_layer_cc {
static void node_composit_init_rlayers(const bContext *C, PointerRNA *ptr)
{
@@ -593,11 +814,60 @@ static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, Pointer
RNA_string_set(&op_ptr, "scene", scene_name);
}
-} // namespace blender::nodes::node_composite_image_cc
+using namespace blender::realtime_compositor;
+
+class RenderLayerOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ const int view_layer = bnode().custom1;
+ GPUTexture *pass_texture = context().get_input_texture(view_layer, SCE_PASS_COMBINED);
+ const int2 size = int2(GPU_texture_width(pass_texture), GPU_texture_height(pass_texture));
+
+ /* Compute image output. */
+ Result &image_result = get_result("Image");
+ image_result.allocate_texture(Domain(size));
+ GPU_texture_copy(image_result.texture(), pass_texture);
+
+ /* Compute alpha output. */
+ Result &alpha_result = get_result("Alpha");
+ alpha_result.allocate_texture(Domain(size));
+
+ GPUShader *shader = shader_manager().get("compositor_extract_alpha_from_color");
+ GPU_shader_bind(shader);
+
+ const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(pass_texture, input_unit);
+
+ alpha_result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ GPU_shader_unbind();
+ GPU_texture_unbind(pass_texture);
+ alpha_result.unbind_as_image();
+
+ /* Other output passes are not supported for now, so allocate them as invalid. */
+ for (const bNodeSocket *output : this->node()->output_sockets()) {
+ if (!STREQ(output->identifier, "Image") && !STREQ(output->identifier, "Alpha")) {
+ get_result(output->identifier).allocate_invalid();
+ }
+ }
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new RenderLayerOperation(context, node);
+}
+
+} // namespace blender::nodes::node_composite_render_layer_cc
void register_node_type_cmp_rlayers()
{
- namespace file_ns = blender::nodes::node_composite_image_cc;
+ namespace file_ns = blender::nodes::node_composite_render_layer_cc;
static bNodeType ntype;
@@ -606,6 +876,7 @@ void register_node_type_cmp_rlayers()
ntype.draw_buttons = file_ns::node_composit_buts_viewlayers;
ntype.initfunc_api = file_ns::node_composit_init_rlayers;
ntype.poll = file_ns::node_composit_poll_rlayers;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.flag |= NODE_PREVIEW;
node_type_storage(
&ntype, nullptr, file_ns::node_composit_free_rlayers, file_ns::node_composit_copy_rlayers);
diff --git a/source/blender/nodes/composite/nodes/node_composite_inpaint.cc b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc
index 2958d1b2869..f6e46bef299 100644
--- a/source/blender/nodes/composite/nodes/node_composite_inpaint.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Inpaint/ ******************** */
@@ -25,6 +27,23 @@ static void node_composit_buts_inpaint(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "distance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class InpaintOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new InpaintOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_inpaint_cc
void register_node_type_cmp_inpaint()
@@ -36,6 +55,7 @@ void register_node_type_cmp_inpaint()
cmp_node_type_base(&ntype, CMP_NODE_INPAINT, "Inpaint", NODE_CLASS_OP_FILTER);
ntype.declare = file_ns::cmp_node_inpaint_declare;
ntype.draw_buttons = file_ns::node_composit_buts_inpaint;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_invert.cc b/source/blender/nodes/composite/nodes/node_composite_invert.cc
index 6dff043537a..4bfcc7b6b9c 100644
--- a/source/blender/nodes/composite/nodes/node_composite_invert.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_invert.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** INVERT ******************** */
@@ -16,8 +20,15 @@ namespace blender::nodes::node_composite_invert_cc {
static void cmp_node_invert_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Color"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Color"));
}
@@ -35,6 +46,45 @@ static void node_composit_buts_invert(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(col, ptr, "invert_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class InvertShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float do_rgb = get_do_rgb();
+ const float do_alpha = get_do_alpha();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_invert",
+ inputs,
+ outputs,
+ GPU_constant(&do_rgb),
+ GPU_constant(&do_alpha));
+ }
+
+ bool get_do_rgb()
+ {
+ return bnode().custom1 & CMP_CHAN_RGB;
+ }
+
+ bool get_do_alpha()
+ {
+ return bnode().custom1 & CMP_CHAN_A;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new InvertShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_invert_cc
void register_node_type_cmp_invert()
@@ -47,6 +97,7 @@ void register_node_type_cmp_invert()
ntype.declare = file_ns::cmp_node_invert_declare;
ntype.draw_buttons = file_ns::node_composit_buts_invert;
node_type_init(&ntype, file_ns::node_composit_init_invert);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_keying.cc b/source/blender/nodes/composite/nodes/node_composite_keying.cc
index fbfdf2ad3c6..8b584e216cd 100644
--- a/source/blender/nodes/composite/nodes/node_composite_keying.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_keying.cc
@@ -14,6 +14,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Keying ******************** */
@@ -63,6 +65,25 @@ static void node_composit_buts_keying(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "blur_post", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class KeyingOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Matte").allocate_invalid();
+ get_result("Edges").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new KeyingOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_keying_cc
void register_node_type_cmp_keying()
@@ -77,6 +98,7 @@ void register_node_type_cmp_keying()
node_type_init(&ntype, file_ns::node_composit_init_keying);
node_type_storage(
&ntype, "NodeKeyingData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc
index c3ed5cd7aa8..9eec705b6ca 100644
--- a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc
@@ -6,16 +6,23 @@
*/
#include "DNA_movieclip_types.h"
+#include "DNA_tracking_types.h"
#include "BLI_math_base.h"
#include "BLI_math_color.h"
+#include "BKE_context.h"
+#include "BKE_lib_id.h"
+#include "BKE_tracking.h"
+
#include "RNA_access.h"
#include "RNA_prototypes.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Keying Screen ******************** */
@@ -27,10 +34,23 @@ static void cmp_node_keyingscreen_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("Screen"));
}
-static void node_composit_init_keyingscreen(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_composit_init_keyingscreen(const bContext *C, PointerRNA *ptr)
{
+ bNode *node = (bNode *)ptr->data;
+
NodeKeyingScreenData *data = MEM_cnew<NodeKeyingScreenData>(__func__);
node->storage = data;
+
+ const Scene *scene = CTX_data_scene(C);
+ if (scene->clip) {
+ MovieClip *clip = scene->clip;
+
+ node->id = &clip->id;
+ id_us_plus(&clip->id);
+
+ const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
+ BLI_strncpy(data->tracking_object, tracking_object->name, sizeof(data->tracking_object));
+ }
}
static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -60,6 +80,23 @@ static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, Point
}
}
+using namespace blender::realtime_compositor;
+
+class KeyingScreenOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("Screen").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new KeyingScreenOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_keyingscreen_cc
void register_node_type_cmp_keyingscreen()
@@ -71,9 +108,10 @@ void register_node_type_cmp_keyingscreen()
cmp_node_type_base(&ntype, CMP_NODE_KEYINGSCREEN, "Keying Screen", NODE_CLASS_MATTE);
ntype.declare = file_ns::cmp_node_keyingscreen_declare;
ntype.draw_buttons = file_ns::node_composit_buts_keyingscreen;
- node_type_init(&ntype, file_ns::node_composit_init_keyingscreen);
+ ntype.initfunc_api = file_ns::node_composit_init_keyingscreen;
node_type_storage(
&ntype, "NodeKeyingScreenData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc
index 593b7cc9b71..260fccf66d0 100644
--- a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc
@@ -5,20 +5,50 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.h"
+#include "BLI_math_vec_types.hh"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
+/* Distortion can't be exactly -1.0 as it will cause infinite pincushion distortion. */
+#define MINIMUM_DISTORTION -0.999f
+/* Arbitrary scaling factor for the dispersion input in projector distortion mode. */
+#define PROJECTOR_DISPERSION_SCALE 5.0f
+/* Arbitrary scaling factor for the dispersion input in screen distortion mode. */
+#define SCREEN_DISPERSION_SCALE 4.0f
+/* Arbitrary scaling factor for the distortion input. */
+#define DISTORTION_SCALE 4.0f
+
namespace blender::nodes::node_composite_lensdist_cc {
+NODE_STORAGE_FUNCS(NodeLensDist)
+
static void cmp_node_lensdist_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Distort")).default_value(0.0f).min(-0.999f).max(1.0f);
- b.add_input<decl::Float>(N_("Dispersion")).default_value(0.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Distort"))
+ .default_value(0.0f)
+ .min(MINIMUM_DISTORTION)
+ .max(1.0f)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Dispersion"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
@@ -42,6 +72,173 @@ static void node_composit_buts_lensdist(uiLayout *layout, bContext *UNUSED(C), P
uiItemR(col, ptr, "use_fit", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class LensDistortionOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ if (is_identity()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ if (get_is_projector()) {
+ execute_projector_distortion();
+ }
+ else {
+ execute_screen_distortion();
+ }
+ }
+
+ void execute_projector_distortion()
+ {
+ GPUShader *shader = shader_manager().get("compositor_projector_lens_distortion");
+ GPU_shader_bind(shader);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ GPU_texture_filter_mode(input_image.texture(), true);
+ GPU_texture_wrap_mode(input_image.texture(), false, false);
+
+ const Domain domain = compute_domain();
+
+ const float dispersion = (get_dispersion() * PROJECTOR_DISPERSION_SCALE) / domain.size.x;
+ GPU_shader_uniform_1f(shader, "dispersion", dispersion);
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_image.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ void execute_screen_distortion()
+ {
+ GPUShader *shader = shader_manager().get(get_screen_distortion_shader());
+ GPU_shader_bind(shader);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ GPU_texture_filter_mode(input_image.texture(), true);
+ GPU_texture_wrap_mode(input_image.texture(), false, false);
+
+ const Domain domain = compute_domain();
+
+ const float3 chromatic_distortion = compute_chromatic_distortion();
+ GPU_shader_uniform_3fv(shader, "chromatic_distortion", chromatic_distortion);
+
+ GPU_shader_uniform_1f(shader, "scale", compute_scale());
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_image.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ const char *get_screen_distortion_shader()
+ {
+ if (get_is_jitter()) {
+ return "compositor_screen_lens_distortion_jitter";
+ }
+ return "compositor_screen_lens_distortion";
+ }
+
+ float get_distortion()
+ {
+ const Result &input = get_input("Distort");
+ return clamp_f(input.get_float_value_default(0.0f), MINIMUM_DISTORTION, 1.0f);
+ }
+
+ float get_dispersion()
+ {
+ const Result &input = get_input("Dispersion");
+ return clamp_f(input.get_float_value_default(0.0f), 0.0f, 1.0f);
+ }
+
+ /* Get the distortion amount for each channel. The green channel has a distortion amount that
+ * matches that specified in the node inputs, while the red and blue channels have higher and
+ * lower distortion amounts respectively based on the dispersion value. */
+ float3 compute_chromatic_distortion()
+ {
+ const float green_distortion = get_distortion();
+ const float dispersion = get_dispersion() / SCREEN_DISPERSION_SCALE;
+ const float red_distortion = clamp_f(green_distortion + dispersion, MINIMUM_DISTORTION, 1.0f);
+ const float blue_distortion = clamp_f(green_distortion - dispersion, MINIMUM_DISTORTION, 1.0f);
+ return float3(red_distortion, green_distortion, blue_distortion) * DISTORTION_SCALE;
+ }
+
+ /* The distortion model will distort the image in such a way that the result will no longer
+ * fit the domain of the original image, so we scale the image to account for that. If get_is_fit
+ * is false, then the scaling factor will be such that the furthest pixels horizontally and
+ * vertically are at the boundary of the image. Otherwise, if get_is_fit is true, the scaling
+ * factor will be such that the furthest pixels diagonally are at the corner of the image. */
+ float compute_scale()
+ {
+ const float3 distortion = compute_chromatic_distortion() / DISTORTION_SCALE;
+ const float maximum_distortion = max_fff(distortion[0], distortion[1], distortion[2]);
+
+ if (get_is_fit() && (maximum_distortion > 0.0f)) {
+ return 1.0f / (1.0f + 2.0f * maximum_distortion);
+ }
+ return 1.0f / (1.0f + maximum_distortion);
+ }
+
+ bool get_is_projector()
+ {
+ return node_storage(bnode()).proj;
+ }
+
+ bool get_is_jitter()
+ {
+ return node_storage(bnode()).jit;
+ }
+
+ bool get_is_fit()
+ {
+ return node_storage(bnode()).fit;
+ }
+
+ /* Returns true if the operation does nothing and the input can be passed through. */
+ bool is_identity()
+ {
+ /* The input is a single value and the operation does nothing. */
+ if (get_input("Image").is_single_value()) {
+ return true;
+ }
+
+ /* Projector have zero dispersion and does nothing. */
+ if (get_is_projector() && get_dispersion() == 0.0f) {
+ return true;
+ }
+
+ /* Both distortion and dispersion are zero and the operation does nothing. */
+ if (get_distortion() == 0.0f && get_dispersion() == 0.0f) {
+ return true;
+ }
+
+ return false;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new LensDistortionOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_lensdist_cc
void register_node_type_cmp_lensdist()
@@ -56,6 +253,7 @@ void register_node_type_cmp_lensdist()
node_type_init(&ntype, file_ns::node_composit_init_lensdist);
node_type_storage(
&ntype, "NodeLensDist", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_levels.cc b/source/blender/nodes/composite/nodes/node_composite_levels.cc
index a30567672f0..2f1ebeb79b5 100644
--- a/source/blender/nodes/composite/nodes/node_composite_levels.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_levels.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** LEVELS ******************** */
@@ -31,6 +33,24 @@ static void node_composit_buts_view_levels(uiLayout *layout, bContext *UNUSED(C)
uiItemR(layout, ptr, "channel", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class LevelsOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("Mean").allocate_invalid();
+ get_result("Std Dev").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new LevelsOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_levels_cc
void register_node_type_cmp_view_levels()
@@ -44,6 +64,7 @@ void register_node_type_cmp_view_levels()
ntype.draw_buttons = file_ns::node_composit_buts_view_levels;
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_view_levels);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc
index 94697a2aafd..59ae62ec411 100644
--- a/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc
@@ -5,18 +5,28 @@
* \ingroup cmpnodes
*/
+#include "IMB_colormanagement.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Luma Matte Node ********************************* */
namespace blender::nodes::node_composite_luma_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_luma_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -40,6 +50,48 @@ static void node_composit_buts_luma_matte(uiLayout *layout, bContext *UNUSED(C),
col, ptr, "limit_min", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class LuminanceMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float high = get_high();
+ const float low = get_low();
+ float luminance_coefficients[3];
+ IMB_colormanagement_get_luminance_coefficients(luminance_coefficients);
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_luminance_matte",
+ inputs,
+ outputs,
+ GPU_uniform(&high),
+ GPU_uniform(&low),
+ GPU_constant(luminance_coefficients));
+ }
+
+ float get_high()
+ {
+ return node_storage(bnode()).t1;
+ }
+
+ float get_low()
+ {
+ return node_storage(bnode()).t2;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new LuminanceMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_luma_matte_cc
void register_node_type_cmp_luma_matte()
@@ -54,6 +106,7 @@ void register_node_type_cmp_luma_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_luma_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_map_range.cc b/source/blender/nodes/composite/nodes/node_composite_map_range.cc
index e52c6d096b9..e72869efa93 100644
--- a/source/blender/nodes/composite/nodes/node_composite_map_range.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_map_range.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Map Range ******************** */
@@ -16,11 +20,31 @@ namespace blender::nodes::node_composite_map_range_cc {
static void cmp_node_map_range_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("From Min")).default_value(0.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("From Max")).default_value(1.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("To Min")).default_value(0.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("To Max")).default_value(1.0f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>(N_("Value"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("From Min"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("From Max"))
+ .default_value(1.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("To Min"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(3);
+ b.add_input<decl::Float>(N_("To Max"))
+ .default_value(1.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(4);
b.add_output<decl::Float>(N_("Value"));
}
@@ -32,6 +56,38 @@ static void node_composit_buts_map_range(uiLayout *layout, bContext *UNUSED(C),
uiItemR(col, ptr, "use_clamp", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class MapRangeShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float should_clamp = get_should_clamp();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_map_range",
+ inputs,
+ outputs,
+ GPU_constant(&should_clamp));
+ }
+
+ bool get_should_clamp()
+ {
+ return bnode().custom1;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new MapRangeShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_map_range_cc
void register_node_type_cmp_map_range()
@@ -43,6 +99,7 @@ void register_node_type_cmp_map_range()
cmp_node_type_base(&ntype, CMP_NODE_MAP_RANGE, "Map Range", NODE_CLASS_OP_VECTOR);
ntype.declare = file_ns::cmp_node_map_range_declare;
ntype.draw_buttons = file_ns::node_composit_buts_map_range;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_map_uv.cc b/source/blender/nodes/composite/nodes/node_composite_map_uv.cc
index 31961f07ea4..4f660d62c3b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_map_uv.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_map_uv.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Map UV ******************** */
@@ -26,6 +28,23 @@ static void node_composit_buts_map_uv(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class MapUVOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new MapUVOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_map_uv_cc
void register_node_type_cmp_mapuv()
@@ -37,6 +56,7 @@ void register_node_type_cmp_mapuv()
cmp_node_type_base(&ntype, CMP_NODE_MAP_UV, "Map UV", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_map_uv_declare;
ntype.draw_buttons = file_ns::node_composit_buts_map_uv;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_map_value.cc b/source/blender/nodes/composite/nodes/node_composite_map_value.cc
index b069cce93fc..e30de39605d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_map_value.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_map_value.cc
@@ -5,20 +5,32 @@
* \ingroup cmpnodes
*/
+#include "BKE_texture.h"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** MAP VALUE ******************** */
namespace blender::nodes::node_composite_map_value_cc {
+NODE_STORAGE_FUNCS(TexMapping)
+
static void cmp_node_map_value_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("Value"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Value"));
}
@@ -48,6 +60,51 @@ static void node_composit_buts_map_value(uiLayout *layout, bContext *UNUSED(C),
uiItemR(sub, ptr, "max", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class MapValueShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const TexMapping &texture_mapping = node_storage(bnode());
+
+ const float use_min = get_use_min();
+ const float use_max = get_use_max();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_map_value",
+ inputs,
+ outputs,
+ GPU_uniform(texture_mapping.loc),
+ GPU_uniform(texture_mapping.size),
+ GPU_constant(&use_min),
+ GPU_uniform(texture_mapping.min),
+ GPU_constant(&use_max),
+ GPU_uniform(texture_mapping.max));
+ }
+
+ bool get_use_min()
+ {
+ return node_storage(bnode()).flag & TEXMAP_CLIP_MIN;
+ }
+
+ bool get_use_max()
+ {
+ return node_storage(bnode()).flag & TEXMAP_CLIP_MAX;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new MapValueShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_map_value_cc
void register_node_type_cmp_map_value()
@@ -61,6 +118,7 @@ void register_node_type_cmp_map_value()
ntype.draw_buttons = file_ns::node_composit_buts_map_value;
node_type_init(&ntype, file_ns::node_composit_init_map_value);
node_type_storage(&ntype, "TexMapping", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_mask.cc b/source/blender/nodes/composite/nodes/node_composite_mask.cc
index 5b8fac5d1c0..2372dbae3f2 100644
--- a/source/blender/nodes/composite/nodes/node_composite_mask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_mask.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Mask ******************** */
@@ -74,6 +76,23 @@ static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *p
}
}
+using namespace blender::realtime_compositor;
+
+class MaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("Mask").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new MaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_mask_cc
void register_node_type_cmp_mask()
@@ -87,6 +106,7 @@ void register_node_type_cmp_mask()
ntype.draw_buttons = file_ns::node_composit_buts_mask;
node_type_init(&ntype, file_ns::node_composit_init_mask);
ntype.labelfunc = file_ns::node_mask_label;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
node_type_storage(&ntype, "NodeMask", node_free_standard_storage, node_copy_standard_storage);
diff --git a/source/blender/nodes/composite/nodes/node_composite_math.cc b/source/blender/nodes/composite/nodes/node_composite_math.cc
index 7b2eadef2cb..4baf057913e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_math.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_math.cc
@@ -5,6 +5,12 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
+#include "NOD_math_functions.hh"
+
#include "node_composite_util.hh"
/* **************** SCALAR MATH ******************** */
@@ -13,18 +19,72 @@ namespace blender::nodes::node_composite_math_cc {
static void cmp_node_math_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Value")).default_value(0.5f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>(N_("Value"))
+ .default_value(0.5f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(0);
b.add_input<decl::Float>(N_("Value"), "Value_001")
.default_value(0.5f)
.min(-10000.0f)
- .max(10000.0f);
+ .max(10000.0f)
+ .compositor_domain_priority(1);
b.add_input<decl::Float>(N_("Value"), "Value_002")
.default_value(0.5f)
.min(-10000.0f)
- .max(10000.0f);
+ .max(10000.0f)
+ .compositor_domain_priority(2);
b.add_output<decl::Float>(N_("Value"));
}
+using namespace blender::realtime_compositor;
+
+class MathShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+
+ if (!get_should_clamp()) {
+ return;
+ }
+
+ const float min = 0.0f;
+ const float max = 1.0f;
+ GPU_link(material,
+ "clamp_value",
+ get_output("Value").link,
+ GPU_constant(&min),
+ GPU_constant(&max),
+ &get_output("Value").link);
+ }
+
+ NodeMathOperation get_operation()
+ {
+ return (NodeMathOperation)bnode().custom1;
+ }
+
+ const char *get_shader_function_name()
+ {
+ return get_float_math_operation_info(get_operation())->shader_name.c_str();
+ }
+
+ bool get_should_clamp()
+ {
+ return bnode().custom2 & SHD_MATH_CLAMP;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new MathShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_math_cc
void register_node_type_cmp_math()
@@ -37,6 +97,7 @@ void register_node_type_cmp_math()
ntype.declare = file_ns::cmp_node_math_declare;
ntype.labelfunc = node_math_label;
node_type_update(&ntype, node_math_update);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc
index fc11aa188b0..a1fbbfe7d40 100644
--- a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc
@@ -5,6 +5,14 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+
+#include "DNA_material_types.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** MIX RGB ******************** */
@@ -13,12 +21,122 @@ namespace blender::nodes::node_composite_mixrgb_cc {
static void cmp_node_mixrgb_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Image"), "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(2);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Image"), "Image_001")
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class MixRGBShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ if (get_use_alpha()) {
+ GPU_link(material,
+ "multiply_by_alpha",
+ get_input_link("Fac"),
+ get_input_link("Image_001"),
+ &get_input("Fac").link);
+ }
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+
+ if (!get_should_clamp()) {
+ return;
+ }
+
+ const float min[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ const float max[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+ GPU_link(material,
+ "clamp_color",
+ get_output("Image").link,
+ GPU_constant(min),
+ GPU_constant(max),
+ &get_output("Image").link);
+ }
+
+ int get_mode()
+ {
+ return bnode().custom1;
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (get_mode()) {
+ case MA_RAMP_BLEND:
+ return "mix_blend";
+ case MA_RAMP_ADD:
+ return "mix_add";
+ case MA_RAMP_MULT:
+ return "mix_mult";
+ case MA_RAMP_SUB:
+ return "mix_sub";
+ case MA_RAMP_SCREEN:
+ return "mix_screen";
+ case MA_RAMP_DIV:
+ return "mix_div";
+ case MA_RAMP_DIFF:
+ return "mix_diff";
+ case MA_RAMP_DARK:
+ return "mix_dark";
+ case MA_RAMP_LIGHT:
+ return "mix_light";
+ case MA_RAMP_OVERLAY:
+ return "mix_overlay";
+ case MA_RAMP_DODGE:
+ return "mix_dodge";
+ case MA_RAMP_BURN:
+ return "mix_burn";
+ case MA_RAMP_HUE:
+ return "mix_hue";
+ case MA_RAMP_SAT:
+ return "mix_sat";
+ case MA_RAMP_VAL:
+ return "mix_val";
+ case MA_RAMP_COLOR:
+ return "mix_color";
+ case MA_RAMP_SOFT:
+ return "mix_soft";
+ case MA_RAMP_LINEAR:
+ return "mix_linear";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+
+ bool get_use_alpha()
+ {
+ return bnode().custom2 & SHD_MIXRGB_USE_ALPHA;
+ }
+
+ bool get_should_clamp()
+ {
+ return bnode().custom2 & SHD_MIXRGB_CLAMP;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new MixRGBShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_mixrgb_cc
void register_node_type_cmp_mix_rgb()
@@ -31,6 +149,7 @@ void register_node_type_cmp_mix_rgb()
ntype.flag |= NODE_PREVIEW;
ntype.declare = file_ns::cmp_node_mixrgb_declare;
ntype.labelfunc = node_blend_label;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
index a4d5f294fe0..b9d9620a214 100644
--- a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
@@ -5,8 +5,13 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+
#include "BKE_context.h"
#include "BKE_lib_id.h"
+#include "BKE_movieclip.h"
+#include "BKE_tracking.h"
+
#include "DNA_defaults.h"
#include "RNA_access.h"
@@ -14,6 +19,12 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_movieclip_cc {
@@ -79,6 +90,184 @@ static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, Point
uiTemplateColorspaceSettings(layout, &clipptr, "colorspace_settings");
}
+using namespace blender::realtime_compositor;
+
+class MovieClipOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUTexture *movie_clip_texture = get_movie_clip_texture();
+
+ compute_image(movie_clip_texture);
+ compute_alpha(movie_clip_texture);
+ compute_stabilization_data(movie_clip_texture);
+
+ free_movie_clip_texture();
+ }
+
+ void compute_image(GPUTexture *movie_clip_texture)
+ {
+ if (!should_compute_output("Image")) {
+ return;
+ }
+
+ Result &result = get_result("Image");
+
+ /* The movie clip texture is invalid or missing, set an appropriate fallback value. */
+ if (!movie_clip_texture) {
+ result.allocate_invalid();
+ return;
+ }
+
+ const int2 size = int2(GPU_texture_width(movie_clip_texture),
+ GPU_texture_height(movie_clip_texture));
+ result.allocate_texture(Domain(size));
+
+ GPUShader *shader = shader_manager().get("compositor_convert_color_to_half_color");
+ GPU_shader_bind(shader);
+
+ const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(movie_clip_texture, input_unit);
+
+ result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ GPU_shader_unbind();
+ GPU_texture_unbind(movie_clip_texture);
+ result.unbind_as_image();
+ }
+
+ void compute_alpha(GPUTexture *movie_clip_texture)
+ {
+ if (!should_compute_output("Alpha")) {
+ return;
+ }
+
+ Result &result = get_result("Alpha");
+
+ /* The movie clip texture is invalid or missing, set an appropriate fallback value. */
+ if (!movie_clip_texture) {
+ result.allocate_single_value();
+ result.set_float_value(1.0f);
+ return;
+ }
+
+ const int2 size = int2(GPU_texture_width(movie_clip_texture),
+ GPU_texture_height(movie_clip_texture));
+ result.allocate_texture(Domain(size));
+
+ GPUShader *shader = shader_manager().get("compositor_extract_alpha_from_color");
+ GPU_shader_bind(shader);
+
+ const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(movie_clip_texture, input_unit);
+
+ result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ GPU_shader_unbind();
+ GPU_texture_unbind(movie_clip_texture);
+ result.unbind_as_image();
+ }
+
+ void compute_stabilization_data(GPUTexture *movie_clip_texture)
+ {
+ /* The movie clip texture is invalid or missing, set appropriate fallback values. */
+ if (!movie_clip_texture) {
+ if (should_compute_output("Offset X")) {
+ Result &result = get_result("Offset X");
+ result.allocate_single_value();
+ result.set_float_value(0.0f);
+ }
+ if (should_compute_output("Offset Y")) {
+ Result &result = get_result("Offset Y");
+ result.allocate_single_value();
+ result.set_float_value(0.0f);
+ }
+ if (should_compute_output("Scale")) {
+ Result &result = get_result("Scale");
+ result.allocate_single_value();
+ result.set_float_value(1.0f);
+ }
+ if (should_compute_output("Angle")) {
+ Result &result = get_result("Angle");
+ result.allocate_single_value();
+ result.set_float_value(0.0f);
+ }
+ return;
+ }
+
+ MovieClip *movie_clip = get_movie_clip();
+ const int frame_number = BKE_movieclip_remap_scene_to_clip_frame(movie_clip,
+ context().get_frame_number());
+ const int width = GPU_texture_width(movie_clip_texture);
+ const int height = GPU_texture_height(movie_clip_texture);
+
+ /* If the movie clip has no stabilization data, it will initialize the given values with
+ * fallback values regardless, so no need to handle that case. */
+ float2 offset;
+ float scale, angle;
+ BKE_tracking_stabilization_data_get(
+ movie_clip, frame_number, width, height, offset, &scale, &angle);
+
+ if (should_compute_output("Offset X")) {
+ Result &result = get_result("Offset X");
+ result.allocate_single_value();
+ result.set_float_value(offset.x);
+ }
+ if (should_compute_output("Offset Y")) {
+ Result &result = get_result("Offset Y");
+ result.allocate_single_value();
+ result.set_float_value(offset.y);
+ }
+ if (should_compute_output("Scale")) {
+ Result &result = get_result("Scale");
+ result.allocate_single_value();
+ result.set_float_value(scale);
+ }
+ if (should_compute_output("Angle")) {
+ Result &result = get_result("Angle");
+ result.allocate_single_value();
+ result.set_float_value(angle);
+ }
+ }
+
+ GPUTexture *get_movie_clip_texture()
+ {
+ MovieClip *movie_clip = get_movie_clip();
+ MovieClipUser *movie_clip_user = get_movie_clip_user();
+ BKE_movieclip_user_set_frame(movie_clip_user, context().get_frame_number());
+ return BKE_movieclip_get_gpu_texture(movie_clip, movie_clip_user);
+ }
+
+ void free_movie_clip_texture()
+ {
+ MovieClip *movie_clip = get_movie_clip();
+ if (movie_clip) {
+ BKE_movieclip_free_gputexture(movie_clip);
+ }
+ }
+
+ MovieClip *get_movie_clip()
+ {
+ return (MovieClip *)bnode().id;
+ }
+
+ MovieClipUser *get_movie_clip_user()
+ {
+ return static_cast<MovieClipUser *>(bnode().storage);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new MovieClipOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_movieclip_cc
void register_node_type_cmp_movieclip()
@@ -91,6 +280,7 @@ void register_node_type_cmp_movieclip()
ntype.declare = file_ns::cmp_node_movieclip_declare;
ntype.draw_buttons = file_ns::node_composit_buts_movieclip;
ntype.draw_buttons_ex = file_ns::node_composit_buts_movieclip_ex;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.initfunc_api = file_ns::init;
ntype.flag |= NODE_PREVIEW;
node_type_storage(
diff --git a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc
index 9c6c6a40b2c..88638586594 100644
--- a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc
@@ -7,10 +7,13 @@
#include "BKE_context.h"
#include "BKE_lib_id.h"
+#include "BKE_tracking.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Translate ******************** */
@@ -80,6 +83,23 @@ static void node_composit_buts_moviedistortion(uiLayout *layout, bContext *C, Po
uiItemR(layout, ptr, "distortion_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class MovieDistortionOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new MovieDistortionOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_moviedistortion_cc
void register_node_type_cmp_moviedistortion()
@@ -94,6 +114,7 @@ void register_node_type_cmp_moviedistortion()
ntype.labelfunc = file_ns::label;
ntype.initfunc_api = file_ns::init;
node_type_storage(&ntype, nullptr, file_ns::storage_free, file_ns::storage_copy);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_normal.cc b/source/blender/nodes/composite/nodes/node_composite_normal.cc
index b4dd0bbacd0..a1a6303e21b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_normal.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_normal.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** NORMAL ******************** */
@@ -17,7 +21,8 @@ static void cmp_node_normal_declare(NodeDeclarationBuilder &b)
.default_value({0.0f, 0.0f, 1.0f})
.min(-1.0f)
.max(1.0f)
- .subtype(PROP_DIRECTION);
+ .subtype(PROP_DIRECTION)
+ .compositor_domain_priority(0);
b.add_output<decl::Vector>(N_("Normal"))
.default_value({0.0f, 0.0f, 1.0f})
.min(-1.0f)
@@ -26,6 +31,40 @@ static void cmp_node_normal_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Dot"));
}
+using namespace blender::realtime_compositor;
+
+class NormalShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_normal",
+ inputs,
+ outputs,
+ GPU_uniform(get_vector_value()));
+ }
+
+ /* The vector value is stored in the default value of the output socket. */
+ const float *get_vector_value()
+ {
+ return node()
+ .output_by_identifier("Normal")
+ ->default_value_typed<bNodeSocketValueVector>()
+ ->value;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new NormalShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_normal_cc
void register_node_type_cmp_normal()
@@ -36,6 +75,7 @@ void register_node_type_cmp_normal()
cmp_node_type_base(&ntype, CMP_NODE_NORMAL, "Normal", NODE_CLASS_OP_VECTOR);
ntype.declare = file_ns::cmp_node_normal_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_normalize.cc b/source/blender/nodes/composite/nodes/node_composite_normalize.cc
index 49318279bdb..21765825468 100644
--- a/source/blender/nodes/composite/nodes/node_composite_normalize.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_normalize.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** NORMALIZE single channel, useful for Z buffer ******************** */
@@ -17,6 +19,23 @@ static void cmp_node_normalize_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Value"));
}
+using namespace blender::realtime_compositor;
+
+class NormalizeOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Value").pass_through(get_result("Value"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new NormalizeOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_normalize_cc
void register_node_type_cmp_normalize()
@@ -27,6 +46,7 @@ void register_node_type_cmp_normalize()
cmp_node_type_base(&ntype, CMP_NODE_NORMALIZE, "Normalize", NODE_CLASS_OP_VECTOR);
ntype.declare = file_ns::cmp_node_normalize_declare;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_output_file.cc b/source/blender/nodes/composite/nodes/node_composite_output_file.cc
index 1fd6e62b4c5..5ed383977a5 100644
--- a/source/blender/nodes/composite/nodes/node_composite_output_file.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_output_file.cc
@@ -24,6 +24,8 @@
#include "IMB_openexr.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** OUTPUT FILE ******************** */
@@ -114,7 +116,7 @@ void ntreeCompositOutputFileUniqueLayer(ListBase *list,
bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree,
bNode *node,
const char *name,
- ImageFormatData *im_format)
+ const ImageFormatData *im_format)
{
NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage;
bNodeSocket *sock = nodeAddStaticSocket(
@@ -130,7 +132,8 @@ bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree,
ntreeCompositOutputFileUniqueLayer(&node->inputs, sock, name, '_');
if (im_format) {
- sockdata->format = *im_format;
+ BKE_image_format_copy(&sockdata->format, im_format);
+ sockdata->format.color_management = R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE;
if (BKE_imtype_is_movie(sockdata->format.imtype)) {
sockdata->format.imtype = R_IMF_IMTYPE_OPENEXR;
}
@@ -198,7 +201,8 @@ static void init_output_file(const bContext *C, PointerRNA *ptr)
RenderData *rd = &scene->r;
BLI_strncpy(nimf->base_path, rd->pic, sizeof(nimf->base_path));
- nimf->format = rd->im_format;
+ BKE_image_format_copy(&nimf->format, &rd->im_format);
+ nimf->format.color_management = R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE;
if (BKE_imtype_is_movie(nimf->format.imtype)) {
nimf->format.imtype = R_IMF_IMTYPE_OPENEXR;
}
@@ -437,6 +441,22 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
}
}
+using namespace blender::realtime_compositor;
+
+class OutputFileOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new OutputFileOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_output_file_cc
void register_node_type_cmp_output_file()
@@ -453,6 +473,7 @@ void register_node_type_cmp_output_file()
node_type_storage(
&ntype, "NodeImageMultiFile", file_ns::free_output_file, file_ns::copy_output_file);
node_type_update(&ntype, file_ns::update_output_file);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc
index 529aa0f84de..c4e42f8247d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc
@@ -5,6 +5,11 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+#include "BLI_math_vector.hh"
+
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Pixelate ******************** */
@@ -17,6 +22,49 @@ static void cmp_node_pixelate_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("Color"));
}
+using namespace blender::realtime_compositor;
+
+class PixelateOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ /* It might seems strange that the input is passed through without any processing, but note
+ * that the actual processing happens inside the domain realization input processor of the
+ * input. Indeed, the pixelate node merely realizes its input on a smaller-sized domain that
+ * matches its apparent size, that is, its size after the domain transformation. The pixelate
+ * node has no effect if the input is scaled-up. See the compute_domain method for more
+ * information. */
+ get_input("Color").pass_through(get_result("Color"));
+ }
+
+ /* Compute a smaller-sized domain that matches the apparent size of the input while having a unit
+ * scale transformation, see the execute method for more information. */
+ Domain compute_domain() override
+ {
+ Domain domain = get_input("Color").domain();
+
+ /* Get the scaling component of the domain transformation, but make sure it doesn't exceed 1,
+ * because pixelation should only happen if the input is scaled down. */
+ const float2 scale = math::min(float2(1.0f), domain.transformation.scale_2d());
+
+ /* Multiply the size of the domain by its scale to match its apparent size, but make sure it is
+ * at least 1 pixel in both axis. */
+ domain.size = math::max(int2(float2(domain.size) * scale), int2(1));
+
+ /* Reset the scale of the transformation by transforming it with the inverse of the scale. */
+ domain.transformation *= float3x3::from_scale(math::safe_divide(float2(1.0f), scale));
+
+ return domain;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new PixelateOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_pixelate_cc
void register_node_type_cmp_pixelate()
@@ -27,6 +75,7 @@ void register_node_type_cmp_pixelate()
cmp_node_type_base(&ntype, CMP_NODE_PIXELATE, "Pixelate", NODE_CLASS_OP_FILTER);
ntype.declare = file_ns::cmp_node_pixelate_declare;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc
index fb0c03579a2..68dc020a02e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc
@@ -5,12 +5,21 @@
* \ingroup cmpnodes
*/
+#include "DNA_movieclip_types.h"
+#include "DNA_tracking_types.h"
+
+#include "BKE_context.h"
+#include "BKE_lib_id.h"
+#include "BKE_tracking.h"
+
#include "RNA_access.h"
#include "RNA_prototypes.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_planetrackdeform_cc {
@@ -22,12 +31,33 @@ static void cmp_node_planetrackdeform_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Plane"));
}
-static void init(bNodeTree *UNUSED(ntree), bNode *node)
+static void init(const bContext *C, PointerRNA *ptr)
{
+ bNode *node = (bNode *)ptr->data;
+
NodePlaneTrackDeformData *data = MEM_cnew<NodePlaneTrackDeformData>(__func__);
data->motion_blur_samples = 16;
data->motion_blur_shutter = 0.5f;
node->storage = data;
+
+ const Scene *scene = CTX_data_scene(C);
+ if (scene->clip) {
+ MovieClip *clip = scene->clip;
+ MovieTracking *tracking = &clip->tracking;
+
+ node->id = &clip->id;
+ id_us_plus(&clip->id);
+
+ const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
+ BLI_strncpy(data->tracking_object, tracking_object->name, sizeof(data->tracking_object));
+
+ const MovieTrackingPlaneTrack *active_plane_track = BKE_tracking_plane_track_get_active(
+ tracking);
+ if (active_plane_track) {
+ BLI_strncpy(
+ data->plane_track_name, active_plane_track->name, sizeof(data->plane_track_name));
+ }
+ }
}
static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -79,6 +109,24 @@ static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, P
}
}
+using namespace blender::realtime_compositor;
+
+class PlaneTrackDeformOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Plane").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new PlaneTrackDeformOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_planetrackdeform_cc
void register_node_type_cmp_planetrackdeform()
@@ -90,9 +138,10 @@ void register_node_type_cmp_planetrackdeform()
cmp_node_type_base(&ntype, CMP_NODE_PLANETRACKDEFORM, "Plane Track Deform", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_planetrackdeform_declare;
ntype.draw_buttons = file_ns::node_composit_buts_planetrackdeform;
- node_type_init(&ntype, file_ns::init);
+ ntype.initfunc_api = file_ns::init;
node_type_storage(
&ntype, "NodePlaneTrackDeformData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_posterize.cc b/source/blender/nodes/composite/nodes/node_composite_posterize.cc
index c97035d55ea..1268219e7e2 100644
--- a/source/blender/nodes/composite/nodes/node_composite_posterize.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_posterize.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Posterize ******************** */
@@ -13,11 +17,37 @@ namespace blender::nodes::node_composite_posterize_cc {
static void cmp_node_posterize_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Steps")).default_value(8.0f).min(2.0f).max(1024.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Steps"))
+ .default_value(8.0f)
+ .min(2.0f)
+ .max(1024.0f)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class PosterizeShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_posterize", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new PosterizeShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_posterize_cc
void register_node_type_cmp_posterize()
@@ -28,6 +58,7 @@ void register_node_type_cmp_posterize()
cmp_node_type_base(&ntype, CMP_NODE_POSTERIZE, "Posterize", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_posterize_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc
index 000cc9df90a..c814ea5f738 100644
--- a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Premul and Key Alpha Convert ******************** */
@@ -16,7 +20,9 @@ namespace blender::nodes::node_composite_premulkey_cc {
static void cmp_node_premulkey_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -25,6 +31,36 @@ static void node_composit_buts_premulkey(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "mapping", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class AlphaConvertShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ if (get_mode() == 0) {
+ GPU_stack_link(material, &bnode(), "color_alpha_premultiply", inputs, outputs);
+ return;
+ }
+
+ GPU_stack_link(material, &bnode(), "color_alpha_unpremultiply", inputs, outputs);
+ }
+
+ CMPNodeAlphaConvertMode get_mode()
+ {
+ return (CMPNodeAlphaConvertMode)bnode().custom1;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new AlphaConvertShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_premulkey_cc
void register_node_type_cmp_premulkey()
@@ -36,6 +72,7 @@ void register_node_type_cmp_premulkey()
cmp_node_type_base(&ntype, CMP_NODE_PREMULKEY, "Alpha Convert", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_premulkey_declare;
ntype.draw_buttons = file_ns::node_composit_buts_premulkey;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_rgb.cc
index 5bc4c67dd8e..f107961f301 100644
--- a/source/blender/nodes/composite/nodes/node_composite_rgb.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_rgb.cc
@@ -5,6 +5,12 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+
+#include "DNA_node_types.h"
+
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** RGB ******************** */
@@ -16,6 +22,29 @@ static void cmp_node_rgb_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("RGBA")).default_value({0.5f, 0.5f, 0.5f, 1.0f});
}
+using namespace blender::realtime_compositor;
+
+class RGBOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &result = get_result("RGBA");
+ result.allocate_single_value();
+
+ const bNodeSocket *socket = static_cast<const bNodeSocket *>(bnode().outputs.first);
+ float4 color = float4(static_cast<const bNodeSocketValueRGBA *>(socket->default_value)->value);
+
+ result.set_color_value(color);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new RGBOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_rgb_cc
void register_node_type_cmp_rgb()
@@ -27,6 +56,7 @@ void register_node_type_cmp_rgb()
cmp_node_type_base(&ntype, CMP_NODE_RGB, "RGB", NODE_CLASS_INPUT);
ntype.declare = file_ns::cmp_node_rgb_declare;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_rotate.cc b/source/blender/nodes/composite/nodes/node_composite_rotate.cc
index a083bc1837b..35caa3cd242 100644
--- a/source/blender/nodes/composite/nodes/node_composite_rotate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_rotate.cc
@@ -5,9 +5,14 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+#include "BLI_float3x3.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Rotate ******************** */
@@ -16,12 +21,15 @@ namespace blender::nodes::node_composite_rotate_cc {
static void cmp_node_rotate_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_input<decl::Float>(N_("Degr"))
.default_value(0.0f)
.min(-10000.0f)
.max(10000.0f)
- .subtype(PROP_ANGLE);
+ .subtype(PROP_ANGLE)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
@@ -35,6 +43,47 @@ static void node_composit_buts_rotate(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class RotateOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+
+ const float rotation = get_input("Degr").get_float_value_default(0.0f);
+
+ const float3x3 transformation = float3x3::from_rotation(rotation);
+
+ result.transform(transformation);
+ result.get_realization_options().interpolation = get_interpolation();
+ }
+
+ Interpolation get_interpolation()
+ {
+ switch (bnode().custom1) {
+ case 0:
+ return Interpolation::Nearest;
+ case 1:
+ return Interpolation::Bilinear;
+ case 2:
+ return Interpolation::Bicubic;
+ }
+
+ BLI_assert_unreachable();
+ return Interpolation::Nearest;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new RotateOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_rotate_cc
void register_node_type_cmp_rotate()
@@ -47,6 +96,7 @@ void register_node_type_cmp_rotate()
ntype.declare = file_ns::cmp_node_rotate_declare;
ntype.draw_buttons = file_ns::node_composit_buts_rotate;
node_type_init(&ntype, file_ns::node_composit_init_rotate);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_scale.cc b/source/blender/nodes/composite/nodes/node_composite_scale.cc
index b2b42a3613c..eb2d7162c69 100644
--- a/source/blender/nodes/composite/nodes/node_composite_scale.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_scale.cc
@@ -5,11 +5,18 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+#include "BLI_float3x3.hh"
+#include "BLI_math_base.hh"
+#include "BLI_math_vec_types.hh"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Scale ******************** */
@@ -18,16 +25,26 @@ namespace blender::nodes::node_composite_scale_cc {
static void cmp_node_scale_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("X")).default_value(1.0f).min(0.0001f).max(CMP_SCALE_MAX);
- b.add_input<decl::Float>(N_("Y")).default_value(1.0f).min(0.0001f).max(CMP_SCALE_MAX);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("X"))
+ .default_value(1.0f)
+ .min(0.0001f)
+ .max(CMP_SCALE_MAX)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Y"))
+ .default_value(1.0f)
+ .min(0.0001f)
+ .max(CMP_SCALE_MAX)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
static void node_composite_update_scale(bNodeTree *ntree, bNode *node)
{
bNodeSocket *sock;
- bool use_xy_scale = ELEM(node->custom1, CMP_SCALE_RELATIVE, CMP_SCALE_ABSOLUTE);
+ bool use_xy_scale = ELEM(node->custom1, CMP_NODE_SCALE_RELATIVE, CMP_NODE_SCALE_ABSOLUTE);
/* Only show X/Y scale factor inputs for modes using them! */
for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
@@ -41,7 +58,7 @@ static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), Poin
{
uiItemR(layout, ptr, "space", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
- if (RNA_enum_get(ptr, "space") == CMP_SCALE_RENDERPERCENT) {
+ if (RNA_enum_get(ptr, "space") == CMP_NODE_SCALE_RENDER_SIZE) {
uiLayout *row;
uiItemR(layout,
ptr,
@@ -55,6 +72,145 @@ static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), Poin
}
}
+using namespace blender::realtime_compositor;
+
+class ScaleOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+
+ const float3x3 transformation = float3x3::from_translation_rotation_scale(
+ get_translation(), 0.0f, get_scale());
+
+ result.transform(transformation);
+ result.get_realization_options().interpolation = Interpolation::Bilinear;
+ }
+
+ float2 get_scale()
+ {
+ switch (get_scale_method()) {
+ case CMP_NODE_SCALE_RELATIVE:
+ return get_scale_relative();
+ case CMP_NODE_SCALE_ABSOLUTE:
+ return get_scale_absolute();
+ case CMP_NODE_SCALE_RENDER_PERCENT:
+ return get_scale_render_percent();
+ case CMP_NODE_SCALE_RENDER_SIZE:
+ return get_scale_render_size();
+ default:
+ BLI_assert_unreachable();
+ return float2(1.0f);
+ }
+ }
+
+ /* Scale by the input factors. */
+ float2 get_scale_relative()
+ {
+ return float2(get_input("X").get_float_value_default(1.0f),
+ get_input("Y").get_float_value_default(1.0f));
+ }
+
+ /* Scale such that the new size matches the input absolute size. */
+ float2 get_scale_absolute()
+ {
+ const float2 input_size = float2(get_input("Image").domain().size);
+ const float2 absolute_size = float2(get_input("X").get_float_value_default(1.0f),
+ get_input("Y").get_float_value_default(1.0f));
+ return absolute_size / input_size;
+ }
+
+ /* Scale by the render resolution percentage. */
+ float2 get_scale_render_percent()
+ {
+ return float2(context().get_scene()->r.size / 100.0f);
+ }
+
+ float2 get_scale_render_size()
+ {
+ switch (get_scale_render_size_method()) {
+ case CMP_NODE_SCALE_RENDER_SIZE_STRETCH:
+ return get_scale_render_size_stretch();
+ case CMP_NODE_SCALE_RENDER_SIZE_FIT:
+ return get_scale_render_size_fit();
+ case CMP_NODE_SCALE_RENDER_SIZE_CROP:
+ return get_scale_render_size_crop();
+ default:
+ BLI_assert_unreachable();
+ return float2(1.0f);
+ }
+ }
+
+ /* Scale such that the new size matches the render size. Since the input is freely scaled, it is
+ * potentially stretched, hence the name. */
+ float2 get_scale_render_size_stretch()
+ {
+ const float2 input_size = float2(get_input("Image").domain().size);
+ const float2 render_size = float2(context().get_output_size());
+ return render_size / input_size;
+ }
+
+ /* Scale such that the dimension with the smaller scaling factor matches that of the render size
+ * while maintaining the input's aspect ratio. Since the other dimension is guaranteed not to
+ * exceed the render size region due to its larger scaling factor, the image is said to be fit
+ * inside that region, hence the name. */
+ float2 get_scale_render_size_fit()
+ {
+ const float2 input_size = float2(get_input("Image").domain().size);
+ const float2 render_size = float2(context().get_output_size());
+ const float2 scale = render_size / input_size;
+ return float2(math::min(scale.x, scale.y));
+ }
+
+ /* Scale such that the dimension with the larger scaling factor matches that of the render size
+ * while maintaining the input's aspect ratio. Since the other dimension is guaranteed to exceed
+ * the render size region due to its lower scaling factor, the image will be cropped inside that
+ * region, hence the name. */
+ float2 get_scale_render_size_crop()
+ {
+ const float2 input_size = float2(get_input("Image").domain().size);
+ const float2 render_size = float2(context().get_output_size());
+ const float2 scale = render_size / input_size;
+ return float2(math::max(scale.x, scale.y));
+ }
+
+ float2 get_translation()
+ {
+ /* Only the render size option supports offset translation. */
+ if (get_scale_method() != CMP_NODE_SCALE_RENDER_SIZE) {
+ return float2(0.0f);
+ }
+
+ /* Translate by the offset factor relative to the new size. */
+ const float2 input_size = float2(get_input("Image").domain().size);
+ return get_offset() * input_size * get_scale();
+ }
+
+ CMPNodeScaleMethod get_scale_method()
+ {
+ return (CMPNodeScaleMethod)bnode().custom1;
+ }
+
+ CMPNodeScaleRenderSizeMethod get_scale_render_size_method()
+ {
+ return (CMPNodeScaleRenderSizeMethod)bnode().custom2;
+ }
+
+ float2 get_offset()
+ {
+ return float2(bnode().custom3, bnode().custom4);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ScaleOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_scale_cc
void register_node_type_cmp_scale()
@@ -67,6 +223,7 @@ void register_node_type_cmp_scale()
ntype.declare = file_ns::cmp_node_scale_declare;
ntype.draw_buttons = file_ns::node_composit_buts_scale;
node_type_update(&ntype, file_ns::node_composite_update_scale);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_scene_time.cc b/source/blender/nodes/composite/nodes/node_composite_scene_time.cc
index 20bafb0d3d4..1f5317378bb 100644
--- a/source/blender/nodes/composite/nodes/node_composite_scene_time.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_scene_time.cc
@@ -3,6 +3,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes {
@@ -13,6 +15,38 @@ static void cmp_node_scene_time_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Frame"));
}
+using namespace blender::realtime_compositor;
+
+class SceneTimeOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ execute_seconds();
+ execute_frame();
+ }
+
+ void execute_seconds()
+ {
+ Result &result = get_result("Seconds");
+ result.allocate_single_value();
+ result.set_float_value(context().get_time());
+ }
+
+ void execute_frame()
+ {
+ Result &result = get_result("Frame");
+ result.allocate_single_value();
+ result.set_float_value(static_cast<float>(context().get_frame_number()));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new SceneTimeOperation(context, node);
+}
+
} // namespace blender::nodes
void register_node_type_cmp_scene_time()
@@ -21,5 +55,7 @@ void register_node_type_cmp_scene_time()
cmp_node_type_base(&ntype, CMP_NODE_SCENE_TIME, "Scene Time", NODE_CLASS_INPUT);
ntype.declare = blender::nodes::cmp_node_scene_time_declare;
+ ntype.get_compositor_operation = blender::nodes::get_compositor_operation;
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc
index b253656a628..f6792d7ce61 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc
@@ -1,5 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BLI_assert.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
static void node_cmp_combsep_color_init(bNodeTree *UNUSED(ntree), bNode *node)
@@ -56,9 +62,13 @@ static void node_cmp_combsep_color_label(const ListBase *sockets, CMPNodeCombSep
namespace blender::nodes::node_composite_separate_color_cc {
+NODE_STORAGE_FUNCS(NodeCMPCombSepColor)
+
static void cmp_node_separate_color_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Red"));
b.add_output<decl::Float>(N_("Green"));
b.add_output<decl::Float>(N_("Blue"));
@@ -71,6 +81,52 @@ static void cmp_node_separate_color_update(bNodeTree *UNUSED(ntree), bNode *node
node_cmp_combsep_color_label(&node->outputs, (CMPNodeCombSepColorMode)storage->mode);
}
+using namespace blender::realtime_compositor;
+
+class SeparateColorShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (node_storage(bnode()).mode) {
+ case CMP_NODE_COMBSEP_COLOR_RGB:
+ return "node_composite_separate_rgba";
+ case CMP_NODE_COMBSEP_COLOR_HSV:
+ return "node_composite_separate_hsva";
+ case CMP_NODE_COMBSEP_COLOR_HSL:
+ return "node_composite_separate_hsla";
+ case CMP_NODE_COMBSEP_COLOR_YUV:
+ return "node_composite_separate_yuva_itu_709";
+ case CMP_NODE_COMBSEP_COLOR_YCC:
+ switch (node_storage(bnode()).ycc_mode) {
+ case BLI_YCC_ITU_BT601:
+ return "node_composite_separate_ycca_itu_601";
+ case BLI_YCC_ITU_BT709:
+ return "node_composite_separate_ycca_itu_709";
+ case BLI_YCC_JFIF_0_255:
+ return "node_composite_separate_ycca_jpeg";
+ }
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateColorShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_separate_color_cc
void register_node_type_cmp_separate_color()
@@ -85,6 +141,7 @@ void register_node_type_cmp_separate_color()
node_type_storage(
&ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage);
node_type_update(&ntype, file_ns::cmp_node_separate_color_update);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
@@ -93,24 +150,34 @@ void register_node_type_cmp_separate_color()
namespace blender::nodes::node_composite_combine_color_cc {
+NODE_STORAGE_FUNCS(NodeCMPCombSepColor)
+
static void cmp_node_combine_color_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Red")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Float>(N_("Red"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(0);
b.add_input<decl::Float>(N_("Green"))
.default_value(0.0f)
.min(0.0f)
.max(1.0f)
- .subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
b.add_input<decl::Float>(N_("Blue"))
.default_value(0.0f)
.min(0.0f)
.max(1.0f)
- .subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(2);
b.add_input<decl::Float>(N_("Alpha"))
.default_value(1.0f)
.min(0.0f)
.max(1.0f)
- .subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
@@ -120,6 +187,52 @@ static void cmp_node_combine_color_update(bNodeTree *UNUSED(ntree), bNode *node)
node_cmp_combsep_color_label(&node->inputs, (CMPNodeCombSepColorMode)storage->mode);
}
+using namespace blender::realtime_compositor;
+
+class CombineColorShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (node_storage(bnode()).mode) {
+ case CMP_NODE_COMBSEP_COLOR_RGB:
+ return "node_composite_combine_rgba";
+ case CMP_NODE_COMBSEP_COLOR_HSV:
+ return "node_composite_combine_hsva";
+ case CMP_NODE_COMBSEP_COLOR_HSL:
+ return "node_composite_combine_hsla";
+ case CMP_NODE_COMBSEP_COLOR_YUV:
+ return "node_composite_combine_yuva_itu_709";
+ case CMP_NODE_COMBSEP_COLOR_YCC:
+ switch (node_storage(bnode()).ycc_mode) {
+ case BLI_YCC_ITU_BT601:
+ return "node_composite_combine_ycca_itu_601";
+ case BLI_YCC_ITU_BT709:
+ return "node_composite_combine_ycca_itu_709";
+ case BLI_YCC_JFIF_0_255:
+ return "node_composite_combine_ycca_jpeg";
+ }
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineColorShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_combine_color_cc
void register_node_type_cmp_combine_color()
@@ -134,6 +247,7 @@ void register_node_type_cmp_combine_color()
node_type_storage(
&ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage);
node_type_update(&ntype, file_ns::cmp_node_combine_color_update);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc
index a0d2485ea5a..b655c0db106 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc
@@ -5,57 +5,112 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE HSVA ******************** */
-namespace blender::nodes::node_composite_sepcomb_hsva_cc {
+namespace blender::nodes::node_composite_separate_hsva_cc {
static void cmp_node_sephsva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("H"));
b.add_output<decl::Float>(N_("S"));
b.add_output<decl::Float>(N_("V"));
b.add_output<decl::Float>(N_("A"));
}
-} // namespace blender::nodes::node_composite_sepcomb_hsva_cc
+using namespace blender::realtime_compositor;
+
+class SeparateHSVAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_separate_hsva", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateHSVAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_hsva_cc
void register_node_type_cmp_sephsva()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_hsva_cc;
+ namespace file_ns = blender::nodes::node_composite_separate_hsva_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA_LEGACY, "Separate HSVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_sephsva_declare;
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
+
nodeRegisterType(&ntype);
}
/* **************** COMBINE HSVA ******************** */
-namespace blender::nodes::node_composite_sepcomb_hsva_cc {
+namespace blender::nodes::node_composite_combine_hsva_cc {
static void cmp_node_combhsva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("H")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("S")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("H")).min(0.0f).max(1.0f).compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("S")).min(0.0f).max(1.0f).compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f).compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("A"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
-} // namespace blender::nodes::node_composite_sepcomb_hsva_cc
+using namespace blender::realtime_compositor;
+
+class CombineHSVAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_combine_hsva", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineHSVAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_hsva_cc
void register_node_type_cmp_combhsva()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_hsva_cc;
+ namespace file_ns = blender::nodes::node_composite_combine_hsva_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA_LEGACY, "Combine HSVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combhsva_declare;
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc
index ae46681b0f4..1f4c9fd153f 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc
@@ -5,57 +5,112 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE RGBA ******************** */
-namespace blender::nodes::node_composite_sepcomb_rgba_cc {
+
+namespace blender::nodes::node_composite_separate_rgba_cc {
static void cmp_node_seprgba_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("R"));
b.add_output<decl::Float>(N_("G"));
b.add_output<decl::Float>(N_("B"));
b.add_output<decl::Float>(N_("A"));
}
-} // namespace blender::nodes::node_composite_sepcomb_rgba_cc
+using namespace blender::realtime_compositor;
+
+class SeparateRGBAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_separate_rgba", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateRGBAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_rgba_cc
void register_node_type_cmp_seprgba()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_rgba_cc;
+ namespace file_ns = blender::nodes::node_composite_separate_rgba_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA_LEGACY, "Separate RGBA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_seprgba_declare;
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** COMBINE RGBA ******************** */
-namespace blender::nodes::node_composite_sepcomb_rgba_cc {
+namespace blender::nodes::node_composite_combine_rgba_cc {
static void cmp_node_combrgba_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("R")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("G")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("B")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("R")).min(0.0f).max(1.0f).compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("G")).min(0.0f).max(1.0f).compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("B")).min(0.0f).max(1.0f).compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("A"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
-} // namespace blender::nodes::node_composite_sepcomb_rgba_cc
+using namespace blender::realtime_compositor;
+
+class CombineRGBAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_combine_rgba", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineRGBAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_rgba_cc
void register_node_type_cmp_combrgba()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_rgba_cc;
+ namespace file_ns = blender::nodes::node_composite_combine_rgba_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA_LEGACY, "Combine RGBA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combrgba_declare;
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc
index 4979c376cab..e288e698808 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc
@@ -5,10 +5,15 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE XYZ ******************** */
-namespace blender::nodes {
+
+namespace blender::nodes::node_composite_separate_xyz_cc {
static void cmp_node_separate_xyz_declare(NodeDeclarationBuilder &b)
{
@@ -18,21 +23,44 @@ static void cmp_node_separate_xyz_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>("Z");
}
-} // namespace blender::nodes
+using namespace blender::realtime_compositor;
+
+class SeparateXYZShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_separate_xyz", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateXYZShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_xyz_cc
void register_node_type_cmp_separate_xyz()
{
+ namespace file_ns = blender::nodes::node_composite_separate_xyz_cc;
+
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPARATE_XYZ, "Separate XYZ", NODE_CLASS_CONVERTER);
- ntype.declare = blender::nodes::cmp_node_separate_xyz_declare;
+ ntype.declare = file_ns::cmp_node_separate_xyz_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** COMBINE XYZ ******************** */
-namespace blender::nodes {
+namespace blender::nodes::node_composite_combine_xyz_cc {
static void cmp_node_combine_xyz_declare(NodeDeclarationBuilder &b)
{
@@ -42,14 +70,37 @@ static void cmp_node_combine_xyz_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>("Vector");
}
-} // namespace blender::nodes
+using namespace blender::realtime_compositor;
+
+class CombineXYZShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_combine_xyz", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineXYZShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_xyz_cc
void register_node_type_cmp_combine_xyz()
{
+ namespace file_ns = blender::nodes::node_composite_combine_xyz_cc;
+
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBINE_XYZ, "Combine XYZ", NODE_CLASS_CONVERTER);
- ntype.declare = blender::nodes::cmp_node_combine_xyz_declare;
+ ntype.declare = file_ns::cmp_node_combine_xyz_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc
index a3c40b61e64..bebe6abe115 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc
@@ -5,15 +5,23 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE YCCA ******************** */
-namespace blender::nodes::node_composite_sepcomb_ycca_cc {
+namespace blender::nodes::node_composite_separate_ycca_cc {
static void cmp_node_sepycca_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Y"));
b.add_output<decl::Float>(N_("Cb"));
b.add_output<decl::Float>(N_("Cr"));
@@ -25,31 +33,85 @@ static void node_composit_init_mode_sepycca(bNodeTree *UNUSED(ntree), bNode *nod
node->custom1 = 1; /* BLI_YCC_ITU_BT709 */
}
-} // namespace blender::nodes::node_composite_sepcomb_ycca_cc
+using namespace blender::realtime_compositor;
+
+class SeparateYCCAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+ }
+
+ int get_mode()
+ {
+ return bnode().custom1;
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (get_mode()) {
+ case BLI_YCC_ITU_BT601:
+ return "node_composite_separate_ycca_itu_601";
+ case BLI_YCC_ITU_BT709:
+ return "node_composite_separate_ycca_itu_709";
+ case BLI_YCC_JFIF_0_255:
+ return "node_composite_separate_ycca_jpeg";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateYCCAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_ycca_cc
void register_node_type_cmp_sepycca()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_ycca_cc;
+ namespace file_ns = blender::nodes::node_composite_separate_ycca_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA_LEGACY, "Separate YCbCrA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_sepycca_declare;
node_type_init(&ntype, file_ns::node_composit_init_mode_sepycca);
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** COMBINE YCCA ******************** */
-namespace blender::nodes::node_composite_sepcomb_ycca_cc {
+namespace blender::nodes::node_composite_combine_ycca_cc {
static void cmp_node_combycca_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("Cb")).default_value(0.5f).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("Cr")).default_value(0.5f).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f).compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Cb"))
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("Cr"))
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("A"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
@@ -58,17 +120,59 @@ static void node_composit_init_mode_combycca(bNodeTree *UNUSED(ntree), bNode *no
node->custom1 = 1; /* BLI_YCC_ITU_BT709 */
}
-} // namespace blender::nodes::node_composite_sepcomb_ycca_cc
+using namespace blender::realtime_compositor;
+
+class CombineYCCAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+ }
+
+ int get_mode()
+ {
+ return bnode().custom1;
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (get_mode()) {
+ case BLI_YCC_ITU_BT601:
+ return "node_composite_combine_ycca_itu_601";
+ case BLI_YCC_ITU_BT709:
+ return "node_composite_combine_ycca_itu_709";
+ case BLI_YCC_JFIF_0_255:
+ return "node_composite_combine_ycca_jpeg";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineYCCAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_ycca_cc
void register_node_type_cmp_combycca()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_ycca_cc;
+ namespace file_ns = blender::nodes::node_composite_combine_ycca_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA_LEGACY, "Combine YCbCrA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combycca_declare;
node_type_init(&ntype, file_ns::node_composit_init_mode_combycca);
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc
index 7fdece5904d..1f0eb04cfc3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc
@@ -5,58 +5,112 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE YUVA ******************** */
-namespace blender::nodes::node_composite_sepcomb_yuva_cc {
+namespace blender::nodes::node_composite_separate_yuva_cc {
static void cmp_node_sepyuva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Y"));
b.add_output<decl::Float>(N_("U"));
b.add_output<decl::Float>(N_("V"));
b.add_output<decl::Float>(N_("A"));
}
-} // namespace blender::nodes::node_composite_sepcomb_yuva_cc
+using namespace blender::realtime_compositor;
+
+class SeparateYUVAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_separate_yuva_itu_709", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateYUVAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_yuva_cc
void register_node_type_cmp_sepyuva()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_yuva_cc;
+ namespace file_ns = blender::nodes::node_composite_separate_yuva_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA_LEGACY, "Separate YUVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_sepyuva_declare;
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** COMBINE YUVA ******************** */
-namespace blender::nodes::node_composite_sepcomb_yuva_cc {
+namespace blender::nodes::node_composite_combine_yuva_cc {
static void cmp_node_combyuva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("U")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f).compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("U")).min(0.0f).max(1.0f).compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f).compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("A"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
-} // namespace blender::nodes::node_composite_sepcomb_yuva_cc
+using namespace blender::realtime_compositor;
+
+class CombineYUVAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_combine_yuva_itu_709", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineYUVAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_yuva_cc
void register_node_type_cmp_combyuva()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_yuva_cc;
+ namespace file_ns = blender::nodes::node_composite_combine_yuva_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA_LEGACY, "Combine YUVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combyuva_declare;
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
index 8aeaafbbf67..df3aca2c665 100644
--- a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
@@ -8,16 +8,28 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SET ALPHA ******************** */
namespace blender::nodes::node_composite_setalpha_cc {
+NODE_STORAGE_FUNCS(NodeSetAlpha)
+
static void cmp_node_setalpha_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Alpha"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -33,6 +45,31 @@ static void node_composit_buts_set_alpha(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "mode", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class SetAlphaShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ if (node_storage(bnode()).mode == CMP_NODE_SETALPHA_MODE_APPLY) {
+ GPU_stack_link(material, &bnode(), "node_composite_set_alpha_apply", inputs, outputs);
+ return;
+ }
+
+ GPU_stack_link(material, &bnode(), "node_composite_set_alpha_replace", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SetAlphaShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_setalpha_cc
void register_node_type_cmp_setalpha()
@@ -47,6 +84,7 @@ void register_node_type_cmp_setalpha()
node_type_init(&ntype, file_ns::node_composit_init_setalpha);
node_type_storage(
&ntype, "NodeSetAlpha", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc
index ab325c4559f..085de69e63e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc
@@ -11,6 +11,12 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** SPLIT VIEWER ******************** */
@@ -43,6 +49,70 @@ static void node_composit_buts_splitviewer(uiLayout *layout, bContext *UNUSED(C)
uiItemR(col, ptr, "factor", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ViewerOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUShader *shader = get_split_viewer_shader();
+ GPU_shader_bind(shader);
+
+ const int2 size = compute_domain().size;
+
+ GPU_shader_uniform_1f(shader, "split_ratio", get_split_ratio());
+ GPU_shader_uniform_2iv(shader, "view_size", size);
+
+ const Result &first_image = get_input("Image");
+ first_image.bind_as_texture(shader, "first_image_tx");
+ const Result &second_image = get_input("Image_001");
+ second_image.bind_as_texture(shader, "second_image_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ first_image.unbind_as_texture();
+ second_image.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* The operation domain have the same dimensions of the output without any transformations. */
+ Domain compute_domain() override
+ {
+ return Domain(context().get_output_size());
+ }
+
+ GPUShader *get_split_viewer_shader()
+ {
+ if (get_split_axis() == CMP_NODE_SPLIT_VIEWER_HORIZONTAL) {
+ return shader_manager().get("compositor_split_viewer_horizontal");
+ }
+
+ return shader_manager().get("compositor_split_viewer_vertical");
+ }
+
+ CMPNodeSplitViewerAxis get_split_axis()
+ {
+ return (CMPNodeSplitViewerAxis)bnode().custom2;
+ }
+
+ float get_split_ratio()
+ {
+ return bnode().custom1 / 100.0f;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ViewerOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_split_viewer_cc
void register_node_type_cmp_splitviewer()
@@ -57,6 +127,7 @@ void register_node_type_cmp_splitviewer()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_splitviewer);
node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.no_muting = true;
diff --git a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc
index 63d00a0864b..75a96a05863 100644
--- a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc
@@ -11,6 +11,8 @@
#include "BKE_context.h"
#include "BKE_lib_id.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Stabilize 2D ******************** */
@@ -58,6 +60,23 @@ static void node_composit_buts_stabilize2d(uiLayout *layout, bContext *C, Pointe
uiItemR(layout, ptr, "invert", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class Stabilize2DOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new Stabilize2DOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_stabilize2d_cc
void register_node_type_cmp_stabilize2d()
@@ -70,6 +89,7 @@ void register_node_type_cmp_stabilize2d()
ntype.declare = file_ns::cmp_node_stabilize2d_declare;
ntype.draw_buttons = file_ns::node_composit_buts_stabilize2d;
ntype.initfunc_api = file_ns::init;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc
index 766f26745ef..4b9264d7e35 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_sunbeams_cc {
@@ -38,6 +40,23 @@ static void node_composit_buts_sunbeams(uiLayout *layout, bContext *UNUSED(C), P
ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class SunBeamsOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new SunBeamsOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_sunbeams_cc
void register_node_type_cmp_sunbeams()
@@ -52,6 +71,7 @@ void register_node_type_cmp_sunbeams()
node_type_init(&ntype, file_ns::init);
node_type_storage(
&ntype, "NodeSunBeams", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_switch.cc b/source/blender/nodes/composite/nodes/node_composite_switch.cc
index bda490572e9..767802cc442 100644
--- a/source/blender/nodes/composite/nodes/node_composite_switch.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_switch.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Switch ******************** */
@@ -26,6 +28,30 @@ static void node_composit_buts_switch(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "check", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class SwitchOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input(get_condition() ? "On" : "Off");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+ }
+
+ bool get_condition()
+ {
+ return bnode().custom1;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new SwitchOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_switch_cc
void register_node_type_cmp_switch()
@@ -38,5 +64,7 @@ void register_node_type_cmp_switch()
ntype.declare = file_ns::cmp_node_switch_declare;
ntype.draw_buttons = file_ns::node_composit_buts_switch;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_switchview.cc b/source/blender/nodes/composite/nodes/node_composite_switchview.cc
index 2cf3da03a05..e74c3b6007a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_switchview.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_switchview.cc
@@ -11,6 +11,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** SWITCH VIEW ******************** */
@@ -140,6 +142,25 @@ static void node_composit_buts_switch_view_ex(uiLayout *layout,
nullptr);
}
+using namespace blender::realtime_compositor;
+
+class SwitchViewOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input(context().get_view_name());
+ Result &result = get_result("Image");
+ input.pass_through(result);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new SwitchViewOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_switchview_cc
void register_node_type_cmp_switch_view()
@@ -153,6 +174,7 @@ void register_node_type_cmp_switch_view()
ntype.draw_buttons_ex = file_ns::node_composit_buts_switch_view_ex;
ntype.initfunc_api = file_ns::init_switch_view;
node_type_update(&ntype, file_ns::cmp_node_switch_view_update);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_texture.cc b/source/blender/nodes/composite/nodes/node_composite_texture.cc
index 7571e97a2cd..5a628aae7a7 100644
--- a/source/blender/nodes/composite/nodes/node_composite_texture.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_texture.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** TEXTURE ******************** */
@@ -23,6 +25,24 @@ static void cmp_node_texture_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("Color"));
}
+using namespace blender::realtime_compositor;
+
+class TextureOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("Value").allocate_invalid();
+ get_result("Color").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TextureOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_texture_cc
void register_node_type_cmp_texture()
@@ -34,6 +54,7 @@ void register_node_type_cmp_texture()
cmp_node_type_base(&ntype, CMP_NODE_TEXTURE, "Texture", NODE_CLASS_INPUT);
ntype.declare = file_ns::cmp_node_texture_declare;
ntype.flag |= NODE_PREVIEW;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc
index cdfe97b038d..4cc3d4f32a3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_tonemap_cc {
@@ -58,6 +60,23 @@ static void node_composit_buts_tonemap(uiLayout *layout, bContext *UNUSED(C), Po
}
}
+using namespace blender::realtime_compositor;
+
+class ToneMapOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ToneMapOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_tonemap_cc
void register_node_type_cmp_tonemap()
@@ -71,6 +90,7 @@ void register_node_type_cmp_tonemap()
ntype.draw_buttons = file_ns::node_composit_buts_tonemap;
node_type_init(&ntype, file_ns::node_composit_init_tonemap);
node_type_storage(&ntype, "NodeTonemap", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
index 17a086f306f..0e9bd800f44 100644
--- a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
@@ -5,12 +5,21 @@
* \ingroup cmpnodes
*/
+#include "DNA_movieclip_types.h"
+#include "DNA_tracking_types.h"
+
+#include "BKE_context.h"
+#include "BKE_lib_id.h"
+#include "BKE_tracking.h"
+
#include "RNA_access.h"
#include "RNA_prototypes.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_trackpos_cc {
@@ -22,11 +31,29 @@ static void cmp_node_trackpos_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>(N_("Speed")).subtype(PROP_VELOCITY);
}
-static void init(bNodeTree *UNUSED(ntree), bNode *node)
+static void init(const bContext *C, PointerRNA *ptr)
{
- NodeTrackPosData *data = MEM_cnew<NodeTrackPosData>(__func__);
+ bNode *node = (bNode *)ptr->data;
+ NodeTrackPosData *data = MEM_cnew<NodeTrackPosData>(__func__);
node->storage = data;
+
+ const Scene *scene = CTX_data_scene(C);
+ if (scene->clip) {
+ MovieClip *clip = scene->clip;
+ MovieTracking *tracking = &clip->tracking;
+
+ node->id = &clip->id;
+ id_us_plus(&clip->id);
+
+ const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
+ BLI_strncpy(data->tracking_object, tracking_object->name, sizeof(data->tracking_object));
+
+ const MovieTrackingTrack *active_track = BKE_tracking_track_get_active(tracking);
+ if (active_track) {
+ BLI_strncpy(data->track_name, active_track->name, sizeof(data->track_name));
+ }
+ }
}
static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -77,6 +104,25 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN
}
}
+using namespace blender::realtime_compositor;
+
+class TrackPositionOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("X").allocate_invalid();
+ get_result("Y").allocate_invalid();
+ get_result("Speed").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TrackPositionOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_trackpos_cc
void register_node_type_cmp_trackpos()
@@ -88,9 +134,10 @@ void register_node_type_cmp_trackpos()
cmp_node_type_base(&ntype, CMP_NODE_TRACKPOS, "Track Position", NODE_CLASS_INPUT);
ntype.declare = file_ns::cmp_node_trackpos_declare;
ntype.draw_buttons = file_ns::node_composit_buts_trackpos;
- node_type_init(&ntype, file_ns::init);
+ ntype.initfunc_api = file_ns::init;
node_type_storage(
&ntype, "NodeTrackPosData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_transform.cc b/source/blender/nodes/composite/nodes/node_composite_transform.cc
index fe72f5e33ca..7c5866d2d06 100644
--- a/source/blender/nodes/composite/nodes/node_composite_transform.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_transform.cc
@@ -5,9 +5,15 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+#include "BLI_float3x3.hh"
+#include "BLI_math_vector.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Transform ******************** */
@@ -16,15 +22,30 @@ namespace blender::nodes::node_composite_transform_cc {
static void cmp_node_transform_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_input<decl::Float>(N_("X")).default_value(0.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("Y")).default_value(0.0f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({0.8f, 0.8f, 0.8f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("X"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Y"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_expects_single_value();
b.add_input<decl::Float>(N_("Angle"))
.default_value(0.0f)
.min(-10000.0f)
.max(10000.0f)
- .subtype(PROP_ANGLE);
- b.add_input<decl::Float>(N_("Scale")).default_value(1.0f).min(0.0001f).max(CMP_SCALE_MAX);
+ .subtype(PROP_ANGLE)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Scale"))
+ .default_value(1.0f)
+ .min(0.0001f)
+ .max(CMP_SCALE_MAX)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
@@ -33,6 +54,51 @@ static void node_composit_buts_transform(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class TransformOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+
+ const float2 translation = float2(get_input("X").get_float_value_default(0.0f),
+ get_input("Y").get_float_value_default(0.0f));
+ const float rotation = get_input("Angle").get_float_value_default(0.0f);
+ const float2 scale = float2(get_input("Scale").get_float_value_default(1.0f));
+
+ const float3x3 transformation = float3x3::from_translation_rotation_scale(
+ translation, rotation, scale);
+
+ result.transform(transformation);
+ result.get_realization_options().interpolation = get_interpolation();
+ }
+
+ Interpolation get_interpolation()
+ {
+ switch (bnode().custom1) {
+ case 0:
+ return Interpolation::Nearest;
+ case 1:
+ return Interpolation::Bilinear;
+ case 2:
+ return Interpolation::Bicubic;
+ }
+
+ BLI_assert_unreachable();
+ return Interpolation::Nearest;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TransformOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_transform_cc
void register_node_type_cmp_transform()
@@ -44,6 +110,7 @@ void register_node_type_cmp_transform()
cmp_node_type_base(&ntype, CMP_NODE_TRANSFORM, "Transform", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_transform_declare;
ntype.draw_buttons = file_ns::node_composit_buts_transform;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_translate.cc b/source/blender/nodes/composite/nodes/node_composite_translate.cc
index bbdc8ca4d31..e0f87ff604a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_translate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_translate.cc
@@ -5,20 +5,37 @@
* \ingroup cmpnodes
*/
+#include "BLI_float3x3.hh"
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Translate ******************** */
namespace blender::nodes::node_composite_translate_cc {
+NODE_STORAGE_FUNCS(NodeTranslateData)
+
static void cmp_node_translate_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("X")).default_value(0.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("Y")).default_value(0.0f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("X"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Y"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
@@ -34,6 +51,54 @@ static void node_composit_buts_translate(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "wrap_axis", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class TranslateOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+
+ float x = get_input("X").get_float_value_default(0.0f);
+ float y = get_input("Y").get_float_value_default(0.0f);
+ if (get_use_relative()) {
+ x *= input.domain().size.x;
+ y *= input.domain().size.y;
+ }
+
+ const float2 translation = float2(x, y);
+ const float3x3 transformation = float3x3::from_translation(translation);
+
+ result.transform(transformation);
+ result.get_realization_options().repeat_x = get_repeat_x();
+ result.get_realization_options().repeat_y = get_repeat_y();
+ }
+
+ bool get_use_relative()
+ {
+ return node_storage(bnode()).relative;
+ }
+
+ bool get_repeat_x()
+ {
+ return ELEM(node_storage(bnode()).wrap_axis, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY);
+ }
+
+ bool get_repeat_y()
+ {
+ return ELEM(node_storage(bnode()).wrap_axis, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TranslateOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_translate_cc
void register_node_type_cmp_translate()
@@ -48,6 +113,7 @@ void register_node_type_cmp_translate()
node_type_init(&ntype, file_ns::node_composit_init_translate);
node_type_storage(
&ntype, "NodeTranslateData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc
index f71028bf8c1..03a7bc61924 100644
--- a/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc
@@ -5,16 +5,33 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+
+#include "IMB_colormanagement.h"
+
+#include "BKE_colorband.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
+#include "BKE_colorband.h"
+
/* **************** VALTORGB ******************** */
-namespace blender::nodes::node_composite_val_to_rgb_cc {
+namespace blender::nodes::node_composite_color_ramp_cc {
static void cmp_node_valtorgb_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_output<decl::Color>(N_("Image"));
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_output<decl::Color>(N_("Image")).compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Alpha"));
}
@@ -23,11 +40,94 @@ static void node_composit_init_valtorgb(bNodeTree *UNUSED(ntree), bNode *node)
node->storage = BKE_colorband_add(true);
}
-} // namespace blender::nodes::node_composite_val_to_rgb_cc
+using namespace blender::realtime_compositor;
+
+class ColorRampShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ struct ColorBand *color_band = get_color_band();
+
+ /* Common / easy case optimization. */
+ if ((color_band->tot <= 2) && (color_band->color_mode == COLBAND_BLEND_RGB)) {
+ float mul_bias[2];
+ switch (color_band->ipotype) {
+ case COLBAND_INTERP_LINEAR:
+ mul_bias[0] = 1.0f / (color_band->data[1].pos - color_band->data[0].pos);
+ mul_bias[1] = -mul_bias[0] * color_band->data[0].pos;
+ GPU_stack_link(material,
+ &bnode(),
+ "valtorgb_opti_linear",
+ inputs,
+ outputs,
+ GPU_uniform(mul_bias),
+ GPU_uniform(&color_band->data[0].r),
+ GPU_uniform(&color_band->data[1].r));
+ return;
+ case COLBAND_INTERP_CONSTANT:
+ mul_bias[1] = max_ff(color_band->data[0].pos, color_band->data[1].pos);
+ GPU_stack_link(material,
+ &bnode(),
+ "valtorgb_opti_constant",
+ inputs,
+ outputs,
+ GPU_uniform(&mul_bias[1]),
+ GPU_uniform(&color_band->data[0].r),
+ GPU_uniform(&color_band->data[1].r));
+ return;
+ case COLBAND_INTERP_EASE:
+ mul_bias[0] = 1.0f / (color_band->data[1].pos - color_band->data[0].pos);
+ mul_bias[1] = -mul_bias[0] * color_band->data[0].pos;
+ GPU_stack_link(material,
+ &bnode(),
+ "valtorgb_opti_ease",
+ inputs,
+ outputs,
+ GPU_uniform(mul_bias),
+ GPU_uniform(&color_band->data[0].r),
+ GPU_uniform(&color_band->data[1].r));
+ return;
+ default:
+ BLI_assert_unreachable();
+ return;
+ }
+ }
+
+ float *array, layer;
+ int size;
+ BKE_colorband_evaluate_table_rgba(color_band, &array, &size);
+ GPUNodeLink *tex = GPU_color_band(material, size, array, &layer);
+
+ if (color_band->ipotype == COLBAND_INTERP_CONSTANT) {
+ GPU_stack_link(
+ material, &bnode(), "valtorgb_nearest", inputs, outputs, tex, GPU_constant(&layer));
+ return;
+ }
+
+ GPU_stack_link(material, &bnode(), "valtorgb", inputs, outputs, tex, GPU_constant(&layer));
+ }
+
+ struct ColorBand *get_color_band()
+ {
+ return static_cast<struct ColorBand *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorRampShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_color_ramp_cc
void register_node_type_cmp_valtorgb()
{
- namespace file_ns = blender::nodes::node_composite_val_to_rgb_cc;
+ namespace file_ns = blender::nodes::node_composite_color_ramp_cc;
static bNodeType ntype;
@@ -36,31 +136,63 @@ void register_node_type_cmp_valtorgb()
node_type_size(&ntype, 240, 200, 320);
node_type_init(&ntype, file_ns::node_composit_init_valtorgb);
node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** RGBTOBW ******************** */
-namespace blender::nodes::node_composite_val_to_rgb_cc {
+namespace blender::nodes::node_composite_rgb_to_bw_cc {
static void cmp_node_rgbtobw_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_output<decl::Color>(N_("Val"));
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({0.8f, 0.8f, 0.8f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_output<decl::Float>(N_("Val"));
+}
+
+using namespace blender::realtime_compositor;
+
+class RGBToBWShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ float luminance_coefficients[3];
+ IMB_colormanagement_get_luminance_coefficients(luminance_coefficients);
+
+ GPU_stack_link(material,
+ &bnode(),
+ "color_to_luminance",
+ inputs,
+ outputs,
+ GPU_constant(luminance_coefficients));
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new RGBToBWShaderNode(node);
}
-} // namespace blender::nodes::node_composite_val_to_rgb_cc
+} // namespace blender::nodes::node_composite_rgb_to_bw_cc
void register_node_type_cmp_rgbtobw()
{
- namespace file_ns = blender::nodes::node_composite_val_to_rgb_cc;
+ namespace file_ns = blender::nodes::node_composite_rgb_to_bw_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_rgbtobw_declare;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_value.cc b/source/blender/nodes/composite/nodes/node_composite_value.cc
index a3269d3d1c2..a96e1db14ad 100644
--- a/source/blender/nodes/composite/nodes/node_composite_value.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_value.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** VALUE ******************** */
@@ -16,6 +18,29 @@ static void cmp_node_value_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Value")).default_value(0.5f);
}
+using namespace blender::realtime_compositor;
+
+class ValueOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &result = get_result("Value");
+ result.allocate_single_value();
+
+ const bNodeSocket *socket = static_cast<bNodeSocket *>(bnode().outputs.first);
+ float value = static_cast<bNodeSocketValueFloat *>(socket->default_value)->value;
+
+ result.set_float_value(value);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ValueOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_value_cc
void register_node_type_cmp_value()
@@ -27,6 +52,7 @@ void register_node_type_cmp_value()
cmp_node_type_base(&ntype, CMP_NODE_VALUE, "Value", NODE_CLASS_INPUT);
ntype.declare = file_ns::cmp_node_value_declare;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc b/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc
index 741f2e0e816..515478da75d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** VECTOR BLUR ******************** */
@@ -51,6 +53,23 @@ static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "use_curved", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class VectorBlurOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new VectorBlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_vec_blur_cc
void register_node_type_cmp_vecblur()
@@ -65,6 +84,7 @@ void register_node_type_cmp_vecblur()
node_type_init(&ntype, file_ns::node_composit_init_vecblur);
node_type_storage(
&ntype, "NodeBlurData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_viewer.cc
index 05f395183b5..4e82b31ca47 100644
--- a/source/blender/nodes/composite/nodes/node_composite_viewer.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+
#include "BKE_global.h"
#include "BKE_image.h"
@@ -13,6 +15,13 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** VIEWER ******************** */
@@ -55,6 +64,125 @@ static void node_composit_buts_viewer_ex(uiLayout *layout, bContext *UNUSED(C),
}
}
+using namespace blender::realtime_compositor;
+
+class ViewerOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ const Result &image = get_input("Image");
+ const Result &alpha = get_input("Alpha");
+
+ if (image.is_single_value() && alpha.is_single_value()) {
+ execute_clear();
+ }
+ else if (ignore_alpha()) {
+ execute_ignore_alpha();
+ }
+ else if (!node().input_by_identifier("Alpha")->is_logically_linked()) {
+ execute_copy();
+ }
+ else {
+ execute_set_alpha();
+ }
+ }
+
+ /* Executes when all inputs are single values, in which case, the output texture can just be
+ * cleared to the appropriate color. */
+ void execute_clear()
+ {
+ const Result &image = get_input("Image");
+ const Result &alpha = get_input("Alpha");
+
+ float4 color = image.get_color_value();
+ if (ignore_alpha()) {
+ color.w = 1.0f;
+ }
+ else if (node().input_by_identifier("Alpha")->is_logically_linked()) {
+ color.w = alpha.get_float_value();
+ }
+
+ GPU_texture_clear(context().get_output_texture(), GPU_DATA_FLOAT, color);
+ }
+
+ /* Executes when the alpha channel of the image is ignored. */
+ void execute_ignore_alpha()
+ {
+ GPUShader *shader = shader_manager().get("compositor_convert_color_to_opaque");
+ GPU_shader_bind(shader);
+
+ const Result &image = get_input("Image");
+ image.bind_as_texture(shader, "input_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, compute_domain().size);
+
+ image.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* Executes when the image texture is written with no adjustments and can thus be copied directly
+ * to the output texture. */
+ void execute_copy()
+ {
+ const Result &image = get_input("Image");
+
+ /* Make sure any prior writes to the texture are reflected before copying it. */
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
+
+ GPU_texture_copy(context().get_output_texture(), image.texture());
+ }
+
+ /* Executes when the alpha channel of the image is set as the value of the input alpha. */
+ void execute_set_alpha()
+ {
+ GPUShader *shader = shader_manager().get("compositor_set_alpha");
+ GPU_shader_bind(shader);
+
+ const Result &image = get_input("Image");
+ image.bind_as_texture(shader, "image_tx");
+
+ const Result &alpha = get_input("Alpha");
+ alpha.bind_as_texture(shader, "alpha_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, compute_domain().size);
+
+ image.unbind_as_texture();
+ alpha.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* If true, the alpha channel of the image is set to 1, that is, it becomes opaque. If false, the
+ * alpha channel of the image is retained, but only if the alpha input is not linked. If the
+ * alpha input is linked, it the value of that input will be used as the alpha of the image. */
+ bool ignore_alpha()
+ {
+ return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA;
+ }
+
+ /* The operation domain have the same dimensions of the output without any transformations. */
+ Domain compute_domain() override
+ {
+ return Domain(context().get_output_size());
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ViewerOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_viewer_cc
void register_node_type_cmp_viewer()
@@ -70,6 +198,7 @@ void register_node_type_cmp_viewer()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_viewer);
node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.no_muting = true;
diff --git a/source/blender/nodes/composite/nodes/node_composite_zcombine.cc b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc
index be90aeb7acc..e5f460099e9 100644
--- a/source/blender/nodes/composite/nodes/node_composite_zcombine.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Z COMBINE ******************** */
@@ -33,6 +35,24 @@ static void node_composit_buts_zcombine(uiLayout *layout, bContext *UNUSED(C), P
uiItemR(col, ptr, "use_antialias_z", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ZCombineOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Z").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ZCombineOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_zcombine_cc
void register_node_type_cmp_zcombine()
@@ -44,6 +64,7 @@ void register_node_type_cmp_zcombine()
cmp_node_type_base(&ntype, CMP_NODE_ZCOMBINE, "Z Combine", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_zcombine_declare;
ntype.draw_buttons = file_ns::node_composit_buts_zcombine;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}