From e7778593f8ba947c41cefb8479203c0cdca61309 Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Wed, 16 Apr 2025 11:59:04 +0200 Subject: [PATCH] Compositor: Turn Tone Map node options into inputs This patch turns the options of the Tone Map node into inputs. In the process, a few changes were made. Input ranges were adjusted to remove artificial limits. The papers indeed mention those limits, but they were only mentioned as typical values, not valid ranges. - The Key option is no longer limited to 1. - Gamma is no longer limited to a value of 3. - Intensity is no longer limited to [-8, 8]. - Contrast is no longer limited to [0, 1]. A few renames were done to clarify options and match the reference papers. - Offset was renamed to Balance, since it is not really an offset, but balances between shadows and highlights. This can be looked up in the paper. - Correction was renamed to Chromatic Adaptation, since it doesn't really correct anything. - Adaptation was renamed to Light Adaptation to distinguish from Chromatic Adaptation and now default to global tone mapping, since this is more useful by default. Reference #137223. Pull Request: https://projects.blender.org/blender/blender/pulls/137589 --- .../blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/intern/node.cc | 11 ++ .../blenloader/intern/versioning_400.cc | 134 +++++++++++++++ source/blender/makesdna/DNA_node_types.h | 9 +- .../blender/makesrna/intern/rna_nodetree.cc | 84 ++++++--- .../composite/nodes/node_composite_tonemap.cc | 160 ++++++++++++------ 6 files changed, 329 insertions(+), 71 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 8704980e525..50b6c412cf1 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 28 +#define BLENDER_FILE_SUBVERSION 29 /* 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/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 90243edacb3..5cd874db0eb 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -757,6 +757,17 @@ static void write_compositor_legacy_properties(bNodeTree &node_tree) write_input_to_property_bool_int16_flag("Use Alpha", node->custom1, 1 << 0); write_input_to_property_bool_int16_flag("Anti-Alias", node->custom2, 1 << 0, true); } + + if (node->type_legacy == CMP_NODE_TONEMAP) { + NodeTonemap *storage = static_cast(node->storage); + write_input_to_property_float("Key", storage->key); + write_input_to_property_float("Balance", storage->offset); + write_input_to_property_float("Gamma", storage->gamma); + write_input_to_property_float("Intensity", storage->f); + write_input_to_property_float("Contrast", storage->m); + write_input_to_property_float("Light Adaptation", storage->a); + write_input_to_property_float("Chromatic Adaptation", storage->c); + } } } diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index a3e6431e785..752c4f7e809 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -2216,6 +2216,114 @@ static void do_version_z_combine_node_options_to_inputs_animation(bNodeTree *nod }); } +/* The options were converted into inputs. */ +static void do_version_tone_map_node_options_to_inputs(bNodeTree *node_tree, bNode *node) +{ + NodeTonemap *storage = static_cast(node->storage); + if (!storage) { + return; + } + + if (!blender::bke::node_find_socket(*node, SOCK_IN, "Key")) { + bNodeSocket *input = blender::bke::node_add_static_socket( + *node_tree, *node, SOCK_IN, SOCK_FLOAT, PROP_NONE, "Key", "Key"); + input->default_value_typed()->value = storage->key; + } + + if (!blender::bke::node_find_socket(*node, SOCK_IN, "Balance")) { + bNodeSocket *input = blender::bke::node_add_static_socket( + *node_tree, *node, SOCK_IN, SOCK_FLOAT, PROP_NONE, "Balance", "Balance"); + input->default_value_typed()->value = storage->offset; + } + + if (!blender::bke::node_find_socket(*node, SOCK_IN, "Gamma")) { + bNodeSocket *input = blender::bke::node_add_static_socket( + *node_tree, *node, SOCK_IN, SOCK_FLOAT, PROP_NONE, "Gamma", "Gamma"); + input->default_value_typed()->value = storage->gamma; + } + + if (!blender::bke::node_find_socket(*node, SOCK_IN, "Intensity")) { + bNodeSocket *input = blender::bke::node_add_static_socket( + *node_tree, *node, SOCK_IN, SOCK_FLOAT, PROP_NONE, "Intensity", "Intensity"); + input->default_value_typed()->value = storage->f; + } + + if (!blender::bke::node_find_socket(*node, SOCK_IN, "Contrast")) { + bNodeSocket *input = blender::bke::node_add_static_socket( + *node_tree, *node, SOCK_IN, SOCK_FLOAT, PROP_NONE, "Contrast", "Contrast"); + input->default_value_typed()->value = storage->m; + } + + if (!blender::bke::node_find_socket(*node, SOCK_IN, "Light Adaptation")) { + bNodeSocket *input = blender::bke::node_add_static_socket(*node_tree, + *node, + SOCK_IN, + SOCK_FLOAT, + PROP_FACTOR, + "Light Adaptation", + "Light Adaptation"); + input->default_value_typed()->value = storage->a; + } + + if (!blender::bke::node_find_socket(*node, SOCK_IN, "Chromatic Adaptation")) { + bNodeSocket *input = blender::bke::node_add_static_socket(*node_tree, + *node, + SOCK_IN, + SOCK_FLOAT, + PROP_FACTOR, + "Chromatic Adaptation", + "Chromatic Adaptation"); + input->default_value_typed()->value = storage->c; + } +} + +/* The options were converted into inputs. */ +static void do_version_tone_map_node_options_to_inputs_animation(bNodeTree *node_tree, bNode *node) +{ + /* Compute the RNA path of the node. */ + char escaped_node_name[sizeof(node->name) * 2 + 1]; + BLI_str_escape(escaped_node_name, node->name, sizeof(escaped_node_name)); + const std::string node_rna_path = fmt::format("nodes[\"{}\"]", escaped_node_name); + + BKE_fcurves_id_cb(&node_tree->id, [&](ID * /*id*/, FCurve *fcurve) { + /* The FCurve does not belong to the node since its RNA path doesn't start with the node's RNA + * path. */ + if (!blender::StringRef(fcurve->rna_path).startswith(node_rna_path)) { + return; + } + + /* Change the RNA path of the FCurve from the old properties to the new inputs, adjusting the + * values of the FCurves frames when needed. */ + char *old_rna_path = fcurve->rna_path; + if (BLI_str_endswith(fcurve->rna_path, "key")) { + fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[1].default_value"); + } + else if (BLI_str_endswith(fcurve->rna_path, "offset")) { + fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[2].default_value"); + } + else if (BLI_str_endswith(fcurve->rna_path, "gamma")) { + fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[3].default_value"); + } + else if (BLI_str_endswith(fcurve->rna_path, "intensity")) { + fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[4].default_value"); + } + else if (BLI_str_endswith(fcurve->rna_path, "contrast")) { + fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[5].default_value"); + } + else if (BLI_str_endswith(fcurve->rna_path, "adaptation")) { + fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[6].default_value"); + } + else if (BLI_str_endswith(fcurve->rna_path, "correction")) { + fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[7].default_value"); + } + + /* The RNA path was changed, free the old path. */ + if (fcurve->rna_path != old_rna_path) { + MEM_freeN(old_rna_path); + } + }); +} + static void do_version_viewer_shortcut(bNodeTree *node_tree) { LISTBASE_FOREACH_MUTABLE (bNode *, node, &node_tree->nodes) { @@ -2748,6 +2856,19 @@ void do_versions_after_linking_400(FileData *fd, Main *bmain) FOREACH_NODETREE_END; } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 29)) { + 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_TONEMAP) { + do_version_tone_map_node_options_to_inputs_animation(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. @@ -7398,6 +7519,19 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) FOREACH_NODETREE_END; } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 29)) { + 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_TONEMAP) { + do_version_tone_map_node_options_to_inputs(node_tree, node); + } + } + } + } + FOREACH_NODETREE_END; + } + /* Always run this versioning (keep at the bottom of the function). Meshes are written with the * legacy format which always needs to be converted to the new format on file load. To be moved * to a subversion check in 5.0. */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index a31c2d32382..3b4ad466b36 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1274,8 +1274,13 @@ typedef struct NodeGlare { /** Tone-map node. */ typedef struct NodeTonemap { - float key, offset, gamma; - float f, m, a, c; + float key DNA_DEPRECATED; + float offset DNA_DEPRECATED; + float gamma DNA_DEPRECATED; + float f DNA_DEPRECATED; + float m DNA_DEPRECATED; + float a DNA_DEPRECATED; + float c DNA_DEPRECATED; int type; } NodeTonemap; diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 884b9672ce8..f5c2f05e2fe 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -3889,6 +3889,15 @@ static const char node_input_invert_alpha[] = "Invert Alpha"; static const char node_input_use_alpha[] = "Use Alpha"; static const char node_input_anti_alias[] = "Anti-Alias"; +/* Tone Map node. */ +static const char node_input_key[] = "Key"; +static const char node_input_balance[] = "Balance"; +static const char node_input_gamma[] = "Gamma"; +static const char node_input_intensity[] = "Intensity"; +static const char node_input_contrast[] = "Contrast"; +static const char node_input_light_adaptation[] = "Light Adaptation"; +static const char node_input_chromatic_adaptation[] = "Chromatic Adaptation"; + /* -------------------------------------------------------------------- * White Balance Node. */ @@ -8487,50 +8496,87 @@ static void def_cmp_tonemap(BlenderRNA * /*brna*/, StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); prop = RNA_def_property(srna, "key", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, nullptr, "key"); + RNA_def_property_float_funcs(prop, + "rna_node_property_to_input_getter", + "rna_node_property_to_input_setter", + nullptr); RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Key", "The value the average luminance is mapped to"); + RNA_def_property_ui_text( + prop, + "Key", + "The value the average luminance is mapped to. (Deprecated: Use Key input instead.)"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, nullptr, "offset"); + RNA_def_property_float_funcs(prop, + "rna_node_property_to_input_getter", + "rna_node_property_to_input_setter", + nullptr); RNA_def_property_range(prop, 0.001f, 10.0f); - RNA_def_property_ui_text( - prop, - "Offset", - "Normally always 1, but can be used as an extra control to alter the brightness curve"); + RNA_def_property_ui_text(prop, + "Offset", + "Normally always 1, but can be used as an extra control to alter the " + "brightness curve. (Deprecated: Use Balance input instead.)"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); prop = RNA_def_property(srna, "gamma", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, nullptr, "gamma"); + RNA_def_property_float_funcs(prop, + "rna_node_property_to_input_getter", + "rna_node_property_to_input_setter", + nullptr); RNA_def_property_range(prop, 0.001f, 3.0f); - RNA_def_property_ui_text(prop, "Gamma", "If not used, set to 1"); + RNA_def_property_ui_text( + prop, "Gamma", "If not used, set to 1. (Deprecated: Use Gamma input instead.)"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); prop = RNA_def_property(srna, "intensity", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, nullptr, "f"); + RNA_def_property_float_funcs(prop, + "rna_node_property_to_input_getter", + "rna_node_property_to_input_setter", + nullptr); RNA_def_property_range(prop, -8.0f, 8.0f); - RNA_def_property_ui_text( - prop, "Intensity", "If less than zero, darkens image; otherwise, makes it brighter"); + RNA_def_property_ui_text(prop, + "Intensity", + "If less than zero, darkens image; otherwise, makes it brighter. " + "(Deprecated: Use Intensity input instead.)"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); prop = RNA_def_property(srna, "contrast", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, nullptr, "m"); + RNA_def_property_float_funcs(prop, + "rna_node_property_to_input_getter", + "rna_node_property_to_input_setter", + nullptr); RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Contrast", "Set to 0 to use estimate from input image"); + RNA_def_property_ui_text( + prop, + "Contrast", + "Set to 0 to use estimate from input image. (Deprecated: Use Contrast input instead.)"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); prop = RNA_def_property(srna, "adaptation", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, nullptr, "a"); + RNA_def_property_float_funcs( + prop, + "rna_node_property_to_input_getter", + "rna_node_property_to_input_setter", + nullptr); RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Adaptation", "If 0, global; if 1, based on pixel intensity"); + RNA_def_property_ui_text(prop, + "Adaptation", + "If 0, global; if 1, based on pixel intensity. (Deprecated: Use Light " + "Adaptation input instead.)"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); prop = RNA_def_property(srna, "correction", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, nullptr, "c"); + RNA_def_property_float_funcs( + prop, + "rna_node_property_to_input_getter", + "rna_node_property_to_input_setter", + nullptr); RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text( - prop, "Color Correction", "If 0, same for all channels; if 1, each independent"); + RNA_def_property_ui_text(prop, + "Color Correction", + "If 0, same for all channels; if 1, each independent (Deprecated: Use " + "Chromatic Adaptation input instead.)"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } diff --git a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc index d351bdc8ae0..14fedf23584 100644 --- a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc +++ b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc @@ -35,58 +35,97 @@ static void cmp_node_tonemap_declare(NodeDeclarationBuilder &b) b.add_input("Image") .default_value({1.0f, 1.0f, 1.0f, 1.0f}) .compositor_domain_priority(0); + + b.add_input("Key") + .default_value(0.18f) + .min(0.0f) + .description( + "The luminance that will be mapped to the log average luminance, typically set to the " + "middle gray value") + .compositor_expects_single_value(); + b.add_input("Balance") + .default_value(1.0f) + .min(0.0f) + .description( + "Balances low and high luminance areas. Lower values emphasize details in shadows, " + "while higher values compress highlights more smoothly") + .compositor_expects_single_value(); + b.add_input("Gamma") + .default_value(1.0f) + .min(0.0f) + .description("Gamma correction factor applied after tone mapping") + .compositor_expects_single_value(); + + b.add_input("Intensity") + .default_value(0.0f) + .description( + "Controls the intensity of the image, lower values makes it darker while higher values " + "makes it lighter") + .compositor_expects_single_value(); + b.add_input("Contrast") + .default_value(0.0f) + .min(0.0f) + .description( + "Controls the contrast of the image. Zero automatically sets the contrast based on its " + "global range for better luminance distribution") + .compositor_expects_single_value(); + b.add_input("Light Adaptation") + .default_value(0.0f) + .subtype(PROP_FACTOR) + .min(0.0f) + .max(1.0f) + .description( + "Specifies if tone mapping operates on the entire image or per pixel, 0 means the " + "entire image, 1 means it is per pixel, and values in between blends between both") + .compositor_expects_single_value(); + b.add_input("Chromatic Adaptation") + .default_value(0.0f) + .subtype(PROP_FACTOR) + .min(0.0f) + .max(1.0f) + .description( + "Specifies if tone mapping operates on the luminance or on each channel independently, " + "0 means it uses luminance, 1 means it is per channel, and values in between blends " + "between both") + .compositor_expects_single_value(); + b.add_output("Image"); } static void node_composit_init_tonemap(bNodeTree * /*ntree*/, bNode *node) { NodeTonemap *ntm = MEM_callocN(__func__); - ntm->type = 1; - ntm->key = 0.18; - ntm->offset = 1; - ntm->gamma = 1; - ntm->f = 0; - ntm->m = 0; /* Actual value is set according to input. */ - /* Default a of 1 works well with natural HDR images, but not always so for CGI. - * Maybe should use 0 or at least lower initial value instead. */ - ntm->a = 1; - ntm->c = 0; + ntm->type = CMP_NODE_TONE_MAP_PHOTORECEPTOR; node->storage = ntm; } static void node_composit_buts_tonemap(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { - uiLayout *col; + uiItemR(layout, ptr, "tonemap_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "tonemap_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); - if (RNA_enum_get(ptr, "tonemap_type") == 0) { - uiItemR( - col, ptr, "key", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, std::nullopt, ICON_NONE); - uiItemR(col, ptr, "offset", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE); - uiItemR(col, ptr, "gamma", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE); - } - else { - uiItemR(col, ptr, "intensity", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE); - uiItemR(col, - ptr, - "contrast", - UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, - std::nullopt, - ICON_NONE); - uiItemR(col, - ptr, - "adaptation", - UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, - std::nullopt, - ICON_NONE); - uiItemR(col, - ptr, - "correction", - UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, - std::nullopt, - ICON_NONE); - } +static void node_update(bNodeTree *ntree, bNode *node) +{ + const bool is_simple = node_storage(*node).type == CMP_NODE_TONE_MAP_SIMPLE; + + bNodeSocket *key_input = bke::node_find_socket(*node, SOCK_IN, "Key"); + bNodeSocket *balance_input = bke::node_find_socket(*node, SOCK_IN, "Balance"); + bNodeSocket *gamma_input = bke::node_find_socket(*node, SOCK_IN, "Gamma"); + + blender::bke::node_set_socket_availability(*ntree, *key_input, is_simple); + blender::bke::node_set_socket_availability(*ntree, *balance_input, is_simple); + blender::bke::node_set_socket_availability(*ntree, *gamma_input, is_simple); + + bNodeSocket *intensity_input = bke::node_find_socket(*node, SOCK_IN, "Intensity"); + bNodeSocket *contrast_input = bke::node_find_socket(*node, SOCK_IN, "Contrast"); + bNodeSocket *light_adaptation_input = bke::node_find_socket(*node, SOCK_IN, "Light Adaptation"); + bNodeSocket *chromatic_adaptation_input = bke::node_find_socket( + *node, SOCK_IN, "Chromatic Adaptation"); + + blender::bke::node_set_socket_availability(*ntree, *intensity_input, !is_simple); + blender::bke::node_set_socket_availability(*ntree, *contrast_input, !is_simple); + blender::bke::node_set_socket_availability(*ntree, *light_adaptation_input, !is_simple); + blender::bke::node_set_socket_availability(*ntree, *chromatic_adaptation_input, !is_simple); } using namespace blender::compositor; @@ -134,7 +173,7 @@ class ToneMapOperation : public NodeOperation { { const float luminance_scale = compute_luminance_scale(); const float luminance_scale_blend_factor = compute_luminance_scale_blend_factor(); - const float gamma = node_storage(bnode()).gamma; + const float gamma = this->get_gamma(); const float inverse_gamma = gamma != 0.0f ? 1.0f / gamma : 0.0f; GPUShader *shader = context().get_shader("compositor_tone_map_simple"); @@ -163,7 +202,7 @@ class ToneMapOperation : public NodeOperation { { const float luminance_scale = compute_luminance_scale(); const float luminance_scale_blend_factor = compute_luminance_scale_blend_factor(); - const float gamma = node_storage(bnode()).gamma; + const float gamma = this->get_gamma(); const float inverse_gamma = gamma != 0.0f ? 1.0f / gamma : 0.0f; const Result &image = get_input("Image"); @@ -195,7 +234,7 @@ class ToneMapOperation : public NodeOperation { float compute_luminance_scale() { const float geometric_mean = compute_geometric_mean_of_luminance(); - return geometric_mean != 0.0 ? node_storage(bnode()).key / geometric_mean : 0.0f; + return geometric_mean != 0.0 ? this->get_key() / geometric_mean : 0.0f; } /* Computes equation (1) from Reinhard's 2002 paper. However, note that the equation in the paper @@ -208,6 +247,11 @@ class ToneMapOperation : public NodeOperation { return std::exp(compute_average_log_luminance()); } + float get_key() + { + return math::max(0.0f, this->get_input("Key").get_single_value_default(0.18f)); + } + /* Equation (3) from Reinhard's 2002 paper blends between high luminance scaling for high * luminance values and low luminance scaling for low luminance values. This is done by adding 1 * to the denominator, since for low luminance values, the denominator will be close to 1 and for @@ -216,7 +260,12 @@ class ToneMapOperation : public NodeOperation { * a parameter to the user for more flexibility. */ float compute_luminance_scale_blend_factor() { - return node_storage(bnode()).offset; + return math::max(0.0f, this->get_input("Balance").get_single_value_default(1.0f)); + } + + float get_gamma() + { + return math::max(0.0f, this->get_input("Gamma").get_single_value_default(1.0f)); } /* Tone mapping based on equation (1) and the trilinear interpolation between equations (6) and @@ -345,15 +394,15 @@ class ToneMapOperation : public NodeOperation { /* Computes equation (5) from Reinhard's 2005 paper. */ float compute_intensity() { - return std::exp(-node_storage(bnode()).f); + return std::exp(-this->get_intensity()); } /* If the contrast is not zero, return it, otherwise, a zero contrast denote automatic derivation * of the contrast value based on equations (2) and (4) from Reinhard's 2005 paper. */ float compute_contrast() { - if (node_storage(bnode()).m != 0.0f) { - return node_storage(bnode()).m; + if (this->get_contrast() != 0.0f) { + return this->get_contrast(); } const float log_maximum_luminance = compute_log_maximum_luminance(); @@ -399,14 +448,26 @@ class ToneMapOperation : public NodeOperation { return std::log(math::max(minimum, 1e-5f)); } + float get_intensity() + { + return this->get_input("Intensity").get_single_value_default(0.0f); + } + + float get_contrast() + { + return math::max(0.0f, this->get_input("Contrast").get_single_value_default(0.0f)); + } + float get_chromatic_adaptation() { - return node_storage(bnode()).c; + return math::clamp( + this->get_input("Chromatic Adaptation").get_single_value_default(0.0f), 0.0f, 1.0f); } float get_light_adaptation() { - return node_storage(bnode()).a; + return math::clamp( + this->get_input("Light Adaptation").get_single_value_default(0.0f), 0.0f, 1.0f); } CMPNodeToneMapType get_type() @@ -436,6 +497,7 @@ void register_node_type_cmp_tonemap() ntype.enum_name_legacy = "TONEMAP"; ntype.nclass = NODE_CLASS_OP_COLOR; ntype.declare = file_ns::cmp_node_tonemap_declare; + ntype.updatefunc = file_ns::node_update; ntype.draw_buttons = file_ns::node_composit_buts_tonemap; ntype.initfunc = file_ns::node_composit_init_tonemap; blender::bke::node_type_storage(