Commit: 6109ad6cce9186bd6e8ff4dbfb281ae8f6742119 Author: Omar Emara Date: Wed Aug 10 09:58:44 2022 +0200 Branches: master https://developer.blender.org/rB6109ad6cce9186bd6e8ff4dbfb281ae8f6742119
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 =================================================================== M source/blender/gpu/CMakeLists.txt M source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl M source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl M source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl M source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl A source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl A source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl A source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_balance.glsl A source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_correction.glsl A source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_exposure.glsl A source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_gamma.glsl A source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl A source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl A source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_invert.glsl A source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_posterize.glsl M source/blender/imbuf/IMB_colormanagement.h M source/blender/imbuf/intern/colormanagement_inline.c M source/blender/makesdna/DNA_node_types.h M source/blender/nodes/composite/nodes/node_composite_alpha_over.cc M source/blender/nodes/composite/nodes/node_composite_brightness.cc M source/blender/nodes/composite/nodes/node_composite_colorbalance.cc M source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc M source/blender/nodes/composite/nodes/node_composite_curves.cc M source/blender/nodes/composite/nodes/node_composite_exposure.cc M source/blender/nodes/composite/nodes/node_composite_gamma.cc M source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc M source/blender/nodes/composite/nodes/node_composite_huecorrect.cc M source/blender/nodes/composite/nodes/node_composite_invert.cc M source/blender/nodes/composite/nodes/node_composite_mixrgb.cc M source/blender/nodes/composite/nodes/node_composite_posterize.cc M source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc =================================================================== diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index c20fff7082e..9c7fb8f290f 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -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 diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl index fe89985ae7f..2ac0ff8c4bb 100644 --- a/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl @@ -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); } diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl index 8948ed77557..db8e114ec7a 100644 --- a/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl @@ -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, 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 124654963fd..a28705f158d 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 @@ -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) diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl index f9652f1150b..39f3c722dd2 100644 --- a/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl @@ -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 col @@ Diff output truncated at 10240 characters. @@ _______________________________________________ Bf-blender-cvs mailing list Bf-blender-cvs@blender.org List details, subscription details or unsubscribe: https://lists.blender.org/mailman/listinfo/bf-blender-cvs