Blender V5.0
node_composite_colorcorrection.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2006 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_math_base.hh"
10#include "BLI_math_vector.hh"
12
14
15#include "NOD_multi_function.hh"
16
18
19#include "UI_resources.hh"
20
21#include "GPU_material.hh"
22
24
25/* ******************* Color Correction ********************************* */
26
28
30{
31 b.is_function_node();
32 b.use_custom_socket_order();
33 b.allow_any_socket_order();
34
35 b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}).hide_value();
36 b.add_output<decl::Color>("Image").align_with_previous();
37
38 b.add_input<decl::Float>("Mask").default_value(1.0f).min(0.0f).max(1.0f);
39
40 PanelDeclarationBuilder &master_panel = b.add_panel("Master").default_closed(true);
41 master_panel.add_input<decl::Float>("Saturation", "Master Saturation")
42 .default_value(1.0f)
44 .min(0.0f)
45 .max(4.0f)
46 .description("Controls the saturation of the entire image");
47 master_panel.add_input<decl::Float>("Contrast", "Master Contrast")
48 .default_value(1.0f)
50 .min(0.0f)
51 .max(4.0f)
52 .description("Controls the contrast of the entire image");
53 master_panel.add_input<decl::Float>("Gamma", "Master Gamma")
54 .default_value(1.0f)
56 .min(0.0f)
57 .max(4.0f)
58 .description("Controls the gamma of the entire image");
59 master_panel.add_input<decl::Float>("Gain", "Master Gain")
60 .default_value(1.0f)
62 .min(0.0f)
63 .max(4.0f)
64 .description("Controls the gain of the entire image");
65 master_panel.add_input<decl::Float>("Offset", "Master Offset")
66 .default_value(0.0f)
68 .min(-1.0f)
69 .max(1.0f)
70 .description("Controls the offset of the entire image");
71
72 PanelDeclarationBuilder &highlights_panel = b.add_panel("Highlights").default_closed(true);
73 highlights_panel.add_input<decl::Float>("Saturation", "Highlights Saturation")
74 .default_value(1.0f)
76 .min(0.0f)
77 .max(4.0f)
78 .description("Controls the saturation of the highlights");
79 highlights_panel.add_input<decl::Float>("Contrast", "Highlights Contrast")
80 .default_value(1.0f)
82 .min(0.0f)
83 .max(4.0f)
84 .description("Controls the contrast of the highlights");
85 highlights_panel.add_input<decl::Float>("Gamma", "Highlights Gamma")
86 .default_value(1.0f)
88 .min(0.0f)
89 .max(4.0f)
90 .description("Controls the gamma of the highlights");
91 highlights_panel.add_input<decl::Float>("Gain", "Highlights Gain")
92 .default_value(1.0f)
94 .min(0.0f)
95 .max(4.0f)
96 .description("Controls the gain of the highlights");
97 highlights_panel.add_input<decl::Float>("Offset", "Highlights Offset")
98 .default_value(0.0f)
100 .min(-1.0f)
101 .max(1.0f)
102 .description("Controls the offset of the highlights");
103
104 PanelDeclarationBuilder &midtones_panel = b.add_panel("Midtones").default_closed(true);
105 midtones_panel.add_input<decl::Float>("Saturation", "Midtones Saturation")
106 .default_value(1.0f)
108 .min(0.0f)
109 .max(4.0f)
110 .description("Controls the saturation of the midtones");
111 midtones_panel.add_input<decl::Float>("Contrast", "Midtones Contrast")
112 .default_value(1.0f)
114 .min(0.0f)
115 .max(4.0f)
116 .description("Controls the contrast of the midtones");
117 midtones_panel.add_input<decl::Float>("Gamma", "Midtones Gamma")
118 .default_value(1.0f)
120 .min(0.0f)
121 .max(4.0f)
122 .description("Controls the gamma of the midtones");
123 midtones_panel.add_input<decl::Float>("Gain", "Midtones Gain")
124 .default_value(1.0f)
126 .min(0.0f)
127 .max(4.0f)
128 .description("Controls the gain of the midtones");
129 midtones_panel.add_input<decl::Float>("Offset", "Midtones Offset")
130 .default_value(0.0f)
132 .min(-1.0f)
133 .max(1.0f)
134 .description("Controls the offset of the midtones");
135
136 PanelDeclarationBuilder &shadows_panel = b.add_panel("Shadows").default_closed(true);
137 shadows_panel.add_input<decl::Float>("Saturation", "Shadows Saturation")
138 .default_value(1.0f)
140 .min(0.0f)
141 .max(4.0f)
142 .description("Controls the saturation of the shadows");
143 shadows_panel.add_input<decl::Float>("Contrast", "Shadows Contrast")
144 .default_value(1.0f)
146 .min(0.0f)
147 .max(4.0f)
148 .description("Controls the contrast of the shadows");
149 shadows_panel.add_input<decl::Float>("Gamma", "Shadows Gamma")
150 .default_value(1.0f)
152 .min(0.0f)
153 .max(4.0f)
154 .description("Controls the gamma of the shadows");
155 shadows_panel.add_input<decl::Float>("Gain", "Shadows Gain")
156 .default_value(1.0f)
158 .min(0.0f)
159 .max(4.0f)
160 .description("Controls the gain of the shadows");
161 shadows_panel.add_input<decl::Float>("Offset", "Shadows Offset")
162 .default_value(0.0f)
164 .min(-1.0f)
165 .max(1.0f)
166 .description("Controls the offset of the shadows");
167
168 PanelDeclarationBuilder &tonal_range_panel = b.add_panel("Tonal Range").default_closed(true);
169 tonal_range_panel.add_input<decl::Float>("Midtones Start")
170 .default_value(0.2f)
172 .min(0.0f)
173 .max(1.0f)
174 .description(
175 "Specifies the luminance at which the midtones of the image start and the shadows end");
176 tonal_range_panel.add_input<decl::Float>("Midtones End")
177 .default_value(0.7f)
179 .min(0.0f)
180 .max(1.0f)
181 .description(
182 "Specifies the luminance at which the midtones of the image end and the highlights "
183 "start");
184
185 PanelDeclarationBuilder &tone_range_panel = b.add_panel("Channels").default_closed(true);
186 tone_range_panel.add_input<decl::Bool>("Red", "Apply On Red")
187 .default_value(true)
188 .description("If true, the correction will be applied on the red channel");
189 tone_range_panel.add_input<decl::Bool>("Green", "Apply On Green")
190 .default_value(true)
191 .description("If true, the correction will be applied on the green channel");
192 tone_range_panel.add_input<decl::Bool>("Blue", "Apply On Blue")
193 .default_value(true)
194 .description("If true, the correction will be applied on the blue channel");
195}
196
197using namespace blender::compositor;
198
199static int node_gpu_material(GPUMaterial *material,
200 bNode *node,
201 bNodeExecData * /*execdata*/,
204{
205 float luminance_coefficients[3];
207
208 return GPU_stack_link(material,
209 node,
210 "node_composite_color_correction",
211 inputs,
212 outputs,
213 GPU_constant(luminance_coefficients));
214}
215
217 const float &mask,
218 const float &master_saturation,
219 const float &master_contrast,
220 const float &master_gamma,
221 const float &master_gain,
222 const float &master_offset,
223 const float &highlights_saturation,
224 const float &highlights_contrast,
225 const float &highlights_gamma,
226 const float &highlights_gain,
227 const float &highlights_offset,
228 const float &midtones_saturation,
229 const float &midtones_contrast,
230 const float &midtones_gamma,
231 const float &midtones_gain,
232 const float &midtones_offset,
233 const float &shadows_saturation,
234 const float &shadows_contrast,
235 const float &shadows_gamma,
236 const float &shadows_gain,
237 const float &shadows_offset,
238 const float &start_midtones,
239 const float &end_midtones,
240 const bool &apply_on_red,
241 const bool &apply_on_green,
242 const bool &apply_on_blue,
243 const float3 &luminance_coefficients)
244{
245 const float margin = 0.10f;
246 const float margin_divider = 0.5f / margin;
247 float level = (color.x + color.y + color.z) / 3.0f;
248 float level_shadows = 0.0f;
249 float level_midtones = 0.0f;
250 float level_highlights = 0.0f;
251 if (level < (start_midtones - margin)) {
252 level_shadows = 1.0f;
253 }
254 else if (level < (start_midtones + margin)) {
255 level_midtones = ((level - start_midtones) * margin_divider) + 0.5f;
256 level_shadows = 1.0f - level_midtones;
257 }
258 else if (level < (end_midtones - margin)) {
259 level_midtones = 1.0f;
260 }
261 else if (level < (end_midtones + margin)) {
262 level_highlights = ((level - end_midtones) * margin_divider) + 0.5f;
263 level_midtones = 1.0f - level_highlights;
264 }
265 else {
266 level_highlights = 1.0f;
267 }
268
269 float contrast = level_shadows * shadows_contrast;
270 contrast += level_midtones * midtones_contrast;
271 contrast += level_highlights * highlights_contrast;
272 contrast *= master_contrast;
273 float saturation = level_shadows * shadows_saturation;
274 saturation += level_midtones * midtones_saturation;
275 saturation += level_highlights * highlights_saturation;
276 saturation *= master_saturation;
277 float gamma = level_shadows * shadows_gamma;
278 gamma += level_midtones * midtones_gamma;
279 gamma += level_highlights * highlights_gamma;
280 gamma *= master_gamma;
281 float gain = level_shadows * shadows_gain;
282 gain += level_midtones * midtones_gain;
283 gain += level_highlights * highlights_gain;
284 gain *= master_gain;
285 float offset = level_shadows * shadows_offset;
286 offset += level_midtones * midtones_offset;
287 offset += level_highlights * highlights_offset;
288 offset += master_offset;
289
290 float inverse_gamma = 1.0f / gamma;
291 float luma = math::dot(color.xyz(), luminance_coefficients);
292
293 float3 corrected = luma + saturation * (color.xyz() - luma);
294 corrected = 0.5f + (corrected - 0.5f) * contrast;
295 corrected = math::fallback_pow(corrected * gain + offset, inverse_gamma, corrected);
296 corrected = math::interpolate(color.xyz(), corrected, math::min(mask, 1.0f));
297
298 return float4(apply_on_red ? corrected.x : color.x,
299 apply_on_green ? corrected.y : color.y,
300 apply_on_blue ? corrected.z : color.z,
301 color.w);
302}
303
305{
306 float3 luminance_coefficients;
308
310 return mf::build::detail::build_multi_function_with_n_inputs_one_output<float4>(
311 "Color Correction",
312 [=](const float4 &color,
313 const float &mask,
314 const float &master_saturation,
315 const float &master_contrast,
316 const float &master_gamma,
317 const float &master_gain,
318 const float &master_offset,
319 const float &highlights_saturation,
320 const float &highlights_contrast,
321 const float &highlights_gamma,
322 const float &highlights_gain,
323 const float &highlights_offset,
324 const float &midtones_saturation,
325 const float &midtones_contrast,
326 const float &midtones_gamma,
327 const float &midtones_gain,
328 const float &midtones_offset,
329 const float &shadows_saturation,
330 const float &shadows_contrast,
331 const float &shadows_gamma,
332 const float &shadows_gain,
333 const float &shadows_offset,
334 const float &start_midtones,
335 const float &end_midtones,
336 const bool &apply_on_red,
337 const bool &apply_on_green,
338 const bool &apply_on_blue) -> float4 {
339 return color_correction(color,
340 mask,
341 master_saturation,
342 master_contrast,
343 master_gamma,
344 master_gain,
345 master_offset,
346 highlights_saturation,
347 highlights_contrast,
348 highlights_gamma,
349 highlights_gain,
350 highlights_offset,
351 midtones_saturation,
352 midtones_contrast,
353 midtones_gamma,
354 midtones_gain,
355 midtones_offset,
356 shadows_saturation,
357 shadows_contrast,
358 shadows_gamma,
359 shadows_gain,
360 shadows_offset,
361 start_midtones,
362 end_midtones,
363 apply_on_red,
364 apply_on_green,
365 apply_on_blue,
366 luminance_coefficients);
367 },
368 mf::build::exec_presets::SomeSpanOrSingle<0>(),
370 float,
371 float,
372 float,
373 float,
374 float,
375 float,
376 float,
377 float,
378 float,
379 float,
380 float,
381 float,
382 float,
383 float,
384 float,
385 float,
386 float,
387 float,
388 float,
389 float,
390 float,
391 float,
392 float,
393 bool,
394 bool,
395 bool>());
396 });
397}
398
399} // namespace blender::nodes::node_composite_colorcorrection_cc
400
402{
404
405 static blender::bke::bNodeType ntype;
406
407 cmp_node_type_base(&ntype, "CompositorNodeColorCorrection", CMP_NODE_COLORCORRECTION);
408 ntype.ui_name = "Color Correction";
409 ntype.ui_description =
410 "Adjust the color of an image, separately in several tonal ranges (highlights, midtones and "
411 "shadows)";
412 ntype.enum_name_legacy = "COLORCORRECTION";
414 ntype.declare = file_ns::cmp_node_colorcorrection_declare;
415 ntype.gpu_fn = file_ns::node_gpu_material;
416 ntype.build_multi_function = file_ns::node_build_multi_function;
417
419}
#define NODE_CLASS_OP_COLOR
Definition BKE_node.hh:449
#define CMP_NODE_COLORCORRECTION
bool GPU_stack_link(GPUMaterial *mat, const bNode *node, const char *name, GPUNodeStack *in, GPUNodeStack *out,...)
GPUNodeLink * GPU_constant(const float *num)
BLI_INLINE void IMB_colormanagement_get_luminance_coefficients(float r_rgb[3])
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ PROP_FACTOR
Definition RNA_types.hh:251
DeclType::Builder & add_input(StringRef name, StringRef identifier="")
void construct_and_set_matching_fn_cb(Fn &&create_multi_function)
nullptr float
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
T min(const T &a, const T &b)
T interpolate(const T &a, const T &b, const FactorT &t)
T fallback_pow(const T &x, const T &power, const T &fallback)
static void node_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
static int node_gpu_material(GPUMaterial *material, bNode *node, bNodeExecData *, GPUNodeStack *inputs, GPUNodeStack *outputs)
static void cmp_node_colorcorrection_declare(NodeDeclarationBuilder &b)
static float4 color_correction(const float4 &color, const float &mask, const float &master_saturation, const float &master_contrast, const float &master_gamma, const float &master_gain, const float &master_offset, const float &highlights_saturation, const float &highlights_contrast, const float &highlights_gamma, const float &highlights_gain, const float &highlights_offset, const float &midtones_saturation, const float &midtones_contrast, const float &midtones_gamma, const float &midtones_gain, const float &midtones_offset, const float &shadows_saturation, const float &shadows_contrast, const float &shadows_gamma, const float &shadows_gain, const float &shadows_offset, const float &start_midtones, const float &end_midtones, const bool &apply_on_red, const bool &apply_on_green, const bool &apply_on_blue, const float3 &luminance_coefficients)
VecBase< float, 4 > float4
VecBase< float, 3 > float3
static void register_node_type_cmp_colorcorrection()
void cmp_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
static blender::bke::bNodeSocketTemplate outputs[]
static blender::bke::bNodeSocketTemplate inputs[]
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
NodeGPUExecFunction gpu_fn
Definition BKE_node.hh:342
NodeMultiFunctionBuildFunction build_multi_function
Definition BKE_node.hh:351
const char * enum_name_legacy
Definition BKE_node.hh:247
NodeDeclareFunction declare
Definition BKE_node.hh:362