Realtime Compositor: Add basic color nodes

This patch implements the following nodes for the realtime compositor:

- Alpha over node.
- Bright contrast node.
- Color balance node.
- Color correction node.
- Exposure node.
- Gamma node.
- Hue correct node.
- Hue saturation value node.
- Invert node.
- Mix node.
- Posterize node.
- Time curve node.
- Vector curve node.

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

Reviewed By: Clement Foucault
This commit is contained in:
Omar Emara 2022-08-10 09:58:44 +02:00
parent 865204fef0
commit 6109ad6cce
31 changed files with 1354 additions and 77 deletions

View File

@ -328,7 +328,17 @@ set(GLSL_SRC
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_color_balance.glsl
shaders/compositor/library/gpu_shader_compositor_color_correction.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_main.glsl
shaders/compositor/library/gpu_shader_compositor_posterize.glsl
shaders/compositor/library/gpu_shader_compositor_store_output.glsl
shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl
shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl

View File

@ -140,6 +140,8 @@ void hsl_to_rgb(vec4 hsl, out vec4 outcol)
outcol = vec4((nr - 0.5) * chroma + l, (ng - 0.5) * chroma + l, (nb - 0.5) * chroma + l, hsl.w);
}
/* ** Alpha Handling ** */
void color_alpha_clear(vec4 color, out vec4 result)
{
result = vec4(color.rgb, 1.0);
@ -147,15 +149,50 @@ void color_alpha_clear(vec4 color, out vec4 result)
void color_alpha_premultiply(vec4 color, out vec4 result)
{
result = vec4(color.rgb * color.a, 1.0);
result = vec4(color.rgb * color.a, color.a);
}
void color_alpha_unpremultiply(vec4 color, out vec4 result)
{
if (color.a == 0.0 || color.a == 1.0) {
result = vec4(color.rgb, 1.0);
result = color;
}
else {
result = vec4(color.rgb / color.a, 1.0);
result = vec4(color.rgb / color.a, color.a);
}
}
float linear_rgb_to_srgb(float color)
{
if (color < 0.0031308) {
return (color < 0.0) ? 0.0 : color * 12.92;
}
return 1.055 * pow(color, 1.0 / 2.4) - 0.055;
}
vec3 linear_rgb_to_srgb(vec3 color)
{
return vec3(
linear_rgb_to_srgb(color.r), linear_rgb_to_srgb(color.g), linear_rgb_to_srgb(color.b));
}
float srgb_to_linear_rgb(float color)
{
if (color < 0.04045) {
return (color < 0.0) ? 0.0 : color * (1.0 / 12.92);
}
return pow((color + 0.055) * (1.0 / 1.055), 2.4);
}
vec3 srgb_to_linear_rgb(vec3 color)
{
return vec3(
srgb_to_linear_rgb(color.r), srgb_to_linear_rgb(color.g), srgb_to_linear_rgb(color.b));
}
float get_luminance(vec3 color, vec3 luminance_coefficients)
{
return dot(color, luminance_coefficients);
}

View File

@ -95,6 +95,81 @@ void curves_combined_only(float factor,
result = mix(color, result, factor);
}
/* Contrary to standard tone curve implementations, the film-like implementation tries to preserve
* the hue of the colors as much as possible. To understand why this might be a problem, consider
* the violet color (0.5, 0.0, 1.0). If this color was to be evaluated at a power curve x^4, the
* color will be blue (0.0625, 0.0, 1.0). So the color changes and not just its luminosity, which
* is what film-like tone curves tries to avoid.
*
* First, the channels with the lowest and highest values are identified and evaluated at the
* curve. Then, the third channel---the median---is computed while maintaining the original hue of
* the color. To do that, we look at the equation for deriving the hue from RGB values. Assuming
* the maximum, minimum, and median channels are known, and ignoring the 1/3 period offset of the
* hue, the equation is:
*
* hue = (median - min) / (max - min) [1]
*
* Since we have the new values for the minimum and maximum after evaluating at the curve, we also
* have:
*
* hue = (new_median - new_min) / (new_max - new_min) [2]
*
* Since we want the hue to be equivalent, by equating [1] and [2] and rearranging:
*
* (new_median - new_min) / (new_max - new_min) = (median - min) / (max - min)
* new_median - new_min = (new_max - new_min) * (median - min) / (max - min)
* new_median = new_min + (new_max - new_min) * (median - min) / (max - min)
* new_median = new_min + (median - min) * ((new_max - new_min) / (max - min)) [QED]
*
* Which gives us the median color that preserves the hue. More intuitively, the median is computed
* such that the change in the distance from the median to the minimum is proportional to the
* change in the distance from the minimum to the maximum. Finally, each of the new minimum,
* maximum, and median values are written to the color channel that they were originally extracted
* from. */
void curves_film_like(float factor,
vec4 color,
vec4 black_level,
vec4 white_level,
sampler1DArray curve_map,
const float layer,
float range_minimum,
float range_divider,
float start_slope,
float end_slope,
out vec4 result)
{
vec4 balanced = white_balance(color, black_level, white_level);
/* Find the maximum, minimum, and median of the color channels. */
float minimum = min(balanced.r, min(balanced.g, balanced.b));
float maximum = max(balanced.r, max(balanced.g, balanced.b));
float median = max(min(balanced.r, balanced.g), min(balanced.b, max(balanced.r, balanced.g)));
/* Evaluate alpha curve map at the maximum and minimum channels. The alpha curve is the Combined
* curve in the UI. */
float min_parameter = NORMALIZE_PARAMETER(minimum, range_minimum, range_divider);
float max_parameter = NORMALIZE_PARAMETER(maximum, range_minimum, range_divider);
float new_min = texture(curve_map, vec2(min_parameter, layer)).a;
float new_max = texture(curve_map, vec2(max_parameter, layer)).a;
/* Then, extrapolate if needed. */
new_min = extrapolate_if_needed(min_parameter, new_min, start_slope, end_slope);
new_max = extrapolate_if_needed(max_parameter, new_max, start_slope, end_slope);
/* Compute the new median using the ratio between the new and the original range. */
float scaling_ratio = (new_max - new_min) / (maximum - minimum);
float new_median = new_min + (median - minimum) * scaling_ratio;
/* Write each value to its original channel. */
bvec3 channel_is_min = equal(balanced.rgb, vec3(minimum));
vec3 median_or_min = mix(vec3(new_median), vec3(new_min), channel_is_min);
bvec3 channel_is_max = equal(balanced.rgb, vec3(maximum));
result.rgb = mix(median_or_min, vec3(new_max), channel_is_max);
result.a = color.a;
result = mix(color, result, clamp(factor, 0.0, 1.0));
}
void curves_vector(vec3 vector,
sampler1DArray curve_map,
const float layer,

View File

@ -34,6 +34,17 @@ float compatible_pow(float x, float y)
return pow(x, y);
}
/* A version of pow that returns a fallback value if the computation is undefined. From the spec:
* The result is undefined if x < 0 or if x = 0 and y is less than or equal 0. */
float fallback_pow(float x, float y, float fallback)
{
if (x < 0.0 || (x == 0.0 && y <= 0.0)) {
return fallback;
}
return pow(x, y);
}
float wrap(float a, float b, float c)
{
float range = b - c;
@ -114,6 +125,13 @@ void vector_copy(vec3 normal, out vec3 outnormal)
outnormal = normal;
}
vec3 fallback_pow(vec3 a, float b, vec3 fallback)
{
return vec3(fallback_pow(a.x, b, fallback.x),
fallback_pow(a.y, b, fallback.y),
fallback_pow(a.z, b, fallback.z));
}
/* Matirx Math */
mat3 euler_to_mat3(vec3 euler)

View File

@ -2,28 +2,24 @@
void mix_blend(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
outcol = mix(col1, col2, fac);
outcol.a = col1.a;
}
void mix_add(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
outcol = mix(col1, col1 + col2, fac);
outcol.a = col1.a;
}
void mix_mult(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
outcol = mix(col1, col1 * col2, fac);
outcol.a = col1.a;
}
void mix_screen(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
outcol = vec4(1.0) - (vec4(facm) + fac * (vec4(1.0) - col2)) * (vec4(1.0) - col1);
@ -32,7 +28,6 @@ void mix_screen(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_overlay(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
outcol = col1;
@ -61,14 +56,30 @@ void mix_overlay(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_sub(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
outcol = mix(col1, col1 - col2, fac);
outcol.a = col1.a;
}
void mix_div(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
outcol = vec4(vec3(0.0), col1.a);
if (col2.r != 0.0) {
outcol.r = facm * col1.r + fac * col1.r / col2.r;
}
if (col2.g != 0.0) {
outcol.g = facm * col1.g + fac * col1.g / col2.g;
}
if (col2.b != 0.0) {
outcol.b = facm * col1.b + fac * col1.b / col2.b;
}
}
/* A variant of mix_div that fallback to the first color upon zero division. */
void mix_div_fallback(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
float facm = 1.0 - fac;
outcol = col1;
@ -86,28 +97,24 @@ void mix_div(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_diff(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
outcol = mix(col1, abs(col1 - col2), fac);
outcol.a = col1.a;
}
void mix_dark(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
outcol.rgb = mix(col1.rgb, min(col1.rgb, col2.rgb), fac);
outcol.a = col1.a;
}
void mix_light(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
outcol.rgb = mix(col1.rgb, max(col1.rgb, col2.rgb), fac);
outcol.a = col1.a;
}
void mix_dodge(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
outcol = col1;
if (outcol.r != 0.0) {
@ -150,7 +157,6 @@ void mix_dodge(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_burn(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
float tmp, facm = 1.0 - fac;
outcol = col1;
@ -200,7 +206,6 @@ void mix_burn(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_hue(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
outcol = col1;
@ -220,7 +225,6 @@ void mix_hue(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_sat(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
outcol = col1;
@ -238,7 +242,6 @@ void mix_sat(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_val(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
vec4 hsv, hsv2;
@ -251,7 +254,6 @@ void mix_val(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_color(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
outcol = col1;
@ -272,22 +274,26 @@ void mix_color(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_soft(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
vec4 one = vec4(1.0);
vec4 scr = one - (one - col2) * (one - col1);
outcol = facm * col1 + fac * ((one - col1) * col2 * col1 + col1 * scr);
outcol.a = col1.a;
}
void mix_linear(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
outcol = col1 + fac * (2.0 * (col2 - vec4(0.5)));
outcol.a = col1.a;
}
void clamp_color(vec3 vec, vec3 min, vec3 max, out vec3 out_vec)
void clamp_color(vec4 vec, const vec4 min, const vec4 max, out vec4 out_vec)
{
out_vec = clamp(vec, min, max);
}
void multiply_by_alpha(float factor, vec4 color, out float result)
{
result = factor * color.a;
}

View File

@ -0,0 +1,48 @@
void node_composite_alpha_over_mixed(
float factor, vec4 color, vec4 over_color, float premultiply_factor, out vec4 result)
{
if (over_color.a <= 0.0) {
result = color;
}
else if (factor == 1.0 && over_color.a >= 1.0) {
result = over_color;
}
else {
float add_factor = 1.0 - premultiply_factor + over_color.a * premultiply_factor;
float premultiplier = factor * add_factor;
float multiplier = 1.0 - factor * over_color.a;
result = multiplier * color + vec2(premultiplier, factor).xxxy * over_color;
}
}
void node_composite_alpha_over_key(float factor, vec4 color, vec4 over_color, out vec4 result)
{
if (over_color.a <= 0.0) {
result = color;
}
else if (factor == 1.0 && over_color.a >= 1.0) {
result = over_color;
}
else {
result = mix(color, vec4(over_color.rgb, 1.0), factor * over_color.a);
}
}
void node_composite_alpha_over_premultiply(float factor,
vec4 color,
vec4 over_color,
out vec4 result)
{
if (over_color.a < 0.0) {
result = color;
}
else if (factor == 1.0 && over_color.a >= 1.0) {
result = over_color;
}
else {
float multiplier = 1.0 - factor * over_color.a;
result = multiplier * color + factor * over_color;
}
}

View File

@ -0,0 +1,38 @@
#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
/* The algorithm is by Werner D. Streidt
* (http://visca.com/ffactory/archives/5-99/msg00021.html)
* Extracted of OpenCV demhist.c
*/
#define FLT_EPSILON 1.192092896e-07F
void node_composite_bright_contrast(
vec4 color, float brightness, float contrast, const float use_premultiply, out vec4 result)
{
brightness /= 100.0;
float delta = contrast / 200.0;
float multiplier, offset;
if (contrast > 0.0) {
multiplier = 1.0 - delta * 2.0;
multiplier = 1.0 / max(multiplier, FLT_EPSILON);
offset = multiplier * (brightness - delta);
}
else {
delta *= -1.0;
multiplier = max(1.0 - delta * 2.0, 0.0);
offset = multiplier * brightness + delta;
}
if (use_premultiply != 0.0) {
color_alpha_unpremultiply(color, color);
}
result.rgb = color.rgb * multiplier + offset;
result.a = color.a;
if (use_premultiply != 0.0) {
color_alpha_premultiply(result, result);
}
}

View File

@ -0,0 +1,34 @@
#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
void node_composite_color_balance_lgg(
float factor, vec4 color, vec3 lift, vec3 gamma, vec3 gain, out vec4 result)
{
lift = 2.0 - lift;
vec3 srgb_color = linear_rgb_to_srgb(color.rgb);
vec3 lift_balanced = ((srgb_color - 1.0) * lift) + 1.0;
vec3 gain_balanced = lift_balanced * gain;
gain_balanced = max(gain_balanced, vec3(0.0));
vec3 linear_color = srgb_to_linear_rgb(gain_balanced);
gamma = mix(gamma, vec3(1e-6), equal(gamma, vec3(0.0)));
vec3 gamma_balanced = pow(linear_color, 1.0 / gamma);
result.rgb = mix(color.rgb, gamma_balanced, min(factor, 1.0));
result.a = color.a;
}
void node_composite_color_balance_asc_cdl(float factor,
vec4 color,
vec3 offset,
vec3 power,
vec3 slope,
float offset_basis,
out vec4 result)
{
offset += offset_basis;
vec3 balanced = color.rgb * slope + offset;
balanced = pow(max(balanced, vec3(0.0)), power);
result.rgb = mix(color.rgb, balanced, min(factor, 1.0));
result.a = color.a;
}

View File

@ -0,0 +1,87 @@
#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
void node_composite_color_correction(vec4 color,
float mask,
const vec3 enabled_channels,
float start_midtones,
float end_midtones,
float master_saturation,
float master_contrast,
float master_gamma,
float master_gain,
float master_lift,
float shadows_saturation,
float shadows_contrast,
float shadows_gamma,
float shadows_gain,
float shadows_lift,
float midtones_saturation,
float midtones_contrast,
float midtones_gamma,
float midtones_gain,
float midtones_lift,
float highlights_saturation,
float highlights_contrast,
float highlights_gamma,
float highlights_gain,
float highlights_lift,
const vec3 luminance_coefficients,
out vec4 result)
{
const float margin = 0.10;
const float margin_divider = 0.5 / margin;
float level = (color.r + color.g + color.b) / 3.0;
float level_shadows = 0.0;
float level_midtones = 0.0;
float level_highlights = 0.0;
if (level < (start_midtones - margin)) {
level_shadows = 1.0;
}
else if (level < (start_midtones + margin)) {
level_midtones = ((level - start_midtones) * margin_divider) + 0.5;
level_shadows = 1.0 - level_midtones;
}
else if (level < (end_midtones - margin)) {
level_midtones = 1.0;
}
else if (level < (end_midtones + margin)) {
level_highlights = ((level - end_midtones) * margin_divider) + 0.5;
level_midtones = 1.0 - level_highlights;
}
else {
level_highlights = 1.0;
}
float contrast = level_shadows * shadows_contrast;
contrast += level_midtones * midtones_contrast;
contrast += level_highlights * highlights_contrast;
contrast *= master_contrast;
float saturation = level_shadows * shadows_saturation;
saturation += level_midtones * midtones_saturation;
saturation += level_highlights * highlights_saturation;
saturation *= master_saturation;
float gamma = level_shadows * shadows_gamma;
gamma += level_midtones * midtones_gamma;
gamma += level_highlights * highlights_gamma;
gamma *= master_gamma;
float gain = level_shadows * shadows_gain;
gain += level_midtones * midtones_gain;
gain += level_highlights * highlights_gain;
gain *= master_gain;
float lift = level_shadows * shadows_lift;
lift += level_midtones * midtones_lift;
lift += level_highlights * highlights_lift;
lift += master_lift;
float inverse_gamma = 1.0 / gamma;
float luma = get_luminance(color.rgb, luminance_coefficients);
vec3 corrected = luma + saturation * (color.rgb - luma);
corrected = 0.5 + (corrected - 0.5) * contrast;
corrected = fallback_pow(corrected * gain + lift, inverse_gamma, corrected);
corrected = mix(color.rgb, corrected, min(mask, 1.0));
result.rgb = mix(corrected, color.rgb, equal(enabled_channels, vec3(0.0)));
result.a = color.a;
}

View File

@ -0,0 +1,6 @@
void node_composite_exposure(vec4 color, float exposure, out vec4 result)
{
float multiplier = exp2(exposure);
result.rgb = color.rgb * multiplier;
result.a = color.a;
}

View File

@ -0,0 +1,7 @@
#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
void node_composite_gamma(vec4 color, float gamma, out vec4 result)
{
result.rgb = fallback_pow(color.rgb, gamma, color.rgb);
result.a = color.a;
}

View File

@ -0,0 +1,39 @@
#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
/* Curve maps are stored in sampler objects that are evaluated in the [0, 1] range, so normalize
* parameters accordingly. */
#define NORMALIZE_PARAMETER(parameter, minimum, range) ((parameter - minimum) * range)
void node_composite_hue_correct(float factor,
vec4 color,
sampler1DArray curve_map,
const float layer,
vec3 minimums,
vec3 range_dividers,
out vec4 result)
{
vec4 hsv;
rgb_to_hsv(color, hsv);
/* First, adjust the hue channel on its own, since corrections in the saturation and value
* channels depends on the new value of the hue, not its original value. A curve map value of 0.5
* means no change in hue, so adjust the value to get an identity at 0.5. Since the identity of
* addition is 0, we subtract 0.5 (0.5 - 0.5 = 0). */
const float hue_parameter = NORMALIZE_PARAMETER(hsv.x, minimums.x, range_dividers.x);
hsv.x += texture(curve_map, vec2(hue_parameter, layer)).x - 0.5;
/* Second, adjust the saturation and value based on the new value of the hue. A curve map value
* of 0.5 means no change in hue, so adjust the value to get an identity at 0.5. Since the
* identity of duplication is 1, we multiply by 2 (0.5 * 2 = 1). */
vec2 parameters = NORMALIZE_PARAMETER(hsv.x, minimums.yz, range_dividers.yz);
hsv.y *= texture(curve_map, vec2(parameters.x, layer)).y * 2.0;
hsv.z *= texture(curve_map, vec2(parameters.y, layer)).z * 2.0;
/* Sanitize the new hue and saturation values. */
hsv.x = fract(hsv.x);
hsv.y = clamp(hsv.y, 0.0, 1.0);
hsv_to_rgb(hsv, result);
result = mix(color, result, factor);
}

View File

@ -0,0 +1,16 @@
#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
void node_composite_hue_saturation_value(
vec4 color, float hue, float saturation, float value, float factor, out vec4 result)
{
vec4 hsv;
rgb_to_hsv(color, hsv);
hsv.x = fract(hsv.x + hue + 0.5);
hsv.y = clamp(hsv.y * saturation, 0.0, 1.0);
hsv.z = hsv.z * value;
hsv_to_rgb(hsv, result);
result = mix(color, result, factor);
}

View File

@ -0,0 +1,13 @@
#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
void node_composite_invert(float fac, vec4 color, float do_rgb, float do_alpha, out vec4 result)
{
result = color;
if (do_rgb != 0.0) {
result.rgb = 1.0 - result.rgb;
}
if (do_alpha != 0.0) {
result.a = 1.0 - result.a;
}
result = mix(color, result, fac);
}

View File

@ -0,0 +1,6 @@
void node_composite_posterize(vec4 color, float steps, out vec4 result)
{
steps = clamp(steps, 2.0, 1024.0);
result = floor(color * steps) / steps;
result.a = color.a;
}

View File

@ -56,6 +56,8 @@ bool IMB_colormanagement_space_name_is_data(const char *name);
bool IMB_colormanagement_space_name_is_scene_linear(const char *name);
bool IMB_colormanagement_space_name_is_srgb(const char *name);
BLI_INLINE void IMB_colormanagement_get_luminance_coefficients(float r_rgb[3]);
/**
* Convert a float RGB triplet to the correct luminance weighted average.
*

View File

@ -11,6 +11,11 @@
#include "BLI_math_vector.h"
#include "IMB_colormanagement_intern.h"
void IMB_colormanagement_get_luminance_coefficients(float r_rgb[3])
{
copy_v3_v3(r_rgb, imbuf_luma_coefficients);
}
float IMB_colormanagement_get_luminance(const float rgb[3])
{
return dot_v3v3(imbuf_luma_coefficients, rgb);

View File

@ -1844,6 +1844,12 @@ typedef enum CMPNodeSplitViewerAxis {
CMP_NODE_SPLIT_VIEWER_VERTICAL = 1,
} CMPNodeSplitViewerAxis;
/* Color Balance Node. Stored in custom1. */
typedef enum CMPNodeColorBalanceMethod {
CMP_NODE_COLOR_BALANCE_LGG = 0,
CMP_NODE_COLOR_BALANCE_ASC_CDL = 1,
} CMPNodeColorBalanceMethod;
/* Plane track deform node. */
enum {

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"
/* **************** ALPHAOVER ******************** */
@ -16,9 +20,18 @@ namespace blender::nodes::node_composite_alpha_over_cc {
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 +49,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 ((NodeTwoFloats *)bnode().storage)->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 +109,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);
}

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"
/* **************** 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);
}

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 Balance ********************************* */
@ -46,8 +50,15 @@ namespace blender::nodes::node_composite_colorbalance_cc {
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 +82,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 +127,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 +150,58 @@ 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 = get_node_color_balance();
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;
}
NodeColorBalance *get_node_color_balance()
{
return static_cast<NodeColorBalance *>(bnode().storage);
}
};
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 +218,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);
}

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"
/* ******************* Color Correction ********************************* */
@ -16,8 +22,14 @@ namespace blender::nodes::node_composite_colorcorrection_cc {
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 +278,73 @@ 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 = get_node_color_correction();
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;
}
}
NodeColorCorrection *get_node_color_correction()
{
return static_cast<NodeColorCorrection *>(bnode().storage);
}
};
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 +361,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);
}

View File

@ -5,16 +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)
{
@ -29,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 = 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));
}
CurveMapping *get_curve_mapping()
{
return static_cast<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;
@ -42,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"));
}
@ -66,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 = 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));
}
CurveMapping *get_curve_mapping()
{
return static_cast<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;
@ -80,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"));
@ -103,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 = 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));
}
CurveMapping *get_curve_mapping()
{
return static_cast<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;
@ -116,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);
}

View File

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

View File

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

View File

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

View File

@ -5,6 +5,12 @@
* \ingroup cmpnodes
*/
#include "BKE_colortools.h"
#include "GPU_material.h"
#include "COM_shader_node.hh"
#include "node_composite_util.hh"
#include "BKE_colortools.h"
@ -13,8 +19,15 @@ 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"));
}
@ -35,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 = 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));
}
CurveMapping *get_curve_mapping()
{
return static_cast<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()
@ -48,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);
}

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"
/* **************** 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);
}

View File

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

View File

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

View File

@ -32,7 +32,7 @@ static const char *gpu_shader_get_name(int mode)
case MA_RAMP_SCREEN:
return "mix_screen";
case MA_RAMP_DIV:
return "mix_div";
return "mix_div_fallback";
case MA_RAMP_DIFF:
return "mix_diff";
case MA_RAMP_DARK:
@ -70,18 +70,23 @@ static int gpu_shader_mix_rgb(GPUMaterial *mat,
{
const char *name = gpu_shader_get_name(node->custom1);
if (name != nullptr) {
int ret = GPU_stack_link(mat, node, name, in, out);
if (ret && node->custom2 & SHD_MIXRGB_CLAMP) {
const float min[3] = {0.0f, 0.0f, 0.0f};
const float max[3] = {1.0f, 1.0f, 1.0f};
GPU_link(
mat, "clamp_color", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link);
}
return ret;
if (name == nullptr) {
return 0;
}
return 0;
const float min = 0.0f;
const float max = 1.0f;
const GPUNodeLink *factor_link = in[0].link ? in[0].link : GPU_uniform(in[0].vec);
GPU_link(mat, "clamp_value", factor_link, GPU_constant(&min), GPU_constant(&max), &in[0].link);
int ret = GPU_stack_link(mat, node, name, in, out);
if (ret && node->custom2 & SHD_MIXRGB_CLAMP) {
const float min[3] = {0.0f, 0.0f, 0.0f};
const float max[3] = {1.0f, 1.0f, 1.0f};
GPU_link(mat, "clamp_color", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link);
}
return ret;
}
class MixRGBFunction : public fn::MultiFunction {