From a1131215628c63f7533af83adb2f1533a9d63cf9 Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Tue, 7 Oct 2025 17:04:34 +0200 Subject: [PATCH] Compositor: Perform LGG Color Balance in linear space This patch adjusts the LGG Color Balance mode to operate in linear space as opposed to sRGB space, which is more inline with other software and is more expected to the user. Versioning was added using pre and post gamma correction, but it is very slightly different in the blacks because the gamma node only does power correction. Pull Request: https://projects.blender.org/blender/blender/pulls/147533 --- .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_500.cc | 92 +++++++++++++++++++ .../gpu_shader_compositor_color_balance.glsl | 12 +-- .../nodes/node_composite_colorbalance.cc | 14 +-- .../node_color_balance_lift.png | 4 +- .../consecutive_pixel_nodes.png | 4 +- .../pixel_and_non_pixel_nodes.png | 4 +- 7 files changed, 105 insertions(+), 27 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index c803d12d89f..5d25ef62e12 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,7 +27,7 @@ /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 102 +#define BLENDER_FILE_SUBVERSION 103 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenloader/intern/versioning_500.cc b/source/blender/blenloader/intern/versioning_500.cc index a59c7e32505..defbaa193c7 100644 --- a/source/blender/blenloader/intern/versioning_500.cc +++ b/source/blender/blenloader/intern/versioning_500.cc @@ -2498,6 +2498,85 @@ static void sequencer_substitute_transform_effects(Scene *scene) }); } +/* The LGG mode of the Color Balance node was being done in sRGB space, while now it is done in + * linear space. So a Gamma node will be added before and after the node to perform the adjustment + * in sRGB space. */ +static void do_version_lift_gamma_gain_srgb_to_linear(bNodeTree &node_tree, bNode &node) +{ + bNodeSocket *image_input = blender::bke::node_find_socket(node, SOCK_IN, "Image"); + bNodeSocket *type_input = blender::bke::node_find_socket(node, SOCK_IN, "Type"); + bNodeSocket *image_output = blender::bke::node_find_socket(node, SOCK_OUT, "Image"); + + /* Find the links going into and out of of the node. */ + bNodeLink *image_input_link = nullptr; + bNodeLink *type_input_link = nullptr; + LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) { + if (link->tosock == image_input) { + image_input_link = link; + } + if (link->tosock == type_input) { + type_input_link = link; + } + } + + if (type_input_link || !type_input || + type_input->default_value_typed()->value != CMP_NODE_COLOR_BALANCE_LGG) + { + return; + } + + bNode *inverse_gamma_node = blender::bke::node_add_static_node( + nullptr, node_tree, SH_NODE_GAMMA); + inverse_gamma_node->parent = node.parent; + inverse_gamma_node->location[0] = node.location[0]; + inverse_gamma_node->location[1] = node.location[1]; + + bNodeSocket *inverse_gamma_color_input = blender::bke::node_find_socket( + *inverse_gamma_node, SOCK_IN, "Color"); + copy_v4_v4(inverse_gamma_color_input->default_value_typed()->value, + image_input->default_value_typed()->value); + bNodeSocket *inverse_gamma_color_output = blender::bke::node_find_socket( + *inverse_gamma_node, SOCK_OUT, "Color"); + + bNodeSocket *inverse_gamma_input = blender::bke::node_find_socket( + *inverse_gamma_node, SOCK_IN, "Gamma"); + inverse_gamma_input->default_value_typed()->value = 1.0f / 2.2f; + + version_node_add_link( + node_tree, *inverse_gamma_node, *inverse_gamma_color_output, node, *image_input); + if (image_input_link) { + version_node_add_link(node_tree, + *image_input_link->fromnode, + *image_input_link->fromsock, + *inverse_gamma_node, + *inverse_gamma_color_input); + blender::bke::node_remove_link(&node_tree, *image_input_link); + } + + bNode *gamma_node = blender::bke::node_add_static_node(nullptr, node_tree, SH_NODE_GAMMA); + gamma_node->parent = node.parent; + gamma_node->location[0] = node.location[0]; + gamma_node->location[1] = node.location[1]; + + bNodeSocket *gamma_color_input = blender::bke::node_find_socket(*gamma_node, SOCK_IN, "Color"); + bNodeSocket *gamma_color_output = blender::bke::node_find_socket(*gamma_node, SOCK_OUT, "Color"); + + bNodeSocket *gamma_input = blender::bke::node_find_socket(*gamma_node, SOCK_IN, "Gamma"); + gamma_input->default_value_typed()->value = 2.2f; + + LISTBASE_FOREACH_BACKWARD_MUTABLE (bNodeLink *, link, &node_tree.links) { + if (link->fromsock != image_output) { + continue; + } + + version_node_add_link( + node_tree, *gamma_node, *gamma_color_output, *link->tonode, *link->tosock); + blender::bke::node_remove_link(&node_tree, *link); + } + + version_node_add_link(node_tree, node, *image_output, *gamma_node, *gamma_color_input); +} + void do_versions_after_linking_500(FileData *fd, Main *bmain) { if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 9)) { @@ -3826,6 +3905,19 @@ void blo_do_versions_500(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 103)) { + FOREACH_NODETREE_BEGIN (bmain, node_tree, id) { + if (node_tree->type == NTREE_COMPOSIT) { + LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) { + if (node->type_legacy == CMP_NODE_COLORBALANCE) { + do_version_lift_gamma_gain_srgb_to_linear(*node_tree, *node); + } + } + } + } + FOREACH_NODETREE_END; + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. diff --git a/source/blender/compositor/shaders/library/gpu_shader_compositor_color_balance.glsl b/source/blender/compositor/shaders/library/gpu_shader_compositor_color_balance.glsl index fc664b71b8b..5950403d0a4 100644 --- a/source/blender/compositor/shaders/library/gpu_shader_compositor_color_balance.glsl +++ b/source/blender/compositor/shaders/library/gpu_shader_compositor_color_balance.glsl @@ -2,7 +2,6 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#include "gpu_shader_common_color_utils.glsl" #include "gpu_shader_math_matrix_construct_lib.glsl" #define CMP_NODE_COLOR_BALANCE_LGG 0 @@ -17,19 +16,14 @@ float4 lift_gamma_gain(const float4 color, const float base_gain, const float4 color_gain) { - const float3 srgb_color = linear_rgb_to_srgb(color.rgb); - const float3 lift = base_lift + color_lift.xyz(); - const float3 lift_balanced = ((srgb_color - 1.0f) * (2.0f - lift)) + 1.0f; + const float3 lift_balanced = ((color.xyz() - 1.0f) * (2.0f - lift)) + 1.0f; const float3 gain = base_gain * color_gain.xyz(); - float3 gain_balanced = lift_balanced * gain; - gain_balanced = max(gain_balanced, float3(0.0f)); - - float3 linear_color = srgb_to_linear_rgb(gain_balanced); + const float3 gain_balanced = max(float3(0.0f), lift_balanced * gain); const float3 gamma = base_gamma * color_gamma.xyz(); - float3 gamma_balanced = pow(linear_color, 1.0f / max(gamma, float3(1e-6))); + const float3 gamma_balanced = pow(gain_balanced, 1.0f / max(gamma, float3(1e-6))); return float4(gamma_balanced, color.w); } diff --git a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc index c4b5a6126a6..19387d9144b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc +++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc @@ -7,7 +7,6 @@ */ #include "BLI_math_base.hh" -#include "BLI_math_color.h" #include "BLI_math_matrix_types.hh" #include "BLI_math_vector.hh" #include "BLI_math_vector_types.hh" @@ -245,21 +244,14 @@ static float4 lift_gamma_gain(const float4 color, const float base_gain, const float4 color_gain) { - float3 srgb_color; - linearrgb_to_srgb_v3_v3(srgb_color, color); - const float3 lift = base_lift + color_lift.xyz(); - const float3 lift_balanced = ((srgb_color - 1.0f) * (2.0f - lift)) + 1.0f; + const float3 lift_balanced = ((color.xyz() - 1.0f) * (2.0f - lift)) + 1.0f; const float3 gain = base_gain * color_gain.xyz(); - float3 gain_balanced = lift_balanced * gain; - gain_balanced = math::max(gain_balanced, float3(0.0f)); - - float3 linear_color; - srgb_to_linearrgb_v3_v3(linear_color, gain_balanced); + const float3 gain_balanced = math::max(float3(0.0f), lift_balanced * gain); const float3 gamma = base_gamma * color_gamma.xyz(); - float3 gamma_balanced = math::pow(linear_color, 1.0f / math::max(gamma, float3(1e-6f))); + const float3 gamma_balanced = math::pow(gain_balanced, 1.0f / math::max(gamma, float3(1e-6f))); return float4(gamma_balanced, color.w); } diff --git a/tests/files/compositor/color/compositor_renders/node_color_balance_lift.png b/tests/files/compositor/color/compositor_renders/node_color_balance_lift.png index d18dce73cad..5d0b10272df 100644 --- a/tests/files/compositor/color/compositor_renders/node_color_balance_lift.png +++ b/tests/files/compositor/color/compositor_renders/node_color_balance_lift.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:688e67a5c9c271c532df3bcc403d792affbc8d7463a683bfda404bf963e03c99 -size 79311 +oid sha256:fa945ec738a49a61a47c42698c9ef5720321b073a958dfdfdc9ff0656967db52 +size 77687 diff --git a/tests/files/compositor/pixel_nodes/compositor_renders/consecutive_pixel_nodes.png b/tests/files/compositor/pixel_nodes/compositor_renders/consecutive_pixel_nodes.png index 96287643d9c..1004d91880e 100644 --- a/tests/files/compositor/pixel_nodes/compositor_renders/consecutive_pixel_nodes.png +++ b/tests/files/compositor/pixel_nodes/compositor_renders/consecutive_pixel_nodes.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b85e3a4c9ac903312adab08d31bf8a12e7a00cd95bceb7f19c8da04485d7c4b -size 49036 +oid sha256:92596e9fcf9a0098e11df01f2f84a8928c8af87b2c668682bc9541a5569e7982 +size 43339 diff --git a/tests/files/compositor/pixel_nodes/compositor_renders/pixel_and_non_pixel_nodes.png b/tests/files/compositor/pixel_nodes/compositor_renders/pixel_and_non_pixel_nodes.png index 2e18f15faec..a0285abcaef 100644 --- a/tests/files/compositor/pixel_nodes/compositor_renders/pixel_and_non_pixel_nodes.png +++ b/tests/files/compositor/pixel_nodes/compositor_renders/pixel_and_non_pixel_nodes.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dc13a22f1346324b33aa0fde366d3d09c29b0c174ebbc9ffaf68892ead41fd63 -size 69743 +oid sha256:2af6a4c199ba6a44fe5f0b50873f9c8d67626c076c11e535144060e4a752d026 +size 69990