From c014021802c323c3ed55c3c283717975a3d93edf Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Wed, 10 Aug 2022 10:21:18 +0200 Subject: 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 --- .../common/gpu_shader_common_math_utils.glsl | 9 ++++ .../shaders/compositor/compositor_box_mask.glsl | 27 +++++++++++ .../compositor/compositor_ellipse_mask.glsl | 27 +++++++++++ .../compositor/infos/compositor_box_mask_info.hh | 35 +++++++++++++++ .../infos/compositor_ellipse_mask_info.hh | 35 +++++++++++++++ .../gpu_shader_compositor_channel_matte.glsl | 52 ++++++++++++++++++++++ .../gpu_shader_compositor_chroma_matte.glsl | 43 ++++++++++++++++++ .../library/gpu_shader_compositor_color_matte.glsl | 27 +++++++++++ .../library/gpu_shader_compositor_color_spill.glsl | 13 ++++++ .../gpu_shader_compositor_difference_matte.glsl | 10 +++++ .../gpu_shader_compositor_distance_matte.glsl | 26 +++++++++++ .../gpu_shader_compositor_luminance_matte.glsl | 14 ++++++ 12 files changed, 318 insertions(+) create mode 100644 source/blender/gpu/shaders/compositor/compositor_box_mask.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl (limited to 'source/blender/gpu/shaders') diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl index a28705f158d..1ba22b4c5da 100644 --- a/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl @@ -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); diff --git a/source/blender/gpu/shaders/compositor/compositor_box_mask.glsl b/source/blender/gpu/shaders/compositor/compositor_box_mask.glsl new file mode 100644 index 00000000000..fad23f28fde --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_box_mask.glsl @@ -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)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl b/source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl new file mode 100644 index 00000000000..28f725067e0 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl @@ -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)); +} diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh new file mode 100644 index 00000000000..ecb253bbab1 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh @@ -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); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh new file mode 100644 index 00000000000..52db91c94e5 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh @@ -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); diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl new file mode 100644 index 00000000000..f2dcc9543f2 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl @@ -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 diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl new file mode 100644 index 00000000000..5d6bea0c9db --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl @@ -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; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl new file mode 100644 index 00000000000..038471bc1bc --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl @@ -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; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl new file mode 100644 index 00000000000..0adad53ad80 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl @@ -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; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl new file mode 100644 index 00000000000..d769cadce3c --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl @@ -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; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl new file mode 100644 index 00000000000..9beed66826c --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl @@ -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; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl new file mode 100644 index 00000000000..3647ac583fe --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl @@ -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; +} -- cgit v1.2.3