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
This commit is contained in:
Omar Emara
2025-04-16 11:59:04 +02:00
committed by Omar Emara
parent cc741fbf99
commit e7778593f8
6 changed files with 329 additions and 71 deletions

View File

@@ -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

View File

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

View File

@@ -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<NodeTonemap *>(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<bNodeSocketValueFloat>()->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<bNodeSocketValueFloat>()->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<bNodeSocketValueFloat>()->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<bNodeSocketValueFloat>()->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<bNodeSocketValueFloat>()->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<bNodeSocketValueFloat>()->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<bNodeSocketValueFloat>()->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. */

View File

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

View File

@@ -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<float, node_input_key>",
"rna_node_property_to_input_setter<float, node_input_key>",
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<float, node_input_balance>",
"rna_node_property_to_input_setter<float, node_input_balance>",
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<float, node_input_gamma>",
"rna_node_property_to_input_setter<float, node_input_gamma>",
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<float, node_input_intensity>",
"rna_node_property_to_input_setter<float, node_input_intensity>",
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<float, node_input_contrast>",
"rna_node_property_to_input_setter<float, node_input_contrast>",
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<float, node_input_light_adaptation>",
"rna_node_property_to_input_setter<float, node_input_light_adaptation>",
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<float, node_input_chromatic_adaptation>",
"rna_node_property_to_input_setter<float, node_input_chromatic_adaptation>",
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");
}

View File

@@ -35,58 +35,97 @@ static void cmp_node_tonemap_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Color>("Image")
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
.compositor_domain_priority(0);
b.add_input<decl::Float>("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<decl::Float>("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<decl::Float>("Gamma")
.default_value(1.0f)
.min(0.0f)
.description("Gamma correction factor applied after tone mapping")
.compositor_expects_single_value();
b.add_input<decl::Float>("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<decl::Float>("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<decl::Float>("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<decl::Float>("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<decl::Color>("Image");
}
static void node_composit_init_tonemap(bNodeTree * /*ntree*/, bNode *node)
{
NodeTonemap *ntm = MEM_callocN<NodeTonemap>(__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(