Compositor: Turn Color Balance options to inputs
This patch turns the options of the Color Balance node into inputs. In the process, each of the wheels were split into two inputs, a base float and a color. For instance, Gain is controlled using both a Base Gain and Color Gain, the former controls the gain for all channels while the latter controls it per channel. Reference #137223. Pull Request: https://projects.blender.org/blender/blender/pulls/138610
This commit is contained in:
@@ -27,7 +27,7 @@
|
||||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 76
|
||||
#define BLENDER_FILE_SUBVERSION 77
|
||||
|
||||
/* 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
|
||||
|
||||
@@ -1049,6 +1049,80 @@ static void write_compositor_legacy_properties(bNodeTree &node_tree)
|
||||
storage->y1 = y_input->default_value_typed<bNodeSocketValueInt>()->value +
|
||||
height_input->default_value_typed<bNodeSocketValueInt>()->value;
|
||||
}
|
||||
|
||||
if (node->type_legacy == CMP_NODE_COLORBALANCE) {
|
||||
NodeColorBalance *storage = static_cast<NodeColorBalance *>(node->storage);
|
||||
|
||||
{
|
||||
const bNodeSocket *base_input = blender::bke::node_find_socket(
|
||||
*node, SOCK_IN, "Base Lift");
|
||||
const bNodeSocket *color_input = blender::bke::node_find_socket(
|
||||
*node, SOCK_IN, "Color Lift");
|
||||
const float3 value = base_input->default_value_typed<bNodeSocketValueFloat>()->value +
|
||||
float3(
|
||||
color_input->default_value_typed<bNodeSocketValueRGBA>()->value);
|
||||
copy_v3_v3(storage->lift, value);
|
||||
}
|
||||
|
||||
{
|
||||
const bNodeSocket *base_input = blender::bke::node_find_socket(
|
||||
*node, SOCK_IN, "Base Gamma");
|
||||
const bNodeSocket *color_input = blender::bke::node_find_socket(
|
||||
*node, SOCK_IN, "Color Gamma");
|
||||
const float3 value = base_input->default_value_typed<bNodeSocketValueFloat>()->value *
|
||||
float3(
|
||||
color_input->default_value_typed<bNodeSocketValueRGBA>()->value);
|
||||
copy_v3_v3(storage->gamma, value);
|
||||
}
|
||||
|
||||
{
|
||||
const bNodeSocket *base_input = blender::bke::node_find_socket(
|
||||
*node, SOCK_IN, "Base Gain");
|
||||
const bNodeSocket *color_input = blender::bke::node_find_socket(
|
||||
*node, SOCK_IN, "Color Gain");
|
||||
const float3 value = base_input->default_value_typed<bNodeSocketValueFloat>()->value *
|
||||
float3(
|
||||
color_input->default_value_typed<bNodeSocketValueRGBA>()->value);
|
||||
copy_v3_v3(storage->gain, value);
|
||||
}
|
||||
|
||||
{
|
||||
const bNodeSocket *base_input = blender::bke::node_find_socket(
|
||||
*node, SOCK_IN, "Base Power");
|
||||
const bNodeSocket *color_input = blender::bke::node_find_socket(
|
||||
*node, SOCK_IN, "Color Power");
|
||||
const float3 value = base_input->default_value_typed<bNodeSocketValueFloat>()->value *
|
||||
float3(
|
||||
color_input->default_value_typed<bNodeSocketValueRGBA>()->value);
|
||||
copy_v3_v3(storage->power, value);
|
||||
}
|
||||
|
||||
{
|
||||
const bNodeSocket *base_input = blender::bke::node_find_socket(
|
||||
*node, SOCK_IN, "Base Slope");
|
||||
const bNodeSocket *color_input = blender::bke::node_find_socket(
|
||||
*node, SOCK_IN, "Color Slope");
|
||||
const float3 value = base_input->default_value_typed<bNodeSocketValueFloat>()->value *
|
||||
float3(
|
||||
color_input->default_value_typed<bNodeSocketValueRGBA>()->value);
|
||||
copy_v3_v3(storage->slope, value);
|
||||
}
|
||||
|
||||
{
|
||||
const bNodeSocket *base_input = blender::bke::node_find_socket(
|
||||
*node, SOCK_IN, "Base Offset");
|
||||
const bNodeSocket *color_input = blender::bke::node_find_socket(
|
||||
*node, SOCK_IN, "Color Offset");
|
||||
storage->offset_basis = base_input->default_value_typed<bNodeSocketValueFloat>()->value;
|
||||
copy_v3_v3(storage->offset,
|
||||
color_input->default_value_typed<bNodeSocketValueRGBA>()->value);
|
||||
}
|
||||
|
||||
write_input_to_property_float("Input Temperature", storage->input_temperature);
|
||||
write_input_to_property_float("Input Tint", storage->input_tint);
|
||||
write_input_to_property_float("Output Temperature", storage->output_temperature);
|
||||
write_input_to_property_float("Output Tint", storage->output_tint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -999,6 +999,33 @@ static void do_versions_nodetree_customnodes(bNodeTree *ntree, int /*is_group*/)
|
||||
}
|
||||
}
|
||||
|
||||
/* Sync functions update formula parameters for other modes, such that the result is comparable.
|
||||
* Note that the results are not exactly the same due to differences in color handling
|
||||
* (sRGB conversion happens for LGG),
|
||||
* but this keeps settings comparable. */
|
||||
static void color_balance_node_cdl_from_lgg(bNode *node)
|
||||
{
|
||||
NodeColorBalance *n = (NodeColorBalance *)node->storage;
|
||||
|
||||
for (int c = 0; c < 3; c++) {
|
||||
n->slope[c] = (2.0f - n->lift[c]) * n->gain[c];
|
||||
n->offset[c] = (n->lift[c] - 1.0f) * n->gain[c];
|
||||
n->power[c] = (n->gamma[c] != 0.0f) ? 1.0f / n->gamma[c] : 1000000.0f;
|
||||
}
|
||||
}
|
||||
|
||||
static void color_balance_node_lgg_from_cdl(bNode *node)
|
||||
{
|
||||
NodeColorBalance *n = (NodeColorBalance *)node->storage;
|
||||
|
||||
for (int c = 0; c < 3; c++) {
|
||||
float d = n->slope[c] + n->offset[c];
|
||||
n->lift[c] = (d != 0.0f ? n->slope[c] + 2.0f * n->offset[c] / d : 0.0f);
|
||||
n->gain[c] = d;
|
||||
n->gamma[c] = (n->power[c] != 0.0f) ? 1.0f / n->power[c] : 1000000.0f;
|
||||
}
|
||||
}
|
||||
|
||||
static bool strip_colorbalance_update_cb(Strip *strip, void * /*user_data*/)
|
||||
{
|
||||
StripData *data = strip->data;
|
||||
@@ -2677,7 +2704,7 @@ void blo_do_versions_260(FileData *fd, Library * /*lib*/, Main *bmain)
|
||||
NodeColorBalance *n = static_cast<NodeColorBalance *>(node->storage);
|
||||
if (node->custom1 == 0) {
|
||||
/* LGG mode stays the same, just init CDL settings */
|
||||
ntreeCompositColorBalanceSyncFromLGG(ntree, node);
|
||||
color_balance_node_cdl_from_lgg(node);
|
||||
}
|
||||
else if (node->custom1 == 1) {
|
||||
/* CDL previously used same variables as LGG, copy them over
|
||||
@@ -2686,7 +2713,7 @@ void blo_do_versions_260(FileData *fd, Library * /*lib*/, Main *bmain)
|
||||
copy_v3_v3(n->offset, n->lift);
|
||||
copy_v3_v3(n->power, n->gamma);
|
||||
copy_v3_v3(n->slope, n->gain);
|
||||
ntreeCompositColorBalanceSyncFromCDL(ntree, node);
|
||||
color_balance_node_lgg_from_cdl(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3831,6 +3831,151 @@ static void do_version_crop_node_options_to_inputs_animation(bNodeTree *node_tre
|
||||
});
|
||||
}
|
||||
|
||||
/* The options were converted into inputs. */
|
||||
static void do_version_color_balance_node_options_to_inputs(bNodeTree *node_tree, bNode *node)
|
||||
{
|
||||
NodeColorBalance *storage = static_cast<NodeColorBalance *>(node->storage);
|
||||
if (!storage) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!blender::bke::node_find_socket(*node, SOCK_IN, "Color Lift")) {
|
||||
bNodeSocket *input = blender::bke::node_add_static_socket(
|
||||
*node_tree, *node, SOCK_IN, SOCK_RGBA, PROP_NONE, "Color Lift", "Lift");
|
||||
copy_v3_v3(input->default_value_typed<bNodeSocketValueRGBA>()->value, storage->lift);
|
||||
}
|
||||
|
||||
if (!blender::bke::node_find_socket(*node, SOCK_IN, "Color Gamma")) {
|
||||
bNodeSocket *input = blender::bke::node_add_static_socket(
|
||||
*node_tree, *node, SOCK_IN, SOCK_RGBA, PROP_NONE, "Color Gamma", "Gamma");
|
||||
copy_v3_v3(input->default_value_typed<bNodeSocketValueRGBA>()->value, storage->gamma);
|
||||
}
|
||||
|
||||
if (!blender::bke::node_find_socket(*node, SOCK_IN, "Color Gain")) {
|
||||
bNodeSocket *input = blender::bke::node_add_static_socket(
|
||||
*node_tree, *node, SOCK_IN, SOCK_RGBA, PROP_NONE, "Color Gain", "Gain");
|
||||
copy_v3_v3(input->default_value_typed<bNodeSocketValueRGBA>()->value, storage->gain);
|
||||
}
|
||||
|
||||
if (!blender::bke::node_find_socket(*node, SOCK_IN, "Color Offset")) {
|
||||
bNodeSocket *input = blender::bke::node_add_static_socket(
|
||||
*node_tree, *node, SOCK_IN, SOCK_RGBA, PROP_NONE, "Color Offset", "Offset");
|
||||
copy_v3_v3(input->default_value_typed<bNodeSocketValueRGBA>()->value, storage->offset);
|
||||
}
|
||||
|
||||
if (!blender::bke::node_find_socket(*node, SOCK_IN, "Color Power")) {
|
||||
bNodeSocket *input = blender::bke::node_add_static_socket(
|
||||
*node_tree, *node, SOCK_IN, SOCK_RGBA, PROP_NONE, "Color Power", "Power");
|
||||
copy_v3_v3(input->default_value_typed<bNodeSocketValueRGBA>()->value, storage->power);
|
||||
}
|
||||
|
||||
if (!blender::bke::node_find_socket(*node, SOCK_IN, "Color Slope")) {
|
||||
bNodeSocket *input = blender::bke::node_add_static_socket(
|
||||
*node_tree, *node, SOCK_IN, SOCK_RGBA, PROP_NONE, "Color Slope", "Slope");
|
||||
copy_v3_v3(input->default_value_typed<bNodeSocketValueRGBA>()->value, storage->slope);
|
||||
}
|
||||
|
||||
if (!blender::bke::node_find_socket(*node, SOCK_IN, "Base Offset")) {
|
||||
bNodeSocket *input = blender::bke::node_add_static_socket(
|
||||
*node_tree, *node, SOCK_IN, SOCK_FLOAT, PROP_NONE, "Base Offset", "Offset");
|
||||
input->default_value_typed<bNodeSocketValueFloat>()->value = storage->offset_basis;
|
||||
}
|
||||
|
||||
if (!blender::bke::node_find_socket(*node, SOCK_IN, "Input Temperature")) {
|
||||
bNodeSocket *input = blender::bke::node_add_static_socket(*node_tree,
|
||||
*node,
|
||||
SOCK_IN,
|
||||
SOCK_FLOAT,
|
||||
PROP_COLOR_TEMPERATURE,
|
||||
"Input Temperature",
|
||||
"Temperature");
|
||||
input->default_value_typed<bNodeSocketValueFloat>()->value = storage->input_temperature;
|
||||
}
|
||||
|
||||
if (!blender::bke::node_find_socket(*node, SOCK_IN, "Input Tint")) {
|
||||
bNodeSocket *input = blender::bke::node_add_static_socket(
|
||||
*node_tree, *node, SOCK_IN, SOCK_FLOAT, PROP_NONE, "Input Tint", "Tint");
|
||||
input->default_value_typed<bNodeSocketValueFloat>()->value = storage->input_tint;
|
||||
}
|
||||
|
||||
if (!blender::bke::node_find_socket(*node, SOCK_IN, "Output Temperature")) {
|
||||
bNodeSocket *input = blender::bke::node_add_static_socket(*node_tree,
|
||||
*node,
|
||||
SOCK_IN,
|
||||
SOCK_FLOAT,
|
||||
PROP_COLOR_TEMPERATURE,
|
||||
"Output Temperature",
|
||||
"Temperature");
|
||||
input->default_value_typed<bNodeSocketValueFloat>()->value = storage->output_temperature;
|
||||
}
|
||||
|
||||
if (!blender::bke::node_find_socket(*node, SOCK_IN, "Output Tint")) {
|
||||
bNodeSocket *input = blender::bke::node_add_static_socket(
|
||||
*node_tree, *node, SOCK_IN, SOCK_FLOAT, PROP_NONE, "Output Tint", "Tint");
|
||||
input->default_value_typed<bNodeSocketValueFloat>()->value = storage->output_tint;
|
||||
}
|
||||
}
|
||||
|
||||
/* The options were converted into inputs. */
|
||||
static void do_version_color_balance_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, "lift")) {
|
||||
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[3].default_value");
|
||||
}
|
||||
else if (BLI_str_endswith(fcurve->rna_path, "gamma")) {
|
||||
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[5].default_value");
|
||||
}
|
||||
else if (BLI_str_endswith(fcurve->rna_path, "gain")) {
|
||||
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[7].default_value");
|
||||
}
|
||||
else if (BLI_str_endswith(fcurve->rna_path, "offset_basis")) {
|
||||
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[8].default_value");
|
||||
}
|
||||
else if (BLI_str_endswith(fcurve->rna_path, "offset")) {
|
||||
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[9].default_value");
|
||||
}
|
||||
else if (BLI_str_endswith(fcurve->rna_path, "power")) {
|
||||
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[11].default_value");
|
||||
}
|
||||
else if (BLI_str_endswith(fcurve->rna_path, "slope")) {
|
||||
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[13].default_value");
|
||||
}
|
||||
else if (BLI_str_endswith(fcurve->rna_path, "input_temperature")) {
|
||||
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[14].default_value");
|
||||
}
|
||||
else if (BLI_str_endswith(fcurve->rna_path, "input_tint")) {
|
||||
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[15].default_value");
|
||||
}
|
||||
else if (BLI_str_endswith(fcurve->rna_path, "output_temperature")) {
|
||||
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[16].default_value");
|
||||
}
|
||||
else if (BLI_str_endswith(fcurve->rna_path, "output_tint")) {
|
||||
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[17].default_value");
|
||||
}
|
||||
|
||||
/* The RNA path was changed, free the old path. */
|
||||
if (fcurve->rna_path != old_rna_path) {
|
||||
MEM_freeN(old_rna_path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_versions_after_linking_450(FileData * /*fd*/, Main *bmain)
|
||||
{
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 8)) {
|
||||
@@ -4421,6 +4566,19 @@ void do_versions_after_linking_450(FileData * /*fd*/, Main *bmain)
|
||||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 77)) {
|
||||
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_color_balance_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.
|
||||
@@ -5566,6 +5724,19 @@ void blo_do_versions_450(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
|
||||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 77)) {
|
||||
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_color_balance_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. */
|
||||
|
||||
@@ -3,35 +3,291 @@
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gpu_shader_common_color_utils.glsl"
|
||||
#include "gpu_shader_math_matrix_lib.glsl"
|
||||
|
||||
void node_composite_color_balance_lgg(
|
||||
float factor, float4 color, float3 lift, float3 gamma, float3 gain, out float4 result)
|
||||
void node_composite_color_balance_lgg(float factor,
|
||||
float4 color,
|
||||
float base_lift,
|
||||
float4 color_lift,
|
||||
float base_gamma,
|
||||
float4 color_gamma,
|
||||
float base_gain,
|
||||
float4 color_gain,
|
||||
float base_offset,
|
||||
float4 color_offset,
|
||||
float base_power,
|
||||
float4 color_power,
|
||||
float base_slope,
|
||||
float4 color_slope,
|
||||
float input_temperature,
|
||||
float input_tint,
|
||||
float output_temperature,
|
||||
float output_tint,
|
||||
out float4 result)
|
||||
{
|
||||
float3 inverse_lift = 2.0f - lift;
|
||||
float3 srgb_color = linear_rgb_to_srgb(color.rgb);
|
||||
float3 lift_balanced = ((srgb_color - 1.0f) * inverse_lift) + 1.0f;
|
||||
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 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);
|
||||
float3 gamma_balanced = pow(linear_color, 1.0f / gamma);
|
||||
|
||||
result = float4(mix(color.rgb, gamma_balanced, min(factor, 1.0f)), color.a);
|
||||
const float3 gamma = base_gamma * color_gamma.xyz();
|
||||
float3 gamma_balanced = pow(linear_color, 1.0f / max(gamma, float3(1e-6)));
|
||||
|
||||
result = float4(mix(color.xyz(), gamma_balanced, min(factor, 1.0f)), color.w);
|
||||
}
|
||||
|
||||
void node_composite_color_balance_asc_cdl(
|
||||
float factor, float4 color, float3 offset, float3 power, float3 slope, out float4 result)
|
||||
void node_composite_color_balance_asc_cdl(float factor,
|
||||
float4 color,
|
||||
float base_lift,
|
||||
float4 color_lift,
|
||||
float base_gamma,
|
||||
float4 color_gamma,
|
||||
float base_gain,
|
||||
float4 color_gain,
|
||||
float base_offset,
|
||||
float4 color_offset,
|
||||
float base_power,
|
||||
float4 color_power,
|
||||
float base_slope,
|
||||
float4 color_slope,
|
||||
float input_temperature,
|
||||
float input_tint,
|
||||
float output_temperature,
|
||||
float output_tint,
|
||||
out float4 result)
|
||||
{
|
||||
float3 balanced = color.rgb * slope + offset;
|
||||
balanced = pow(max(balanced, float3(0.0f)), power);
|
||||
result = float4(mix(color.rgb, balanced, min(factor, 1.0f)), color.a);
|
||||
const float3 slope = base_slope * color_slope.xyz();
|
||||
const float3 slope_balanced = color.xyz() * slope;
|
||||
|
||||
const float3 offset = base_offset + color_offset.xyz();
|
||||
const float3 offset_balanced = slope_balanced + offset;
|
||||
|
||||
const float3 power = base_power * color_power.xyz();
|
||||
const float3 power_balanced = pow(max(offset_balanced, float3(0.0f)), power);
|
||||
|
||||
result = float4(mix(color.xyz(), power_balanced, min(factor, 1.0f)), color.w);
|
||||
}
|
||||
|
||||
void node_composite_color_balance_whitepoint(float factor,
|
||||
float4 color,
|
||||
float4x4 matrix,
|
||||
out float4 result)
|
||||
void node_composite_color_balance_white_point_constant(float factor,
|
||||
float4 color,
|
||||
float base_lift,
|
||||
float4 color_lift,
|
||||
float base_gamma,
|
||||
float4 color_gamma,
|
||||
float base_gain,
|
||||
float4 color_gain,
|
||||
float base_offset,
|
||||
float4 color_offset,
|
||||
float base_power,
|
||||
float4 color_power,
|
||||
float base_slope,
|
||||
float4 color_slope,
|
||||
float input_temperature,
|
||||
float input_tint,
|
||||
float output_temperature,
|
||||
float output_tint,
|
||||
float4x4 white_point_matrix,
|
||||
out float4 result)
|
||||
{
|
||||
result = mix(color, matrix * color, min(factor, 1.0f));
|
||||
const float3 balanced = to_float3x3(white_point_matrix) * color.xyz;
|
||||
result = float4(mix(color.xyz, balanced, min(factor, 1.0f)), color.w);
|
||||
}
|
||||
|
||||
float3 whitepoint_from_temp_tint(const float temperature, const float tint)
|
||||
{
|
||||
/* Tabulated approximation of the Planckian locus. Based on:
|
||||
*
|
||||
* http://www.brucelindbloom.com/Eqn_XYZ_to_T.html.
|
||||
*
|
||||
* Original source:
|
||||
*
|
||||
* "Color Science: Concepts and Methods, Quantitative Data and Formulae", Second Edition,
|
||||
* Gunter Wyszecki and W. S. Stiles, John Wiley & Sons, 1982, pp. 227, 228.
|
||||
*
|
||||
* Note that the inverse temperature table is multiplied by 10^6 compared to the reference
|
||||
* table. */
|
||||
constexpr float inverse_temperatures[31] = float_array(0.0f,
|
||||
10.0f,
|
||||
20.0f,
|
||||
30.0f,
|
||||
40.0f,
|
||||
50.0f,
|
||||
60.0f,
|
||||
70.0f,
|
||||
80.0f,
|
||||
90.0f,
|
||||
100.0f,
|
||||
125.0f,
|
||||
150.0f,
|
||||
175.0f,
|
||||
200.0f,
|
||||
225.0f,
|
||||
250.0f,
|
||||
275.0f,
|
||||
300.0f,
|
||||
325.0f,
|
||||
350.0f,
|
||||
375.0f,
|
||||
400.0f,
|
||||
425.0f,
|
||||
450.0f,
|
||||
475.0f,
|
||||
500.0f,
|
||||
525.0f,
|
||||
550.0f,
|
||||
575.0f,
|
||||
600.0f);
|
||||
|
||||
constexpr float2 uv_coordinates[31] = float2_array(float2(0.18006f, 0.26352f),
|
||||
float2(0.18066f, 0.26589f),
|
||||
float2(0.18133f, 0.26846f),
|
||||
float2(0.18208f, 0.27119f),
|
||||
float2(0.18293f, 0.27407f),
|
||||
float2(0.18388f, 0.27709f),
|
||||
float2(0.18494f, 0.28021f),
|
||||
float2(0.18611f, 0.28342f),
|
||||
float2(0.18740f, 0.28668f),
|
||||
float2(0.18880f, 0.28997f),
|
||||
float2(0.19032f, 0.29326f),
|
||||
float2(0.19462f, 0.30141f),
|
||||
float2(0.19962f, 0.30921f),
|
||||
float2(0.20525f, 0.31647f),
|
||||
float2(0.21142f, 0.32312f),
|
||||
float2(0.21807f, 0.32909f),
|
||||
float2(0.22511f, 0.33439f),
|
||||
float2(0.23247f, 0.33904f),
|
||||
float2(0.24010f, 0.34308f),
|
||||
float2(0.24792f, 0.34655f),
|
||||
float2(0.25591f, 0.34951f),
|
||||
float2(0.26400f, 0.35200f),
|
||||
float2(0.27218f, 0.35407f),
|
||||
float2(0.28039f, 0.35577f),
|
||||
float2(0.28863f, 0.35714f),
|
||||
float2(0.29685f, 0.35823f),
|
||||
float2(0.30505f, 0.35907f),
|
||||
float2(0.31320f, 0.35968f),
|
||||
float2(0.32129f, 0.36011f),
|
||||
float2(0.32931f, 0.36038f),
|
||||
float2(0.33724f, 0.36051f));
|
||||
|
||||
constexpr float isotherm_parameters[31] = float_array(-0.24341f,
|
||||
-0.25479f,
|
||||
-0.26876f,
|
||||
-0.28539f,
|
||||
-0.30470f,
|
||||
-0.32675f,
|
||||
-0.35156f,
|
||||
-0.37915f,
|
||||
-0.40955f,
|
||||
-0.44278f,
|
||||
-0.47888f,
|
||||
-0.58204f,
|
||||
-0.70471f,
|
||||
-0.84901f,
|
||||
-1.0182f,
|
||||
-1.2168f,
|
||||
-1.4512f,
|
||||
-1.7298f,
|
||||
-2.0637f,
|
||||
-2.4681f,
|
||||
-2.9641f,
|
||||
-3.5814f,
|
||||
-4.3633f,
|
||||
-5.3762f,
|
||||
-6.7262f,
|
||||
-8.5955f,
|
||||
-11.324f,
|
||||
-15.628f,
|
||||
-23.325f,
|
||||
-40.770f,
|
||||
-116.45f);
|
||||
|
||||
/* Compute the inverse temperature, multiplying by 10^6 since the reference table is scaled by
|
||||
* that factor. We also make sure we don't divide by zero and are less than 600 for a simpler
|
||||
* algorithm as will be seen. */
|
||||
const float inverse_temperature = clamp(1e6f / max(1e-6f, temperature), 0.0f, 600.0f - 1e-6f);
|
||||
|
||||
/* Find the index of the table entry that is less than or equal the inverse temperature. Note
|
||||
* that the table is two arithmetic sequences concatenated, [0, 10, 20, ...] followed by [100,
|
||||
* 125, 150, ...]. So the index in the first sequence is simply the floor division by 10, and the
|
||||
* second is simply the floor division by 25, while of course adjusting for the start value of
|
||||
* the sequence. */
|
||||
const int i = inverse_temperature < 100.0f ? (int(inverse_temperature) / 10) :
|
||||
(int(inverse_temperature - 100.0f) / 25 + 10);
|
||||
|
||||
/* Find interpolation factor. */
|
||||
const float interpolation_factor = (inverse_temperature - inverse_temperatures[i]) /
|
||||
(inverse_temperatures[i + 1] - inverse_temperatures[i]);
|
||||
|
||||
/* Interpolate point along Planckian locus. */
|
||||
const float2 uv = mix(uv_coordinates[i], uv_coordinates[i + 1], interpolation_factor);
|
||||
|
||||
/* Compute and interpolate isotherm. */
|
||||
const float2 lower_isotherm = normalize(float2(1.0f, isotherm_parameters[i]));
|
||||
const float2 higher_isotherm = normalize(float2(1.0f, isotherm_parameters[i + 1]));
|
||||
const float2 isotherm = normalize(mix(lower_isotherm, higher_isotherm, interpolation_factor));
|
||||
|
||||
/* Offset away from the Planckian locus according to the tint.
|
||||
* Tint is parameterized such that +-3000 tint corresponds to +-1 delta UV. */
|
||||
const float2 tinted_uv = uv - (isotherm * tint / 3000.0f);
|
||||
|
||||
/* Convert CIE 1960 uv -> xyY. */
|
||||
const float x = 3.0f * tinted_uv.x / (2.0f * tinted_uv.x - 8.0f * tinted_uv.y + 4.0f);
|
||||
const float y = 2.0f * tinted_uv.y / (2.0f * tinted_uv.x - 8.0f * tinted_uv.y + 4.0f);
|
||||
|
||||
/* Convert xyY -> XYZ (assuming Y=1). */
|
||||
return float3(x / y, 1.0f, (1.0f - x - y) / y);
|
||||
}
|
||||
|
||||
float3x3 chromatic_adaption_matrix(const float3 from_XYZ, const float3 to_XYZ)
|
||||
{
|
||||
/* Bradford transformation matrix (XYZ -> LMS). */
|
||||
const float3x3 bradford = float3x3(float3(0.8951f, -0.7502f, 0.0389f),
|
||||
float3(0.2664f, 1.7135f, -0.0685f),
|
||||
float3(-0.1614f, 0.0367f, 1.0296f));
|
||||
|
||||
/* Compute white points in LMS space. */
|
||||
const float3 from_LMS = bradford * from_XYZ / from_XYZ.y;
|
||||
const float3 to_LMS = bradford * to_XYZ / to_XYZ.y;
|
||||
|
||||
/* Assemble full transform: XYZ -> LMS -> adapted LMS -> adapted XYZ. */
|
||||
return inverse(bradford) * from_scale(to_LMS / from_LMS) * bradford;
|
||||
}
|
||||
|
||||
void node_composite_color_balance_white_point_variable(float factor,
|
||||
float4 color,
|
||||
float base_lift,
|
||||
float4 color_lift,
|
||||
float base_gamma,
|
||||
float4 color_gamma,
|
||||
float base_gain,
|
||||
float4 color_gain,
|
||||
float base_offset,
|
||||
float4 color_offset,
|
||||
float base_power,
|
||||
float4 color_power,
|
||||
float base_slope,
|
||||
float4 color_slope,
|
||||
float input_temperature,
|
||||
float input_tint,
|
||||
float output_temperature,
|
||||
float output_tint,
|
||||
float4x4 scene_to_xyz,
|
||||
float4x4 xyz_to_scene,
|
||||
out float4 result)
|
||||
{
|
||||
const float3 input_white_point = whitepoint_from_temp_tint(input_temperature, input_tint);
|
||||
const float3 output_white_point = whitepoint_from_temp_tint(output_temperature, output_tint);
|
||||
const float3x3 adaption = chromatic_adaption_matrix(input_white_point, output_white_point);
|
||||
const float3x3 white_point_matrix = to_float3x3(xyz_to_scene) * adaption *
|
||||
to_float3x3(scene_to_xyz);
|
||||
|
||||
const float3 balanced = white_point_matrix * color.xyz;
|
||||
result = float4(mix(color.xyz, balanced, min(factor, 1.0f)), color.w);
|
||||
}
|
||||
|
||||
@@ -648,6 +648,26 @@ inline auto SI6_SO(const char *name,
|
||||
name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4, In5, In6>());
|
||||
}
|
||||
|
||||
/** Build multi-function with 8 single-input and 1 single-output parameter. */
|
||||
template<typename In1,
|
||||
typename In2,
|
||||
typename In3,
|
||||
typename In4,
|
||||
typename In5,
|
||||
typename In6,
|
||||
typename In7,
|
||||
typename In8,
|
||||
typename Out1,
|
||||
typename ElementFn,
|
||||
typename ExecPreset = exec_presets::Materialized>
|
||||
inline auto SI8_SO(const char *name,
|
||||
const ElementFn element_fn,
|
||||
const ExecPreset exec_preset = exec_presets::Materialized())
|
||||
{
|
||||
return detail::build_multi_function_with_n_inputs_one_output<Out1>(
|
||||
name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4, In5, In6, In7, In8>());
|
||||
}
|
||||
|
||||
/** Build multi-function with 1 single-mutable parameter. */
|
||||
template<typename Mut1, typename ElementFn, typename ExecPreset = exec_presets::AllSpanOrSingle>
|
||||
inline auto SM(const char *name,
|
||||
|
||||
@@ -1346,22 +1346,22 @@ typedef struct NodeLensDist {
|
||||
|
||||
typedef struct NodeColorBalance {
|
||||
/* ASC CDL parameters. */
|
||||
float slope[3];
|
||||
float offset[3];
|
||||
float power[3];
|
||||
float offset_basis;
|
||||
float slope[3] DNA_DEPRECATED;
|
||||
float offset[3] DNA_DEPRECATED;
|
||||
float power[3] DNA_DEPRECATED;
|
||||
float offset_basis DNA_DEPRECATED;
|
||||
char _pad[4];
|
||||
|
||||
/* LGG parameters. */
|
||||
float lift[3];
|
||||
float gamma[3];
|
||||
float gain[3];
|
||||
float lift[3] DNA_DEPRECATED;
|
||||
float gamma[3] DNA_DEPRECATED;
|
||||
float gain[3] DNA_DEPRECATED;
|
||||
|
||||
/* White-point parameters. */
|
||||
float input_temperature;
|
||||
float input_tint;
|
||||
float output_temperature;
|
||||
float output_tint;
|
||||
float input_temperature DNA_DEPRECATED;
|
||||
float input_tint DNA_DEPRECATED;
|
||||
float output_temperature DNA_DEPRECATED;
|
||||
float output_tint DNA_DEPRECATED;
|
||||
} NodeColorBalance;
|
||||
|
||||
typedef struct NodeColorspill {
|
||||
|
||||
@@ -3509,20 +3509,6 @@ static PointerRNA rna_NodeOutputFile_slot_file_get(CollectionPropertyIterator *i
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void rna_NodeColorBalance_update_lgg(Main *bmain, Scene *scene, PointerRNA *ptr)
|
||||
{
|
||||
ntreeCompositColorBalanceSyncFromLGG(reinterpret_cast<bNodeTree *>(ptr->owner_id),
|
||||
ptr->data_as<bNode>());
|
||||
rna_Node_update(bmain, scene, ptr);
|
||||
}
|
||||
|
||||
static void rna_NodeColorBalance_update_cdl(Main *bmain, Scene *scene, PointerRNA *ptr)
|
||||
{
|
||||
ntreeCompositColorBalanceSyncFromCDL(reinterpret_cast<bNodeTree *>(ptr->owner_id),
|
||||
ptr->data_as<bNode>());
|
||||
rna_Node_update(bmain, scene, ptr);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Glare Node Compatibility Setters/Getters.
|
||||
*
|
||||
@@ -4242,6 +4228,19 @@ static const char node_input_extend_bounds[] = "Extend Bounds";
|
||||
static const char node_input_x[] = "X";
|
||||
static const char node_input_y[] = "Y";
|
||||
|
||||
/* Color Balance node. */
|
||||
static const char node_input_color_lift[] = "Color Lift";
|
||||
static const char node_input_color_gamma[] = "Color Gamma";
|
||||
static const char node_input_color_gain[] = "Color Gain";
|
||||
static const char node_input_color_offset[] = "Color Offset";
|
||||
static const char node_input_color_power[] = "Color Power";
|
||||
static const char node_input_color_slope[] = "Color Slope";
|
||||
static const char node_input_base_offset[] = "Base Offset";
|
||||
static const char node_input_input_temperature[] = "Input Temperature";
|
||||
static const char node_input_input_tint[] = "Input Tint";
|
||||
static const char node_input_output_temperature[] = "Output Temperature";
|
||||
static const char node_input_output_tint[] = "Output Tint";
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* White Balance Node.
|
||||
*/
|
||||
@@ -4249,29 +4248,49 @@ static const char node_input_y[] = "Y";
|
||||
static void rna_NodeColorBalance_input_whitepoint_get(PointerRNA *ptr, float value[3])
|
||||
{
|
||||
bNode *node = ptr->data_as<bNode>();
|
||||
NodeColorBalance *n = static_cast<NodeColorBalance *>(node->storage);
|
||||
IMB_colormanagement_get_whitepoint(n->input_temperature, n->input_tint, value);
|
||||
bNodeSocket *temperature_input = blender::bke::node_find_socket(
|
||||
*node, SOCK_IN, "Input Temperature");
|
||||
bNodeSocket *tint_input = blender::bke::node_find_socket(*node, SOCK_IN, "Input Tint");
|
||||
IMB_colormanagement_get_whitepoint(
|
||||
temperature_input->default_value_typed<bNodeSocketValueFloat>()->value,
|
||||
tint_input->default_value_typed<bNodeSocketValueFloat>()->value,
|
||||
value);
|
||||
}
|
||||
|
||||
static void rna_NodeColorBalance_input_whitepoint_set(PointerRNA *ptr, const float value[3])
|
||||
{
|
||||
bNode *node = ptr->data_as<bNode>();
|
||||
NodeColorBalance *n = static_cast<NodeColorBalance *>(node->storage);
|
||||
IMB_colormanagement_set_whitepoint(value, n->input_temperature, n->input_tint);
|
||||
bNodeSocket *temperature_input = blender::bke::node_find_socket(
|
||||
*node, SOCK_IN, "Input Temperature");
|
||||
bNodeSocket *tint_input = blender::bke::node_find_socket(*node, SOCK_IN, "Input Tint");
|
||||
IMB_colormanagement_set_whitepoint(
|
||||
value,
|
||||
temperature_input->default_value_typed<bNodeSocketValueFloat>()->value,
|
||||
tint_input->default_value_typed<bNodeSocketValueFloat>()->value);
|
||||
}
|
||||
|
||||
static void rna_NodeColorBalance_output_whitepoint_get(PointerRNA *ptr, float value[3])
|
||||
{
|
||||
bNode *node = ptr->data_as<bNode>();
|
||||
NodeColorBalance *n = static_cast<NodeColorBalance *>(node->storage);
|
||||
IMB_colormanagement_get_whitepoint(n->output_temperature, n->output_tint, value);
|
||||
bNodeSocket *temperature_input = blender::bke::node_find_socket(
|
||||
*node, SOCK_IN, "Output Temperature");
|
||||
bNodeSocket *tint_input = blender::bke::node_find_socket(*node, SOCK_IN, "Output Tint");
|
||||
IMB_colormanagement_get_whitepoint(
|
||||
temperature_input->default_value_typed<bNodeSocketValueFloat>()->value,
|
||||
tint_input->default_value_typed<bNodeSocketValueFloat>()->value,
|
||||
value);
|
||||
}
|
||||
|
||||
static void rna_NodeColorBalance_output_whitepoint_set(PointerRNA *ptr, const float value[3])
|
||||
{
|
||||
bNode *node = ptr->data_as<bNode>();
|
||||
NodeColorBalance *n = static_cast<NodeColorBalance *>(node->storage);
|
||||
IMB_colormanagement_set_whitepoint(value, n->output_temperature, n->output_tint);
|
||||
bNodeSocket *temperature_input = blender::bke::node_find_socket(
|
||||
*node, SOCK_IN, "Output Temperature");
|
||||
bNodeSocket *tint_input = blender::bke::node_find_socket(*node, SOCK_IN, "Output Tint");
|
||||
IMB_colormanagement_set_whitepoint(
|
||||
value,
|
||||
temperature_input->default_value_typed<bNodeSocketValueFloat>()->value,
|
||||
tint_input->default_value_typed<bNodeSocketValueFloat>()->value);
|
||||
}
|
||||
|
||||
static void rna_NodeCryptomatte_source_set(PointerRNA *ptr, int value)
|
||||
@@ -9200,79 +9219,127 @@ static void def_cmp_colorbalance(BlenderRNA * /*brna*/, StructRNA *srna)
|
||||
RNA_def_struct_sdna_from(srna, "NodeColorBalance", "storage");
|
||||
|
||||
prop = RNA_def_property(srna, "lift", PROP_FLOAT, PROP_COLOR_GAMMA);
|
||||
RNA_def_property_float_sdna(prop, nullptr, "lift");
|
||||
RNA_def_property_float_funcs(
|
||||
prop,
|
||||
"rna_node_array_property_to_input_getter<float, node_input_color_lift>",
|
||||
"rna_node_array_property_to_input_setter<float, node_input_color_lift>",
|
||||
nullptr);
|
||||
RNA_def_property_array(prop, 3);
|
||||
RNA_def_property_float_array_default(prop, default_1);
|
||||
RNA_def_property_ui_range(prop, 0, 2, 0.1, 3);
|
||||
RNA_def_property_ui_text(prop, "Lift", "Correction for shadows");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeColorBalance_update_lgg");
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Lift", "Correction for shadows. (Deprecated: Use Lift input instead.)");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "gamma", PROP_FLOAT, PROP_COLOR_GAMMA);
|
||||
RNA_def_property_float_sdna(prop, nullptr, "gamma");
|
||||
RNA_def_property_float_funcs(
|
||||
prop,
|
||||
"rna_node_array_property_to_input_getter<float, node_input_color_gamma>",
|
||||
"rna_node_array_property_to_input_setter<float, node_input_color_gamma>",
|
||||
nullptr);
|
||||
RNA_def_property_array(prop, 3);
|
||||
RNA_def_property_float_array_default(prop, default_1);
|
||||
RNA_def_property_ui_range(prop, 0, 2, 0.1, 3);
|
||||
RNA_def_property_ui_text(prop, "Gamma", "Correction for midtones");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeColorBalance_update_lgg");
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Gamma", "Correction for midtones. (Deprecated: Use Gamma input instead.)");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "gain", PROP_FLOAT, PROP_COLOR_GAMMA);
|
||||
RNA_def_property_float_sdna(prop, nullptr, "gain");
|
||||
RNA_def_property_float_funcs(
|
||||
prop,
|
||||
"rna_node_array_property_to_input_getter<float, node_input_color_gain>",
|
||||
"rna_node_array_property_to_input_setter<float, node_input_color_gain>",
|
||||
nullptr);
|
||||
RNA_def_property_array(prop, 3);
|
||||
RNA_def_property_float_array_default(prop, default_1);
|
||||
RNA_def_property_ui_range(prop, 0, 2, 0.1, 3);
|
||||
RNA_def_property_ui_text(prop, "Gain", "Correction for highlights");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeColorBalance_update_lgg");
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Gain", "Correction for highlights. (Deprecated: Use Gain input instead.)");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_COLOR_GAMMA);
|
||||
RNA_def_property_float_sdna(prop, nullptr, "offset");
|
||||
RNA_def_property_float_funcs(
|
||||
prop,
|
||||
"rna_node_array_property_to_input_getter<float, node_input_color_offset>",
|
||||
"rna_node_array_property_to_input_setter<float, node_input_color_offset>",
|
||||
nullptr);
|
||||
RNA_def_property_array(prop, 3);
|
||||
RNA_def_property_ui_range(prop, 0, 1, 0.1, 3);
|
||||
RNA_def_property_ui_text(prop, "Offset", "Correction for entire tonal range");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeColorBalance_update_cdl");
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Offset",
|
||||
"Correction for entire tonal range. (Deprecated: Use Offset input instead.)");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "power", PROP_FLOAT, PROP_COLOR_GAMMA);
|
||||
RNA_def_property_float_sdna(prop, nullptr, "power");
|
||||
RNA_def_property_float_funcs(
|
||||
prop,
|
||||
"rna_node_array_property_to_input_getter<float, node_input_color_power>",
|
||||
"rna_node_array_property_to_input_setter<float, node_input_color_power>",
|
||||
nullptr);
|
||||
RNA_def_property_array(prop, 3);
|
||||
RNA_def_property_float_array_default(prop, default_1);
|
||||
RNA_def_property_range(prop, 0.0f, FLT_MAX);
|
||||
RNA_def_property_ui_range(prop, 0, 2, 0.1, 3);
|
||||
RNA_def_property_ui_text(prop, "Power", "Correction for midtones");
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Power", "Correction for midtones. (Deprecated: Use Power input instead.)");
|
||||
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_MOVIECLIP);
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeColorBalance_update_cdl");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "slope", PROP_FLOAT, PROP_COLOR_GAMMA);
|
||||
RNA_def_property_float_sdna(prop, nullptr, "slope");
|
||||
RNA_def_property_float_funcs(
|
||||
prop,
|
||||
"rna_node_array_property_to_input_getter<float, node_input_color_slope>",
|
||||
"rna_node_array_property_to_input_setter<float, node_input_color_slope>",
|
||||
nullptr);
|
||||
RNA_def_property_array(prop, 3);
|
||||
RNA_def_property_float_array_default(prop, default_1);
|
||||
RNA_def_property_range(prop, 0.0f, FLT_MAX);
|
||||
RNA_def_property_ui_range(prop, 0, 2, 0.1, 3);
|
||||
RNA_def_property_ui_text(prop, "Slope", "Correction for highlights");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeColorBalance_update_cdl");
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Slope", "Correction for highlights. (Deprecated: Use Slope input instead.)");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "offset_basis", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_float_funcs(prop,
|
||||
"rna_node_property_to_input_getter<float, node_input_base_offset>",
|
||||
"rna_node_property_to_input_setter<float, node_input_base_offset>",
|
||||
nullptr);
|
||||
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
|
||||
RNA_def_property_ui_range(prop, -1.0, 1.0, 1.0, 2);
|
||||
RNA_def_property_ui_text(prop, "Basis", "Support negative color by using this as the RGB basis");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeColorBalance_update_cdl");
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Basis",
|
||||
"Support negative color by using this as the RGB basis. (Deprecated: "
|
||||
"Use Offset Basis input instead.)");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "input_temperature", PROP_FLOAT, PROP_COLOR_TEMPERATURE);
|
||||
RNA_def_property_float_sdna(prop, nullptr, "input_temperature");
|
||||
RNA_def_property_float_funcs(
|
||||
prop,
|
||||
"rna_node_property_to_input_getter<float, node_input_input_temperature>",
|
||||
"rna_node_property_to_input_setter<float, node_input_input_temperature>",
|
||||
nullptr);
|
||||
RNA_def_property_float_default(prop, 6500.0f);
|
||||
RNA_def_property_range(prop, 1800.0f, 100000.0f);
|
||||
RNA_def_property_ui_range(prop, 2000.0f, 11000.0f, 100, 0);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Input Temperature", "Color temperature of the input's white point");
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Input Temperature",
|
||||
"Color temperature of the input's white point. (Deprecated: Use Input "
|
||||
"Temperature input instead.)");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "input_tint", PROP_FLOAT, PROP_FACTOR);
|
||||
RNA_def_property_float_sdna(prop, nullptr, "input_tint");
|
||||
RNA_def_property_float_funcs(prop,
|
||||
"rna_node_property_to_input_getter<float, node_input_input_tint>",
|
||||
"rna_node_property_to_input_setter<float, node_input_input_tint>",
|
||||
nullptr);
|
||||
RNA_def_property_float_default(prop, 10.0f);
|
||||
RNA_def_property_range(prop, -500.0f, 500.0f);
|
||||
RNA_def_property_ui_range(prop, -150.0f, 150.0f, 1, 1);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Input Tint",
|
||||
"Color tint of the input's white point (the default of 10 matches daylight)");
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Input Tint",
|
||||
"Color tint of the input's white point (the default of 10 matches "
|
||||
"daylight). (Deprecated: Use Input Tint input instead.)");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "input_whitepoint", PROP_FLOAT, PROP_COLOR);
|
||||
@@ -9288,23 +9355,32 @@ static void def_cmp_colorbalance(BlenderRNA * /*brna*/, StructRNA *srna)
|
||||
RNA_def_property_update(prop, NC_WINDOW, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "output_temperature", PROP_FLOAT, PROP_COLOR_TEMPERATURE);
|
||||
RNA_def_property_float_sdna(prop, nullptr, "output_temperature");
|
||||
RNA_def_property_float_funcs(
|
||||
prop,
|
||||
"rna_node_property_to_input_getter<float, node_input_output_temperature>",
|
||||
"rna_node_property_to_input_setter<float, node_input_output_temperature>",
|
||||
nullptr);
|
||||
RNA_def_property_float_default(prop, 6500.0f);
|
||||
RNA_def_property_range(prop, 1800.0f, 100000.0f);
|
||||
RNA_def_property_ui_range(prop, 2000.0f, 11000.0f, 100, 0);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Output Temperature", "Color temperature of the output's white point");
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Output Temperature",
|
||||
"Color temperature of the output's white point. (Deprecated: Use "
|
||||
"Output Temperature input instead.)");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "output_tint", PROP_FLOAT, PROP_FACTOR);
|
||||
RNA_def_property_float_sdna(prop, nullptr, "output_tint");
|
||||
RNA_def_property_float_funcs(prop,
|
||||
"rna_node_property_to_input_getter<float, node_input_output_tint>",
|
||||
"rna_node_property_to_input_setter<float, node_input_output_tint>",
|
||||
nullptr);
|
||||
RNA_def_property_float_default(prop, 10.0f);
|
||||
RNA_def_property_range(prop, -500.0f, 500.0f);
|
||||
RNA_def_property_ui_range(prop, -150.0f, 150.0f, 1, 1);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Output Tint",
|
||||
"Color tint of the output's white point (the default of 10 matches daylight)");
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Output Tint",
|
||||
"Color tint of the output's white point (the default of 10 matches "
|
||||
"daylight). (Deprecated: Use Output Tint input instead.)");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "output_whitepoint", PROP_FLOAT, PROP_COLOR);
|
||||
|
||||
@@ -77,9 +77,6 @@ void ntreeCompositOutputFileUniqueLayer(ListBase *list,
|
||||
const char defname[],
|
||||
char delim);
|
||||
|
||||
void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node);
|
||||
void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *ntree, bNode *node);
|
||||
|
||||
void ntreeCompositCryptomatteSyncFromAdd(bNode *node);
|
||||
void ntreeCompositCryptomatteSyncFromRemove(bNode *node);
|
||||
bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node);
|
||||
|
||||
@@ -32,40 +32,20 @@
|
||||
|
||||
/* ******************* Color Balance ********************************* */
|
||||
|
||||
/* Sync functions update formula parameters for other modes, such that the result is comparable.
|
||||
* Note that the results are not exactly the same due to differences in color handling
|
||||
* (sRGB conversion happens for LGG),
|
||||
* but this keeps settings comparable. */
|
||||
|
||||
void ntreeCompositColorBalanceSyncFromLGG(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeColorBalance *n = (NodeColorBalance *)node->storage;
|
||||
|
||||
for (int c = 0; c < 3; c++) {
|
||||
n->slope[c] = (2.0f - n->lift[c]) * n->gain[c];
|
||||
n->offset[c] = (n->lift[c] - 1.0f) * n->gain[c];
|
||||
n->power[c] = (n->gamma[c] != 0.0f) ? 1.0f / n->gamma[c] : 1000000.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void ntreeCompositColorBalanceSyncFromCDL(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeColorBalance *n = (NodeColorBalance *)node->storage;
|
||||
|
||||
for (int c = 0; c < 3; c++) {
|
||||
float d = n->slope[c] + n->offset[c];
|
||||
n->lift[c] = (d != 0.0f ? n->slope[c] + 2.0f * n->offset[c] / d : 0.0f);
|
||||
n->gain[c] = d;
|
||||
n->gamma[c] = (n->power[c] != 0.0f) ? 1.0f / n->power[c] : 1000000.0f;
|
||||
}
|
||||
}
|
||||
|
||||
namespace blender::nodes::node_composite_colorbalance_cc {
|
||||
|
||||
NODE_STORAGE_FUNCS(NodeColorBalance)
|
||||
|
||||
static void cmp_node_colorbalance_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.use_custom_socket_order();
|
||||
|
||||
b.add_output<decl::Color>("Image");
|
||||
|
||||
b.add_layout([](uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) {
|
||||
layout->prop(ptr, "correction_method", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
});
|
||||
|
||||
b.add_input<decl::Float>("Fac")
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
@@ -75,153 +55,165 @@ static void cmp_node_colorbalance_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_output<decl::Color>("Image");
|
||||
|
||||
b.add_input<decl::Float>("Lift", "Base Lift")
|
||||
.default_value(0.0f)
|
||||
.min(-1.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Correction for shadows");
|
||||
b.add_input<decl::Color>("Lift", "Color Lift")
|
||||
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
|
||||
.description("Correction for shadows");
|
||||
b.add_input<decl::Float>("Gamma", "Base Gamma")
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(2.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Correction for midtones");
|
||||
b.add_input<decl::Color>("Gamma", "Color Gamma")
|
||||
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
|
||||
.description("Correction for midtones");
|
||||
b.add_input<decl::Float>("Gain", "Base Gain")
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(2.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Correction for highlights");
|
||||
b.add_input<decl::Color>("Gain", "Color Gain")
|
||||
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
|
||||
.description("Correction for highlights");
|
||||
|
||||
b.add_input<decl::Float>("Offset", "Base Offset")
|
||||
.default_value(0.0f)
|
||||
.min(-1.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Correction for shadows");
|
||||
b.add_input<decl::Color>("Offset", "Color Offset")
|
||||
.default_value({0.0f, 0.0f, 0.0f, 1.0f})
|
||||
.description("Correction for shadows");
|
||||
b.add_input<decl::Float>("Power", "Base Power")
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(2.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Correction for midtones");
|
||||
b.add_input<decl::Color>("Power", "Color Power")
|
||||
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
|
||||
.description("Correction for midtones");
|
||||
b.add_input<decl::Float>("Slope", "Base Slope")
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(2.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Correction for highlights");
|
||||
b.add_input<decl::Color>("Slope", "Color Slope")
|
||||
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
|
||||
.description("Correction for highlights");
|
||||
|
||||
PanelDeclarationBuilder &input_panel = b.add_panel("Input");
|
||||
input_panel.add_input<decl::Float>("Temperature", "Input Temperature")
|
||||
.default_value(6500.0f)
|
||||
.subtype(PROP_COLOR_TEMPERATURE)
|
||||
.min(1800.0f)
|
||||
.max(100000.0f)
|
||||
.description("Color temperature of the input's white point");
|
||||
input_panel.add_input<decl::Float>("Tint", "Input Tint")
|
||||
.default_value(10.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.min(-150.0f)
|
||||
.max(150.0f)
|
||||
.description("Color tint of the input's white point (the default of 10 matches daylight)");
|
||||
input_panel.add_layout([](uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) {
|
||||
uiLayout *split = &layout->split(0.2f, false);
|
||||
uiTemplateCryptoPicker(split, ptr, "input_whitepoint", ICON_EYEDROPPER);
|
||||
});
|
||||
|
||||
PanelDeclarationBuilder &output_panel = b.add_panel("Output");
|
||||
output_panel.add_input<decl::Float>("Temperature", "Output Temperature")
|
||||
.default_value(6500.0f)
|
||||
.subtype(PROP_COLOR_TEMPERATURE)
|
||||
.min(1800.0f)
|
||||
.max(100000.0f)
|
||||
.description("Color temperature of the output's white point");
|
||||
output_panel.add_input<decl::Float>("Tint", "Output Tint")
|
||||
.default_value(10.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.min(-150.0f)
|
||||
.max(150.0f)
|
||||
.description("Color tint of the output's white point (the default of 10 matches daylight)");
|
||||
output_panel.add_layout([](uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) {
|
||||
uiLayout *split = &layout->split(0.2f, false);
|
||||
uiTemplateCryptoPicker(split, ptr, "output_whitepoint", ICON_EYEDROPPER);
|
||||
});
|
||||
}
|
||||
|
||||
static void node_composit_init_colorbalance(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
/* All members are deprecated and needn't be set, but the data is still allocated for forward
|
||||
* compatibility. */
|
||||
NodeColorBalance *n = MEM_callocN<NodeColorBalance>(__func__);
|
||||
|
||||
n->lift[0] = n->lift[1] = n->lift[2] = 1.0f;
|
||||
n->gamma[0] = n->gamma[1] = n->gamma[2] = 1.0f;
|
||||
n->gain[0] = n->gain[1] = n->gain[2] = 1.0f;
|
||||
|
||||
n->slope[0] = n->slope[1] = n->slope[2] = 1.0f;
|
||||
n->offset[0] = n->offset[1] = n->offset[2] = 0.0f;
|
||||
n->power[0] = n->power[1] = n->power[2] = 1.0f;
|
||||
|
||||
n->input_temperature = n->output_temperature = 6500.0f;
|
||||
n->input_tint = n->output_tint = 10.0f;
|
||||
node->storage = n;
|
||||
}
|
||||
|
||||
static void node_composit_buts_colorbalance(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
uiLayout *split, *col, *row;
|
||||
|
||||
layout->prop(ptr, "correction_method", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
|
||||
const int method = RNA_enum_get(ptr, "correction_method");
|
||||
|
||||
if (method == CMP_NODE_COLOR_BALANCE_LGG) {
|
||||
split = &layout->split(0.0f, false);
|
||||
col = &split->column(false);
|
||||
uiTemplateColorPicker(col, ptr, "lift", true, true, false, true);
|
||||
row = &col->row(false);
|
||||
row->prop(ptr, "lift", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
|
||||
col = &split->column(false);
|
||||
uiTemplateColorPicker(col, ptr, "gamma", true, true, true, true);
|
||||
row = &col->row(false);
|
||||
row->prop(ptr, "gamma", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
|
||||
col = &split->column(false);
|
||||
uiTemplateColorPicker(col, ptr, "gain", true, true, true, true);
|
||||
row = &col->row(false);
|
||||
row->prop(ptr, "gain", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
else if (method == CMP_NODE_COLOR_BALANCE_ASC_CDL) {
|
||||
split = &layout->split(0.0f, false);
|
||||
col = &split->column(false);
|
||||
uiTemplateColorPicker(col, ptr, "offset", true, true, false, true);
|
||||
row = &col->row(false);
|
||||
row->prop(ptr, "offset", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
col->prop(ptr, "offset_basis", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
|
||||
col = &split->column(false);
|
||||
uiTemplateColorPicker(col, ptr, "power", true, true, false, true);
|
||||
row = &col->row(false);
|
||||
row->prop(ptr, "power", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
|
||||
col = &split->column(false);
|
||||
uiTemplateColorPicker(col, ptr, "slope", true, true, false, true);
|
||||
row = &col->row(false);
|
||||
row->prop(ptr, "slope", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
else if (method == CMP_NODE_COLOR_BALANCE_WHITEPOINT) {
|
||||
split = &layout->split(0.0f, false);
|
||||
|
||||
col = &split->column(false);
|
||||
row = &col->row(true);
|
||||
row->label(IFACE_("Input"), ICON_NONE);
|
||||
uiTemplateCryptoPicker(row, ptr, "input_whitepoint", ICON_EYEDROPPER);
|
||||
col->prop(
|
||||
ptr, "input_temperature", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Temperature"), ICON_NONE);
|
||||
col->prop(ptr, "input_tint", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Tint"), ICON_NONE);
|
||||
|
||||
col = &split->column(false);
|
||||
row = &col->row(true);
|
||||
row->label(IFACE_("Output"), ICON_NONE);
|
||||
uiTemplateCryptoPicker(row, ptr, "output_whitepoint", ICON_EYEDROPPER);
|
||||
col->prop(
|
||||
ptr, "output_temperature", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Temperature"), ICON_NONE);
|
||||
col->prop(ptr, "output_tint", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Tint"), ICON_NONE);
|
||||
}
|
||||
else {
|
||||
BLI_assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void node_composit_buts_colorbalance_ex(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout->prop(ptr, "correction_method", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
|
||||
const int method = RNA_enum_get(ptr, "correction_method");
|
||||
|
||||
if (method == CMP_NODE_COLOR_BALANCE_LGG) {
|
||||
uiTemplateColorPicker(layout, ptr, "lift", true, true, false, true);
|
||||
layout->prop(ptr, "lift", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
|
||||
uiTemplateColorPicker(layout, ptr, "gamma", true, true, true, true);
|
||||
layout->prop(ptr, "gamma", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
|
||||
uiTemplateColorPicker(layout, ptr, "gain", true, true, true, true);
|
||||
layout->prop(ptr, "gain", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
else if (method == CMP_NODE_COLOR_BALANCE_ASC_CDL) {
|
||||
uiTemplateColorPicker(layout, ptr, "offset", true, true, false, true);
|
||||
layout->prop(ptr, "offset", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
|
||||
uiTemplateColorPicker(layout, ptr, "power", true, true, false, true);
|
||||
layout->prop(ptr, "power", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
|
||||
uiTemplateColorPicker(layout, ptr, "slope", true, true, false, true);
|
||||
layout->prop(ptr, "slope", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
else if (method == CMP_NODE_COLOR_BALANCE_WHITEPOINT) {
|
||||
layout->prop(ptr, "input_temperature", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
layout->prop(ptr, "input_tint", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
layout->prop(ptr, "output_temperature", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
layout->prop(ptr, "output_tint", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
else {
|
||||
BLI_assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
using namespace blender::compositor;
|
||||
|
||||
static CMPNodeColorBalanceMethod get_color_balance_method(const bNode &node)
|
||||
{
|
||||
return static_cast<CMPNodeColorBalanceMethod>(node.custom1);
|
||||
}
|
||||
|
||||
static float3 get_sanitized_gamma(const float3 gamma)
|
||||
static void node_update(bNodeTree *ntree, bNode *node)
|
||||
{
|
||||
return float3(gamma.x == 0.0f ? 1e-6f : gamma.x,
|
||||
gamma.y == 0.0f ? 1e-6f : gamma.y,
|
||||
gamma.z == 0.0f ? 1e-6f : gamma.z);
|
||||
const bool is_lgg = get_color_balance_method(*node) == CMP_NODE_COLOR_BALANCE_LGG;
|
||||
bNodeSocket *base_lift_input = bke::node_find_socket(*node, SOCK_IN, "Base Lift");
|
||||
bNodeSocket *base_gamma_input = bke::node_find_socket(*node, SOCK_IN, "Base Gamma");
|
||||
bNodeSocket *base_gain_input = bke::node_find_socket(*node, SOCK_IN, "Base Gain");
|
||||
bNodeSocket *color_lift_input = bke::node_find_socket(*node, SOCK_IN, "Color Lift");
|
||||
bNodeSocket *color_gamma_input = bke::node_find_socket(*node, SOCK_IN, "Color Gamma");
|
||||
bNodeSocket *color_gain_input = bke::node_find_socket(*node, SOCK_IN, "Color Gain");
|
||||
blender::bke::node_set_socket_availability(*ntree, *base_lift_input, is_lgg);
|
||||
blender::bke::node_set_socket_availability(*ntree, *base_gamma_input, is_lgg);
|
||||
blender::bke::node_set_socket_availability(*ntree, *base_gain_input, is_lgg);
|
||||
blender::bke::node_set_socket_availability(*ntree, *color_lift_input, is_lgg);
|
||||
blender::bke::node_set_socket_availability(*ntree, *color_gamma_input, is_lgg);
|
||||
blender::bke::node_set_socket_availability(*ntree, *color_gain_input, is_lgg);
|
||||
|
||||
const bool is_cdl = get_color_balance_method(*node) == CMP_NODE_COLOR_BALANCE_ASC_CDL;
|
||||
bNodeSocket *base_offset_input = bke::node_find_socket(*node, SOCK_IN, "Base Offset");
|
||||
bNodeSocket *base_power_input = bke::node_find_socket(*node, SOCK_IN, "Base Power");
|
||||
bNodeSocket *base_slope_input = bke::node_find_socket(*node, SOCK_IN, "Base Slope");
|
||||
bNodeSocket *color_offset_input = bke::node_find_socket(*node, SOCK_IN, "Color Offset");
|
||||
bNodeSocket *color_power_input = bke::node_find_socket(*node, SOCK_IN, "Color Power");
|
||||
bNodeSocket *color_slope_input = bke::node_find_socket(*node, SOCK_IN, "Color Slope");
|
||||
blender::bke::node_set_socket_availability(*ntree, *base_offset_input, is_cdl);
|
||||
blender::bke::node_set_socket_availability(*ntree, *base_power_input, is_cdl);
|
||||
blender::bke::node_set_socket_availability(*ntree, *base_slope_input, is_cdl);
|
||||
blender::bke::node_set_socket_availability(*ntree, *color_offset_input, is_cdl);
|
||||
blender::bke::node_set_socket_availability(*ntree, *color_power_input, is_cdl);
|
||||
blender::bke::node_set_socket_availability(*ntree, *color_slope_input, is_cdl);
|
||||
|
||||
const bool is_white_point = get_color_balance_method(*node) == CMP_NODE_COLOR_BALANCE_WHITEPOINT;
|
||||
bNodeSocket *input_temperature_input = bke::node_find_socket(
|
||||
*node, SOCK_IN, "Input Temperature");
|
||||
bNodeSocket *input_tint_input = bke::node_find_socket(*node, SOCK_IN, "Input Tint");
|
||||
bNodeSocket *output_temperature_input = bke::node_find_socket(
|
||||
*node, SOCK_IN, "Output Temperature");
|
||||
bNodeSocket *output_tint_input = bke::node_find_socket(*node, SOCK_IN, "Output Tint");
|
||||
blender::bke::node_set_socket_availability(*ntree, *input_temperature_input, is_white_point);
|
||||
blender::bke::node_set_socket_availability(*ntree, *input_tint_input, is_white_point);
|
||||
blender::bke::node_set_socket_availability(*ntree, *output_temperature_input, is_white_point);
|
||||
blender::bke::node_set_socket_availability(*ntree, *output_tint_input, is_white_point);
|
||||
}
|
||||
|
||||
static float3x3 get_white_point_matrix(const bNode &node)
|
||||
static float3x3 get_white_point_matrix(const float input_temperature,
|
||||
const float input_tint,
|
||||
const float output_temperature,
|
||||
const float output_tint)
|
||||
{
|
||||
const NodeColorBalance &node_color_balance = node_storage(node);
|
||||
const float3x3 scene_to_xyz = IMB_colormanagement_get_scene_linear_to_xyz();
|
||||
const float3x3 xyz_to_scene = IMB_colormanagement_get_xyz_to_scene_linear();
|
||||
const float3 input = blender::math::whitepoint_from_temp_tint(
|
||||
node_color_balance.input_temperature, node_color_balance.input_tint);
|
||||
const float3 output = blender::math::whitepoint_from_temp_tint(
|
||||
node_color_balance.output_temperature, node_color_balance.output_tint);
|
||||
const float3 input = blender::math::whitepoint_from_temp_tint(input_temperature, input_tint);
|
||||
const float3 output = blender::math::whitepoint_from_temp_tint(output_temperature, output_tint);
|
||||
const float3x3 adaption = blender::math::chromatic_adaption_matrix(input, output);
|
||||
return xyz_to_scene * adaption * scene_to_xyz;
|
||||
}
|
||||
@@ -232,47 +224,47 @@ static int node_gpu_material(GPUMaterial *material,
|
||||
GPUNodeStack *inputs,
|
||||
GPUNodeStack *outputs)
|
||||
{
|
||||
const NodeColorBalance &node_color_balance = node_storage(*node);
|
||||
|
||||
switch (get_color_balance_method(*node)) {
|
||||
case CMP_NODE_COLOR_BALANCE_LGG: {
|
||||
const float3 lift = node_color_balance.lift;
|
||||
const float3 gamma = node_color_balance.gamma;
|
||||
const float3 gain = node_color_balance.gain;
|
||||
const float3 sanitized_gamma = get_sanitized_gamma(gamma);
|
||||
|
||||
return GPU_stack_link(material,
|
||||
node,
|
||||
"node_composite_color_balance_lgg",
|
||||
inputs,
|
||||
outputs,
|
||||
GPU_uniform(lift),
|
||||
GPU_uniform(sanitized_gamma),
|
||||
GPU_uniform(gain));
|
||||
return GPU_stack_link(material, node, "node_composite_color_balance_lgg", inputs, outputs);
|
||||
}
|
||||
case CMP_NODE_COLOR_BALANCE_ASC_CDL: {
|
||||
const float3 offset = node_color_balance.offset;
|
||||
const float3 power = node_color_balance.power;
|
||||
const float3 slope = node_color_balance.slope;
|
||||
const float3 full_offset = node_color_balance.offset_basis + offset;
|
||||
|
||||
return GPU_stack_link(material,
|
||||
node,
|
||||
"node_composite_color_balance_asc_cdl",
|
||||
inputs,
|
||||
outputs,
|
||||
GPU_uniform(full_offset),
|
||||
GPU_uniform(power),
|
||||
GPU_uniform(slope));
|
||||
return GPU_stack_link(
|
||||
material, node, "node_composite_color_balance_asc_cdl", inputs, outputs);
|
||||
}
|
||||
case CMP_NODE_COLOR_BALANCE_WHITEPOINT: {
|
||||
const float3x3 matrix = get_white_point_matrix(*node);
|
||||
const bNodeSocket &input_temperature = node->input_by_identifier("Input Temperature");
|
||||
const bNodeSocket &input_tint = node->input_by_identifier("Input Tint");
|
||||
const bNodeSocket &output_temperature = node->input_by_identifier("Output Temperature");
|
||||
const bNodeSocket &output_tint = node->input_by_identifier("Output Tint");
|
||||
|
||||
/* If all inputs are not linked, compute the white point matrix on the host and pass it to
|
||||
* the shader. */
|
||||
if (input_temperature.is_directly_linked() && input_tint.is_directly_linked() &&
|
||||
output_temperature.is_directly_linked() && output_tint.is_directly_linked())
|
||||
{
|
||||
const float3x3 white_point_matrix = get_white_point_matrix(
|
||||
input_temperature.default_value_typed<bNodeSocketValueFloat>()->value,
|
||||
input_tint.default_value_typed<bNodeSocketValueFloat>()->value,
|
||||
output_temperature.default_value_typed<bNodeSocketValueFloat>()->value,
|
||||
output_tint.default_value_typed<bNodeSocketValueFloat>()->value);
|
||||
return GPU_stack_link(material,
|
||||
node,
|
||||
"node_composite_color_balance_white_point_constant",
|
||||
inputs,
|
||||
outputs,
|
||||
GPU_uniform(blender::float4x4(white_point_matrix).base_ptr()));
|
||||
}
|
||||
|
||||
const float3x3 scene_to_xyz = IMB_colormanagement_get_scene_linear_to_xyz();
|
||||
const float3x3 xyz_to_scene = IMB_colormanagement_get_xyz_to_scene_linear();
|
||||
return GPU_stack_link(material,
|
||||
node,
|
||||
"node_composite_color_balance_whitepoint",
|
||||
"node_composite_color_balance_white_point_variable",
|
||||
inputs,
|
||||
outputs,
|
||||
GPU_uniform(blender::float4x4(matrix).base_ptr()));
|
||||
GPU_uniform(blender::float4x4(scene_to_xyz).base_ptr()),
|
||||
GPU_uniform(blender::float4x4(xyz_to_scene).base_ptr()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,83 +273,205 @@ static int node_gpu_material(GPUMaterial *material,
|
||||
|
||||
static float4 color_balance_lgg(const float factor,
|
||||
const float4 &color,
|
||||
const float3 &lift,
|
||||
const float3 &gamma,
|
||||
const float3 &gain)
|
||||
const float &base_lift,
|
||||
const float4 &color_lift,
|
||||
const float &base_gamma,
|
||||
const float4 &color_gamma,
|
||||
const float &base_gain,
|
||||
const float4 &color_gain)
|
||||
{
|
||||
float3 inverse_lift = 2.0f - lift;
|
||||
float3 srgb_color;
|
||||
linearrgb_to_srgb_v3_v3(srgb_color, color);
|
||||
float3 lift_balanced = ((srgb_color - 1.0f) * inverse_lift) + 1.0f;
|
||||
|
||||
const float3 lift = base_lift + color_lift.xyz();
|
||||
const float3 lift_balanced = ((srgb_color - 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);
|
||||
float3 gamma_balanced = math::pow(linear_color, 1.0f / gamma);
|
||||
|
||||
const float3 gamma = base_gamma * color_gamma.xyz();
|
||||
float3 gamma_balanced = math::pow(linear_color, 1.0f / math::max(gamma, float3(1e-6f)));
|
||||
|
||||
return float4(math::interpolate(color.xyz(), gamma_balanced, math::min(factor, 1.0f)), color.w);
|
||||
}
|
||||
|
||||
static float4 color_balance_asc_cdl(const float factor,
|
||||
const float4 &color,
|
||||
const float3 &offset,
|
||||
const float3 &power,
|
||||
const float3 &slope)
|
||||
const float &base_offset,
|
||||
const float4 &color_offset,
|
||||
const float &base_power,
|
||||
const float4 &color_power,
|
||||
const float &base_slope,
|
||||
const float4 &color_slope)
|
||||
{
|
||||
float3 balanced = color.xyz() * slope + offset;
|
||||
balanced = math::pow(math::max(balanced, float3(0.0f)), power);
|
||||
const float3 slope = base_slope * color_slope.xyz();
|
||||
const float3 slope_balanced = color.xyz() * slope;
|
||||
|
||||
const float3 offset = base_offset + color_offset.xyz();
|
||||
const float3 offset_balanced = slope_balanced + offset;
|
||||
|
||||
const float3 power = base_power * color_power.xyz();
|
||||
const float3 power_balanced = math::pow(math::max(offset_balanced, float3(0.0f)), power);
|
||||
|
||||
return float4(math::interpolate(color.xyz(), power_balanced, math::min(factor, 1.0f)), color.w);
|
||||
}
|
||||
|
||||
static float4 color_balance_white_point_constant(const float factor,
|
||||
const float4 &color,
|
||||
const float3x3 &white_point_matrix)
|
||||
{
|
||||
const float3 balanced = white_point_matrix * color.xyz();
|
||||
return float4(math::interpolate(color.xyz(), balanced, math::min(factor, 1.0f)), color.w);
|
||||
}
|
||||
|
||||
static float4 color_balance_white_point_variable(const float factor,
|
||||
const float4 &color,
|
||||
const float input_temperature,
|
||||
const float input_tint,
|
||||
const float output_temperature,
|
||||
const float output_tint,
|
||||
const float3x3 &scene_to_xyz,
|
||||
const float3x3 &xyz_to_scene)
|
||||
{
|
||||
const float3 input = blender::math::whitepoint_from_temp_tint(input_temperature, input_tint);
|
||||
const float3 output = blender::math::whitepoint_from_temp_tint(output_temperature, output_tint);
|
||||
const float3x3 adaption = blender::math::chromatic_adaption_matrix(input, output);
|
||||
const float3x3 white_point_matrix = xyz_to_scene * adaption * scene_to_xyz;
|
||||
|
||||
const float3 balanced = white_point_matrix * color.xyz();
|
||||
return float4(math::interpolate(color.xyz(), balanced, math::min(factor, 1.0f)), color.w);
|
||||
}
|
||||
|
||||
class ColorBalanceWhitePointFunction : public mf::MultiFunction {
|
||||
public:
|
||||
ColorBalanceWhitePointFunction()
|
||||
{
|
||||
static const mf::Signature signature = []() {
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"Color Balance White Point", signature};
|
||||
builder.single_input<float>("Factor");
|
||||
builder.single_input<float4>("Color");
|
||||
builder.single_input<float>("Input Temperature");
|
||||
builder.single_input<float>("Input Tint");
|
||||
builder.single_input<float>("Output Temperature");
|
||||
builder.single_input<float>("Output Tint");
|
||||
builder.single_output<float4>("Result");
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
|
||||
{
|
||||
const VArray<float> factor_array = params.readonly_single_input<float>(0, "Factor");
|
||||
const VArray<float4> color_array = params.readonly_single_input<float4>(1, "Color");
|
||||
const VArray<float> input_temperature_array = params.readonly_single_input<float>(
|
||||
2, "Input Temperature");
|
||||
const VArray<float> input_tint_array = params.readonly_single_input<float>(3, "Input Tint");
|
||||
const VArray<float> output_temperature_array = params.readonly_single_input<float>(
|
||||
4, "Output Temperature");
|
||||
const VArray<float> output_tint_array = params.readonly_single_input<float>(5, "Output Tint");
|
||||
|
||||
MutableSpan<float4> result = params.uninitialized_single_output<float4>(6, "Result");
|
||||
|
||||
const std::optional<float> input_temperature_single = input_temperature_array.get_if_single();
|
||||
const std::optional<float> input_tint_single = input_tint_array.get_if_single();
|
||||
const std::optional<float> output_temperature_single =
|
||||
output_temperature_array.get_if_single();
|
||||
const std::optional<float> output_tint_single = output_tint_array.get_if_single();
|
||||
|
||||
if (input_temperature_single.has_value() && input_tint_single.has_value() &&
|
||||
output_temperature_single.has_value() && output_tint_single.has_value())
|
||||
{
|
||||
const float3x3 white_point_matrix = get_white_point_matrix(input_temperature_single.value(),
|
||||
input_tint_single.value(),
|
||||
output_temperature_single.value(),
|
||||
output_tint_single.value());
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
result[i] = color_balance_white_point_constant(
|
||||
factor_array[i], color_array[i], white_point_matrix);
|
||||
});
|
||||
}
|
||||
else {
|
||||
const float3x3 scene_to_xyz = IMB_colormanagement_get_scene_linear_to_xyz();
|
||||
const float3x3 xyz_to_scene = IMB_colormanagement_get_xyz_to_scene_linear();
|
||||
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
result[i] = color_balance_white_point_variable(factor_array[i],
|
||||
color_array[i],
|
||||
input_temperature_array[i],
|
||||
input_tint_array[i],
|
||||
output_temperature_array[i],
|
||||
output_tint_array[i],
|
||||
scene_to_xyz,
|
||||
xyz_to_scene);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void node_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const NodeColorBalance &node_color_balance = node_storage(builder.node());
|
||||
|
||||
switch (get_color_balance_method(builder.node())) {
|
||||
case CMP_NODE_COLOR_BALANCE_LGG: {
|
||||
const float3 lift = node_color_balance.lift;
|
||||
const float3 gamma = node_color_balance.gamma;
|
||||
const float3 gain = node_color_balance.gain;
|
||||
const float3 sanitized_gamma = get_sanitized_gamma(gamma);
|
||||
|
||||
builder.construct_and_set_matching_fn_cb([=]() {
|
||||
return mf::build::SI2_SO<float, float4, float4>(
|
||||
"Color Balance LGG",
|
||||
[=](const float factor, const float4 &color) -> float4 {
|
||||
return color_balance_lgg(factor, color, lift, sanitized_gamma, gain);
|
||||
},
|
||||
mf::build::exec_presets::SomeSpanOrSingle<1>());
|
||||
return mf::build::
|
||||
SI8_SO<float, float4, float, float4, float, float4, float, float4, float4>(
|
||||
"Color Balance LGG",
|
||||
[=](const float factor,
|
||||
const float4 &color,
|
||||
const float base_lift,
|
||||
const float4 &color_lift,
|
||||
const float base_gamma,
|
||||
const float4 &color_gamma,
|
||||
const float base_gain,
|
||||
const float4 &color_gain) -> float4 {
|
||||
return color_balance_lgg(factor,
|
||||
color,
|
||||
base_lift,
|
||||
color_lift,
|
||||
base_gamma,
|
||||
color_gamma,
|
||||
base_gain,
|
||||
color_gain);
|
||||
},
|
||||
mf::build::exec_presets::SomeSpanOrSingle<1>());
|
||||
});
|
||||
break;
|
||||
}
|
||||
case CMP_NODE_COLOR_BALANCE_ASC_CDL: {
|
||||
const float3 offset = node_color_balance.offset;
|
||||
const float3 power = node_color_balance.power;
|
||||
const float3 slope = node_color_balance.slope;
|
||||
const float3 full_offset = node_color_balance.offset_basis + offset;
|
||||
|
||||
builder.construct_and_set_matching_fn_cb([=]() {
|
||||
return mf::build::SI2_SO<float, float4, float4>(
|
||||
"Color Balance ASC CDL",
|
||||
[=](const float factor, const float4 &color) -> float4 {
|
||||
return color_balance_asc_cdl(factor, color, full_offset, power, slope);
|
||||
},
|
||||
mf::build::exec_presets::SomeSpanOrSingle<1>());
|
||||
return mf::build::
|
||||
SI8_SO<float, float4, float, float4, float, float4, float, float4, float4>(
|
||||
"Color Balance ASC CDL",
|
||||
[=](const float factor,
|
||||
const float4 &color,
|
||||
const float base_offset,
|
||||
const float4 &color_offset,
|
||||
const float base_power,
|
||||
const float4 &color_power,
|
||||
const float base_slope,
|
||||
const float4 &color_slope) -> float4 {
|
||||
return color_balance_asc_cdl(factor,
|
||||
color,
|
||||
base_offset,
|
||||
color_offset,
|
||||
base_power,
|
||||
color_power,
|
||||
base_slope,
|
||||
color_slope);
|
||||
},
|
||||
mf::build::exec_presets::SomeSpanOrSingle<1>());
|
||||
});
|
||||
break;
|
||||
}
|
||||
case CMP_NODE_COLOR_BALANCE_WHITEPOINT: {
|
||||
const float4x4 matrix = float4x4(get_white_point_matrix(builder.node()));
|
||||
builder.construct_and_set_matching_fn_cb([=]() {
|
||||
return mf::build::SI2_SO<float, float4, float4>(
|
||||
"Color Balance White Point",
|
||||
[=](const float factor, const float4 &color) -> float4 {
|
||||
return math::interpolate(color, matrix * color, math::min(factor, 1.0f));
|
||||
},
|
||||
mf::build::exec_presets::SomeSpanOrSingle<1>());
|
||||
});
|
||||
const static ColorBalanceWhitePointFunction function;
|
||||
builder.set_matching_fn(function);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -377,9 +491,7 @@ static void register_node_type_cmp_colorbalance()
|
||||
ntype.enum_name_legacy = "COLORBALANCE";
|
||||
ntype.nclass = NODE_CLASS_OP_COLOR;
|
||||
ntype.declare = file_ns::cmp_node_colorbalance_declare;
|
||||
ntype.draw_buttons = file_ns::node_composit_buts_colorbalance;
|
||||
ntype.draw_buttons_ex = file_ns::node_composit_buts_colorbalance_ex;
|
||||
blender::bke::node_type_size(ntype, 400, 200, 400);
|
||||
ntype.updatefunc = file_ns::node_update;
|
||||
ntype.initfunc = file_ns::node_composit_init_colorbalance;
|
||||
blender::bke::node_type_storage(
|
||||
ntype, "NodeColorBalance", node_free_standard_storage, node_copy_standard_storage);
|
||||
|
||||
Reference in New Issue
Block a user