Realtime Compositor: Add basic matte nodes

This patch implements the following nodes for the realtime compositor:

- Box mask node.
- Channel matte node.
- Chroma matte node.
- Color matte node.
- Color spill node.
- Difference matte node.
- Distance matte node.
- Ellipse matte node.
- Luminance matte node.

Differential Revision: https://developer.blender.org/D15230

Reviewed By: Clement Foucault
This commit is contained in:
Omar Emara 2022-08-10 10:21:18 +02:00
parent b5df7a02ac
commit c014021802
23 changed files with 1073 additions and 17 deletions

View File

@ -323,21 +323,30 @@ set(GLSL_SRC
shaders/common/gpu_shader_common_math_utils.glsl
shaders/common/gpu_shader_common_mix_rgb.glsl
shaders/compositor/compositor_box_mask.glsl
shaders/compositor/compositor_convert.glsl
shaders/compositor/compositor_ellipse_mask.glsl
shaders/compositor/compositor_realize_on_domain.glsl
shaders/compositor/compositor_set_alpha.glsl
shaders/compositor/compositor_split_viewer.glsl
shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl
shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl
shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl
shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl
shaders/compositor/library/gpu_shader_compositor_color_balance.glsl
shaders/compositor/library/gpu_shader_compositor_color_correction.glsl
shaders/compositor/library/gpu_shader_compositor_color_matte.glsl
shaders/compositor/library/gpu_shader_compositor_color_spill.glsl
shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl
shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl
shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl
shaders/compositor/library/gpu_shader_compositor_exposure.glsl
shaders/compositor/library/gpu_shader_compositor_gamma.glsl
shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl
shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl
shaders/compositor/library/gpu_shader_compositor_invert.glsl
shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl
shaders/compositor/library/gpu_shader_compositor_main.glsl
shaders/compositor/library/gpu_shader_compositor_map_value.glsl
shaders/compositor/library/gpu_shader_compositor_normal.glsl
@ -553,7 +562,9 @@ set(SRC_SHADER_CREATE_INFOS
shaders/infos/gpu_shader_text_info.hh
shaders/infos/gpu_srgb_to_framebuffer_space_info.hh
shaders/compositor/infos/compositor_box_mask_info.hh
shaders/compositor/infos/compositor_convert_info.hh
shaders/compositor/infos/compositor_ellipse_mask_info.hh
shaders/compositor/infos/compositor_realize_on_domain_info.hh
shaders/compositor/infos/compositor_set_alpha_info.hh
shaders/compositor/infos/compositor_split_viewer_info.hh

View File

@ -134,6 +134,15 @@ vec3 fallback_pow(vec3 a, float b, vec3 fallback)
/* Matirx Math */
/* Return a 2D rotation matrix with the angle that the input 2D vector makes with the x axis. */
mat2 vector_to_rotation_matrix(vec2 vector)
{
vec2 normalized_vector = normalize(vector);
float cos_angle = normalized_vector.x;
float sin_angle = normalized_vector.y;
return mat2(cos_angle, sin_angle, -sin_angle, cos_angle);
}
mat3 euler_to_mat3(vec3 euler)
{
float cx = cos(euler.x);

View File

@ -0,0 +1,27 @@
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
vec2 uv = vec2(texel) / vec2(domain_size - ivec2(1));
uv -= location;
uv.y *= float(domain_size.y) / float(domain_size.x);
uv = mat2(cos_angle, -sin_angle, sin_angle, cos_angle) * uv;
bool is_inside = all(lessThan(abs(uv), size));
float base_mask_value = texture_load(base_mask_tx, texel).x;
float value = texture_load(mask_value_tx, texel).x;
#if defined(CMP_NODE_MASKTYPE_ADD)
float output_mask_value = is_inside ? max(base_mask_value, value) : base_mask_value;
#elif defined(CMP_NODE_MASKTYPE_SUBTRACT)
float output_mask_value = is_inside ? clamp(base_mask_value - value, 0.0, 1.0) : base_mask_value;
#elif defined(CMP_NODE_MASKTYPE_MULTIPLY)
float output_mask_value = is_inside ? base_mask_value * value : 0.0;
#elif defined(CMP_NODE_MASKTYPE_NOT)
float output_mask_value = is_inside ? (base_mask_value > 0.0 ? 0.0 : value) : base_mask_value;
#endif
imageStore(output_mask_img, texel, vec4(output_mask_value));
}

View File

@ -0,0 +1,27 @@
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
vec2 uv = vec2(texel) / vec2(domain_size - ivec2(1));
uv -= location;
uv.y *= float(domain_size.y) / float(domain_size.x);
uv = mat2(cos_angle, -sin_angle, sin_angle, cos_angle) * uv;
bool is_inside = length(uv / radius) < 1.0;
float base_mask_value = texture_load(base_mask_tx, texel).x;
float value = texture_load(mask_value_tx, texel).x;
#if defined(CMP_NODE_MASKTYPE_ADD)
float output_mask_value = is_inside ? max(base_mask_value, value) : base_mask_value;
#elif defined(CMP_NODE_MASKTYPE_SUBTRACT)
float output_mask_value = is_inside ? clamp(base_mask_value - value, 0.0, 1.0) : base_mask_value;
#elif defined(CMP_NODE_MASKTYPE_MULTIPLY)
float output_mask_value = is_inside ? base_mask_value * value : 0.0;
#elif defined(CMP_NODE_MASKTYPE_NOT)
float output_mask_value = is_inside ? (base_mask_value > 0.0 ? 0.0 : value) : base_mask_value;
#endif
imageStore(output_mask_img, texel, vec4(output_mask_value));
}

View File

@ -0,0 +1,35 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(compositor_box_mask_shared)
.local_group_size(16, 16)
.push_constant(Type::IVEC2, "domain_size")
.push_constant(Type::VEC2, "location")
.push_constant(Type::VEC2, "size")
.push_constant(Type::FLOAT, "cos_angle")
.push_constant(Type::FLOAT, "sin_angle")
.sampler(0, ImageType::FLOAT_2D, "base_mask_tx")
.sampler(1, ImageType::FLOAT_2D, "mask_value_tx")
.image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_mask_img")
.compute_source("compositor_box_mask.glsl");
GPU_SHADER_CREATE_INFO(compositor_box_mask_add)
.additional_info("compositor_box_mask_shared")
.define("CMP_NODE_MASKTYPE_ADD")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_box_mask_subtract)
.additional_info("compositor_box_mask_shared")
.define("CMP_NODE_MASKTYPE_SUBTRACT")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_box_mask_multiply)
.additional_info("compositor_box_mask_shared")
.define("CMP_NODE_MASKTYPE_MULTIPLY")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_box_mask_not)
.additional_info("compositor_box_mask_shared")
.define("CMP_NODE_MASKTYPE_NOT")
.do_static_compilation(true);

View File

@ -0,0 +1,35 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_shared)
.local_group_size(16, 16)
.push_constant(Type::IVEC2, "domain_size")
.push_constant(Type::VEC2, "location")
.push_constant(Type::VEC2, "radius")
.push_constant(Type::FLOAT, "cos_angle")
.push_constant(Type::FLOAT, "sin_angle")
.sampler(0, ImageType::FLOAT_2D, "base_mask_tx")
.sampler(1, ImageType::FLOAT_2D, "mask_value_tx")
.image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_mask_img")
.compute_source("compositor_ellipse_mask.glsl");
GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_add)
.additional_info("compositor_ellipse_mask_shared")
.define("CMP_NODE_MASKTYPE_ADD")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_subtract)
.additional_info("compositor_ellipse_mask_shared")
.define("CMP_NODE_MASKTYPE_SUBTRACT")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_multiply)
.additional_info("compositor_ellipse_mask_shared")
.define("CMP_NODE_MASKTYPE_MULTIPLY")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_not)
.additional_info("compositor_ellipse_mask_shared")
.define("CMP_NODE_MASKTYPE_NOT")
.do_static_compilation(true);

View File

@ -0,0 +1,52 @@
#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
#define CMP_NODE_CHANNEL_MATTE_CS_RGB 1.0
#define CMP_NODE_CHANNEL_MATTE_CS_HSV 2.0
#define CMP_NODE_CHANNEL_MATTE_CS_YUV 3.0
#define CMP_NODE_CHANNEL_MATTE_CS_YCC 4.0
void node_composite_channel_matte(vec4 color,
const float color_space,
const float matte_channel,
const vec2 limit_channels,
float max_limit,
float min_limit,
out vec4 result,
out float matte)
{
vec4 channels;
if (color_space == CMP_NODE_CHANNEL_MATTE_CS_HSV) {
rgb_to_hsv(color, channels);
}
else if (color_space == CMP_NODE_CHANNEL_MATTE_CS_YUV) {
rgba_to_yuva_itu_709(color, channels);
}
else if (color_space == CMP_NODE_CHANNEL_MATTE_CS_YCC) {
rgba_to_ycca_itu_709(color, channels);
}
else {
channels = color;
}
float matte_value = channels[int(matte_channel)];
float limit_value = max(channels[int(limit_channels.x)], channels[int(limit_channels.y)]);
float alpha = 1.0 - (matte_value - limit_value);
if (alpha > max_limit) {
alpha = color.a;
}
else if (alpha < min_limit) {
alpha = 0.0;
}
else {
alpha = (alpha - min_limit) / (max_limit - min_limit);
}
matte = min(alpha, color.a);
result = color * matte;
}
#undef CMP_NODE_CHANNEL_MATTE_CS_RGB
#undef CMP_NODE_CHANNEL_MATTE_CS_HSV
#undef CMP_NODE_CHANNEL_MATTE_CS_YUV
#undef CMP_NODE_CHANNEL_MATTE_CS_YCC

View File

@ -0,0 +1,43 @@
#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
/* Algorithm from the book Video Demystified. Chapter 7. Chroma Keying. */
void node_composite_chroma_matte(vec4 color,
vec4 key,
float acceptance,
float cutoff,
float falloff,
out vec4 result,
out float matte)
{
vec4 color_ycca;
rgba_to_ycca_itu_709(color, color_ycca);
vec4 key_ycca;
rgba_to_ycca_itu_709(key, key_ycca);
/* Normalize the CrCb components into the [-1, 1] range. */
vec2 color_cc = color_ycca.yz * 2.0 - 1.0;
vec2 key_cc = key_ycca.yz * 2.0 - 1.0;
/* Rotate the color onto the space of the key such that x axis of the color space passes through
* the key color. */
color_cc = vector_to_rotation_matrix(key_cc * vec2(1.0, -1.0)) * color_cc;
/* Compute foreground key. If positive, the value is in the [0, 1] range. */
float foreground_key = color_cc.x - (abs(color_cc.y) / acceptance);
/* Negative foreground key values retain the original alpha. Positive values are scaled by the
* falloff, while colors that make an angle less than the cutoff angle get a zero alpha. */
float alpha = color.a;
if (foreground_key > 0.0) {
alpha = 1.0 - (foreground_key / falloff);
if (abs(atan(color_cc.y, color_cc.x)) < (cutoff / 2.0)) {
alpha = 0.0;
}
}
/* Compute output. */
matte = min(alpha, color.a);
result = color * matte;
}

View File

@ -0,0 +1,27 @@
#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
void node_composite_color_matte(vec4 color,
vec4 key,
float hue_epsilon,
float saturation_epsilon,
float value_epsilon,
out vec4 result,
out float matte)
{
vec4 color_hsva;
rgb_to_hsv(color, color_hsva);
vec4 key_hsva;
rgb_to_hsv(key, key_hsva);
bool is_within_saturation = distance(color_hsva.y, key_hsva.y) < saturation_epsilon;
bool is_within_value = distance(color_hsva.z, key_hsva.z) < value_epsilon;
bool is_within_hue = distance(color_hsva.x, key_hsva.x) < hue_epsilon;
/* Hue wraps around, so check the distance around the boundary. */
float min_hue = min(color_hsva.x, key_hsva.x);
float max_hue = max(color_hsva.x, key_hsva.x);
is_within_hue = is_within_hue || ((min_hue + (1.0 - max_hue)) < hue_epsilon);
matte = (is_within_hue && is_within_saturation && is_within_value) ? 0.0 : color.a;
result = color * matte;
}

View File

@ -0,0 +1,13 @@
void node_composite_color_spill(vec4 color,
float factor,
const float spill_channel,
vec3 spill_scale,
const vec2 limit_channels,
float limit_scale,
out vec4 result)
{
float average_limit = (color[int(limit_channels.x)] + color[int(limit_channels.y)]) / 2.0;
float map = factor * color[int(spill_channel)] - limit_scale * average_limit;
result.rgb = map > 0.0 ? color.rgb + spill_scale * map : color.rgb;
result.a = color.a;
}

View File

@ -0,0 +1,10 @@
void node_composite_difference_matte(
vec4 color, vec4 key, float tolerance, float falloff, out vec4 result, out float matte)
{
vec4 difference = abs(color - key);
float average_difference = (difference.r + difference.g + difference.b) / 3.0;
bool is_opaque = average_difference > tolerance + falloff;
float alpha = is_opaque ? color.a : (max(0.0, average_difference - tolerance) / falloff);
matte = min(alpha, color.a);
result = color * matte;
}

View File

@ -0,0 +1,26 @@
#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
void node_composite_distance_matte_rgba(
vec4 color, vec4 key, float tolerance, float falloff, out vec4 result, out float matte)
{
float difference = distance(color.rgb, key.rgb);
bool is_opaque = difference > tolerance + falloff;
float alpha = is_opaque ? color.a : max(0.0, difference - tolerance) / falloff;
matte = min(alpha, color.a);
result = color * matte;
}
void node_composite_distance_matte_ycca(
vec4 color, vec4 key, float tolerance, float falloff, out vec4 result, out float matte)
{
vec4 color_ycca;
rgba_to_ycca_itu_709(color, color_ycca);
vec4 key_ycca;
rgba_to_ycca_itu_709(key, key_ycca);
float difference = distance(color_ycca.yz, key_ycca.yz);
bool is_opaque = difference > tolerance + falloff;
float alpha = is_opaque ? color.a : max(0.0, difference - tolerance) / falloff;
matte = min(alpha, color.a);
result = color * matte;
}

View File

@ -0,0 +1,14 @@
#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
void node_composite_luminance_matte(vec4 color,
float high,
float low,
const vec3 luminance_coefficients,
out vec4 result,
out float matte)
{
float luminance = get_luminance(color.rgb, luminance_coefficients);
float alpha = clamp(0.0, 1.0, (luminance - low) / (high - low));
matte = min(alpha, color.a);
result = color * matte;
}

View File

@ -632,12 +632,12 @@ typedef struct bNodeSocketValueMaterial {
/* Data structs, for `node->storage`. */
enum {
typedef enum CMPNodeMaskType {
CMP_NODE_MASKTYPE_ADD = 0,
CMP_NODE_MASKTYPE_SUBTRACT = 1,
CMP_NODE_MASKTYPE_MULTIPLY = 2,
CMP_NODE_MASKTYPE_NOT = 3,
};
} CMPNodeMaskType;
enum {
CMP_NODE_DILATEERODE_STEP = 0,
@ -1856,6 +1856,24 @@ typedef enum CMPNodeAlphaConvertMode {
CMP_NODE_ALPHA_CONVERT_UNPREMULTIPLY = 1,
} CMPNodeAlphaConvertMode;
/* Distance Matte Node. Stored in NodeChroma.channel. */
typedef enum CMPNodeDistanceMatteColorSpace {
CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_YCCA = 0,
CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_RGBA = 1,
} CMPNodeDistanceMatteColorSpace;
/* Color Spill Node. Stored in custom2. */
typedef enum CMPNodeColorSpillLimitAlgorithm {
CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_SINGLE = 0,
CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_AVERAGE = 1,
} CMPNodeColorSpillLimitAlgorithm;
/* Channel Matte Node. Stored in NodeChroma.algorith. */
typedef enum CMPNodeChannelMatteLimitAlgorithm {
CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_SINGLE = 0,
CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX = 1,
} CMPNodeChannelMatteLimitAlgorithm;
/* Plane track deform node. */
enum {

View File

@ -5,9 +5,18 @@
* \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 ******************** */
@ -48,6 +57,98 @@ 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";
}
}
NodeBoxMask &get_node_box_mask()
{
return *static_cast<NodeBoxMask *>(bnode().storage);
}
float2 get_location()
{
return float2(get_node_box_mask().x, get_node_box_mask().y);
}
float2 get_size()
{
return float2(get_node_box_mask().width, get_node_box_mask().height);
}
float get_angle()
{
return get_node_box_mask().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 +162,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);
}

View File

@ -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"
/* ******************* Channel Matte Node ********************************* */
@ -18,7 +22,9 @@ namespace blender::nodes::node_composite_channel_matte_cc {
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 +85,96 @@ 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;
}
NodeChroma *get_node_chroma()
{
return static_cast<NodeChroma *>(bnode().storage);
}
/* Get the index of the channel used to compute the limit value. */
int get_limit_channel()
{
return get_node_chroma()->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 is the same value. */
void get_limit_channels(float limit_channels[2])
{
if (get_node_chroma()->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 get_node_chroma()->t1;
}
float get_min_limit()
{
return get_node_chroma()->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 +189,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);
}

View File

@ -5,11 +5,15 @@
* \ingroup cmpnodes
*/
#include "BLI_math_rotation.h"
#include <cmath>
#include "UI_interface.h"
#include "UI_resources.h"
#include "GPU_material.h"
#include "COM_shader_node.hh"
#include "node_composite_util.hh"
/* ******************* Chroma Key ********************************************************** */
@ -18,8 +22,12 @@ namespace blender::nodes::node_composite_chroma_matte_cc {
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 +59,57 @@ 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));
}
NodeChroma *get_node_chroma()
{
return static_cast<NodeChroma *>(bnode().storage);
}
float get_acceptance()
{
return std::tan(get_node_chroma()->t1) / 2.0f;
}
float get_cutoff()
{
return get_node_chroma()->t2;
}
float get_falloff()
{
return get_node_chroma()->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 +124,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);
}

View File

@ -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"
/* ******************* Color Matte ********************************************************** */
@ -16,8 +20,12 @@ namespace blender::nodes::node_composite_color_matte_cc {
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 +58,58 @@ 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));
}
NodeChroma *get_node_chroma()
{
return static_cast<NodeChroma *>(bnode().storage);
}
float get_hue_epsilon()
{
/* Divide by 2 because the hue wraps around. */
return get_node_chroma()->t1 / 2.0f;
}
float get_saturation_epsilon()
{
return get_node_chroma()->t2;
}
float get_value_epsilon()
{
return get_node_chroma()->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 +124,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);
}

View File

@ -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 Spill Suppression ********************************* */
@ -18,8 +22,15 @@ namespace blender::nodes::node_composite_color_spill_cc {
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 +38,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 +91,103 @@ 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;
}
NodeColorspill *get_node_color_spill()
{
return static_cast<NodeColorspill *>(bnode().storage);
}
void get_spill_scale(float spill_scale[3])
{
const NodeColorspill *node_color_spill = get_node_color_spill();
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 get_node_color_spill()->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 get_node_color_spill()->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 +202,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);
}

View File

@ -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"
/* ******************* channel Difference Matte ********************************* */
@ -16,8 +20,12 @@ namespace blender::nodes::node_composite_diff_matte_cc {
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::Float>(N_("Matte"));
}
@ -40,6 +48,50 @@ 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));
}
NodeChroma *get_node_chroma()
{
return static_cast<NodeChroma *>(bnode().storage);
}
float get_tolerance()
{
return get_node_chroma()->t1;
}
float get_falloff()
{
return get_node_chroma()->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 +106,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);
}

View File

@ -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"
/* ******************* channel Distance Matte ********************************* */
@ -16,8 +20,12 @@ namespace blender::nodes::node_composite_distance_matte_cc {
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 +34,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 +56,66 @@ 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));
}
NodeChroma *get_node_chroma()
{
return static_cast<NodeChroma *>(bnode().storage);
}
CMPNodeDistanceMatteColorSpace get_color_space()
{
return (CMPNodeDistanceMatteColorSpace)get_node_chroma()->channel;
}
float get_tolerance()
{
return get_node_chroma()->t1;
}
float get_falloff()
{
return get_node_chroma()->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 +130,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);
}

View File

@ -5,9 +5,18 @@
* \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 ******************** */
@ -46,6 +55,98 @@ 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";
}
}
NodeEllipseMask &get_node_ellipse_mask()
{
return *static_cast<NodeEllipseMask *>(bnode().storage);
}
float2 get_location()
{
return float2(get_node_ellipse_mask().x, get_node_ellipse_mask().y);
}
float2 get_size()
{
return float2(get_node_ellipse_mask().width, get_node_ellipse_mask().height);
}
float get_angle()
{
return get_node_ellipse_mask().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 +162,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);
}

View File

@ -5,9 +5,15 @@
* \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 ********************************* */
@ -16,7 +22,9 @@ namespace blender::nodes::node_composite_luma_matte_cc {
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 +48,53 @@ 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));
}
NodeChroma *get_node_chroma()
{
return static_cast<NodeChroma *>(bnode().storage);
}
float get_high()
{
return get_node_chroma()->t1;
}
float get_low()
{
return get_node_chroma()->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 +109,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);
}