From bea5fe65055e32a0a95232450b014c0d84e21e68 Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Fri, 18 Nov 2022 12:52:14 +0000 Subject: [PATCH] Nodes: Add Exclusion color mix mode Expands Color Mix nodes with new Exclusion mode. Similar to Difference but produces less contrast. Requested by Pierre Schiller @3D_director and @OmarSquircleArt on twitter. Differential Revision: https://developer.blender.org/D16543 --- .../kernel/osl/shaders/node_color_blend.h | 5 ++ intern/cycles/kernel/osl/shaders/node_mix.osl | 7 +++ .../kernel/osl/shaders/node_mix_color.osl | 2 + intern/cycles/kernel/svm/color_util.h | 7 +++ intern/cycles/kernel/svm/types.h | 1 + intern/cycles/scene/shader_nodes.cpp | 2 + source/blender/blenkernel/intern/material.cc | 8 +++ .../blender/compositor/nodes/COM_MixNode.cc | 3 + .../compositor/operations/COM_MixOperation.cc | 60 ++++++++++++++++++- .../compositor/operations/COM_MixOperation.h | 8 +++ .../freestyle/intern/python/BPy_Freestyle.cpp | 3 + .../common/gpu_shader_common_mix_rgb.glsl | 6 ++ .../gpu_shader_material_mix_color.glsl | 17 ++++++ source/blender/makesdna/DNA_material_types.h | 1 + source/blender/makesrna/intern/rna_material.c | 1 + .../composite/nodes/node_composite_mixrgb.cc | 2 + .../nodes/shader/nodes/node_shader_mix.cc | 2 + .../nodes/shader/nodes/node_shader_mix_rgb.cc | 2 + 18 files changed, 136 insertions(+), 1 deletion(-) diff --git a/intern/cycles/kernel/osl/shaders/node_color_blend.h b/intern/cycles/kernel/osl/shaders/node_color_blend.h index ab4b4809a97..205784c0cda 100644 --- a/intern/cycles/kernel/osl/shaders/node_color_blend.h +++ b/intern/cycles/kernel/osl/shaders/node_color_blend.h @@ -73,6 +73,11 @@ color node_mix_diff(float t, color col1, color col2) return mix(col1, abs(col1 - col2), t); } +color node_mix_exclusion(float t, color col1, color col2) +{ + return max(mix(col1, col1 + col2 - 2.0 * col1 * col2, t), 0.0); +} + color node_mix_dark(float t, color col1, color col2) { return mix(col1, min(col1, col2), t); diff --git a/intern/cycles/kernel/osl/shaders/node_mix.osl b/intern/cycles/kernel/osl/shaders/node_mix.osl index 1bf3abc7f08..6b6806dd05f 100644 --- a/intern/cycles/kernel/osl/shaders/node_mix.osl +++ b/intern/cycles/kernel/osl/shaders/node_mix.osl @@ -76,6 +76,11 @@ color node_mix_diff(float t, color col1, color col2) return mix(col1, abs(col1 - col2), t); } +color node_mix_exclusion(float t, color col1, color col2) +{ + return max(mix(col1, col1 + col2 - 2.0 * col1 * col2, t), 0.0); +} + color node_mix_dark(float t, color col1, color col2) { return mix(col1, min(col1, col2), t); @@ -291,6 +296,8 @@ shader node_mix(string mix_type = "mix", Color = node_mix_div(t, Color1, Color2); if (mix_type == "difference") Color = node_mix_diff(t, Color1, Color2); + if (mix_type == "exclusion") + Color = node_mix_exclusion(t, Color1, Color2); if (mix_type == "darken") Color = node_mix_dark(t, Color1, Color2); if (mix_type == "lighten") diff --git a/intern/cycles/kernel/osl/shaders/node_mix_color.osl b/intern/cycles/kernel/osl/shaders/node_mix_color.osl index 3ddd89ed306..30521aa1efd 100644 --- a/intern/cycles/kernel/osl/shaders/node_mix_color.osl +++ b/intern/cycles/kernel/osl/shaders/node_mix_color.osl @@ -31,6 +31,8 @@ shader node_mix_color(string blend_type = "mix", Result = node_mix_div(t, A, B); if (blend_type == "difference") Result = node_mix_diff(t, A, B); + if (blend_type == "exclusion") + Result = node_mix_exclusion(t, A, B); if (blend_type == "darken") Result = node_mix_dark(t, A, B); if (blend_type == "lighten") diff --git a/intern/cycles/kernel/svm/color_util.h b/intern/cycles/kernel/svm/color_util.h index 96adb6fd64c..5e9e9db82af 100644 --- a/intern/cycles/kernel/svm/color_util.h +++ b/intern/cycles/kernel/svm/color_util.h @@ -79,6 +79,11 @@ ccl_device float3 svm_mix_diff(float t, float3 col1, float3 col2) return interp(col1, fabs(col1 - col2), t); } +ccl_device float3 svm_mix_exclusion(float t, float3 col1, float3 col2) +{ + return max(interp(col1, col1 + col2 - 2.0f * col1 * col2, t), zero_float3()); +} + ccl_device float3 svm_mix_dark(float t, float3 col1, float3 col2) { return interp(col1, min(col1, col2), t); @@ -266,6 +271,8 @@ ccl_device_noinline_cpu float3 svm_mix(NodeMix type, float t, float3 c1, float3 return svm_mix_div(t, c1, c2); case NODE_MIX_DIFF: return svm_mix_diff(t, c1, c2); + case NODE_MIX_EXCLUSION: + return svm_mix_exclusion(t, c1, c2); case NODE_MIX_DARK: return svm_mix_dark(t, c1, c2); case NODE_MIX_LIGHT: diff --git a/intern/cycles/kernel/svm/types.h b/intern/cycles/kernel/svm/types.h index 9dd8f196e0f..7e956505c7f 100644 --- a/intern/cycles/kernel/svm/types.h +++ b/intern/cycles/kernel/svm/types.h @@ -136,6 +136,7 @@ typedef enum NodeMix { NODE_MIX_COL, NODE_MIX_SOFT, NODE_MIX_LINEAR, + NODE_MIX_EXCLUSION, NODE_MIX_CLAMP /* used for the clamp UI option */ } NodeMix; diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index e95f362793f..8cd64cd189e 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -4948,6 +4948,7 @@ NODE_DEFINE(MixNode) type_enum.insert("color", NODE_MIX_COL); type_enum.insert("soft_light", NODE_MIX_SOFT); type_enum.insert("linear_light", NODE_MIX_LINEAR); + type_enum.insert("exclusion", NODE_MIX_EXCLUSION); SOCKET_ENUM(mix_type, "Type", type_enum, NODE_MIX_BLEND); SOCKET_BOOLEAN(use_clamp, "Use Clamp", false); @@ -5026,6 +5027,7 @@ NODE_DEFINE(MixColorNode) type_enum.insert("color", NODE_MIX_COL); type_enum.insert("soft_light", NODE_MIX_SOFT); type_enum.insert("linear_light", NODE_MIX_LINEAR); + type_enum.insert("exclusion", NODE_MIX_EXCLUSION); SOCKET_ENUM(blend_type, "Type", type_enum, NODE_MIX_BLEND); SOCKET_IN_FLOAT(fac, "Factor", 0.5f); diff --git a/source/blender/blenkernel/intern/material.cc b/source/blender/blenkernel/intern/material.cc index 51e61dbebad..4b0508ddf20 100644 --- a/source/blender/blenkernel/intern/material.cc +++ b/source/blender/blenkernel/intern/material.cc @@ -1689,6 +1689,14 @@ void ramp_blend(int type, float r_col[3], const float fac, const float col[3]) r_col[1] = facm * (r_col[1]) + fac * fabsf(r_col[1] - col[1]); r_col[2] = facm * (r_col[2]) + fac * fabsf(r_col[2] - col[2]); break; + case MA_RAMP_EXCLUSION: + r_col[0] = max_ff(facm * (r_col[0]) + fac * (r_col[0] + col[0] - 2.0f * r_col[0] * col[0]), + 0.0f); + r_col[1] = max_ff(facm * (r_col[1]) + fac * (r_col[1] + col[1] - 2.0f * r_col[1] * col[1]), + 0.0f); + r_col[2] = max_ff(facm * (r_col[2]) + fac * (r_col[2] + col[2] - 2.0f * r_col[2] * col[2]), + 0.0f); + break; case MA_RAMP_DARK: r_col[0] = min_ff(r_col[0], col[0]) * fac + r_col[0] * facm; r_col[1] = min_ff(r_col[1], col[1]) * fac + r_col[1] * facm; diff --git a/source/blender/compositor/nodes/COM_MixNode.cc b/source/blender/compositor/nodes/COM_MixNode.cc index e9179d7063c..5fdb1cab7b1 100644 --- a/source/blender/compositor/nodes/COM_MixNode.cc +++ b/source/blender/compositor/nodes/COM_MixNode.cc @@ -57,6 +57,9 @@ void MixNode::convert_to_operations(NodeConverter &converter, case MA_RAMP_DIFF: convert_prog = new MixDifferenceOperation(); break; + case MA_RAMP_EXCLUSION: + convert_prog = new MixExclusionOperation(); + break; case MA_RAMP_SAT: convert_prog = new MixSaturationOperation(); break; diff --git a/source/blender/compositor/operations/COM_MixOperation.cc b/source/blender/compositor/operations/COM_MixOperation.cc index 7a6c48b89c0..96b6cb52afe 100644 --- a/source/blender/compositor/operations/COM_MixOperation.cc +++ b/source/blender/compositor/operations/COM_MixOperation.cc @@ -494,7 +494,65 @@ void MixDifferenceOperation::update_memory_buffer_row(PixelCursor &p) } } -/* ******** Mix Difference Operation ******** */ +/* ******** Mix Exclusion Operation ******** */ + +void MixExclusionOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float input_color1[4]; + float input_color2[4]; + float input_value[4]; + + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); + + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; + } + float valuem = 1.0f - value; + output[0] = max_ff(valuem * input_color1[0] + value * (input_color1[0] + input_color2[0] - + 2.0f * input_color1[0] * input_color2[0]), + 0.0f); + output[1] = max_ff(valuem * input_color1[1] + value * (input_color1[1] + input_color2[1] - + 2.0f * input_color1[1] * input_color2[1]), + 0.0f); + output[2] = max_ff(valuem * input_color1[2] + value * (input_color1[2] + input_color2[2] - + 2.0f * input_color1[2] * input_color2[2]), + 0.0f); + output[3] = input_color1[3]; + + clamp_if_needed(output); +} + +void MixExclusionOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->use_value_alpha_multiply()) { + value *= p.color2[3]; + } + const float value_m = 1.0f - value; + p.out[0] = max_ff(value_m * p.color1[0] + + value * (p.color1[0] + p.color2[0] - 2.0f * p.color1[0] * p.color2[0]), + 0.0f); + p.out[1] = max_ff(value_m * p.color1[1] + + value * (p.color1[1] + p.color2[1] - 2.0f * p.color1[1] * p.color2[1]), + 0.0f); + p.out[2] = max_ff(value_m * p.color1[2] + + value * (p.color1[2] + p.color2[2] - 2.0f * p.color1[2] * p.color2[2]), + 0.0f); + p.out[3] = p.color1[3]; + + clamp_if_needed(p.out); + p.next(); + } +} + +/* ******** Mix Divide Operation ******** */ void MixDivideOperation::execute_pixel_sampled(float output[4], float x, diff --git a/source/blender/compositor/operations/COM_MixOperation.h b/source/blender/compositor/operations/COM_MixOperation.h index 73c1f9fcb22..d7e5afcd9fc 100644 --- a/source/blender/compositor/operations/COM_MixOperation.h +++ b/source/blender/compositor/operations/COM_MixOperation.h @@ -143,6 +143,14 @@ class MixDifferenceOperation : public MixBaseOperation { void update_memory_buffer_row(PixelCursor &p) override; }; +class MixExclusionOperation : public MixBaseOperation { + public: + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; +}; + class MixDivideOperation : public MixBaseOperation { public: void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; diff --git a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp index f99e66c822d..402acd28602 100644 --- a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp +++ b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp @@ -91,6 +91,9 @@ static int ramp_blend_type(const char *type) if (STREQ(type, "DIFFERENCE")) { return MA_RAMP_DIFF; } + if (STREQ(type, "EXCLUSION")) { + return MA_RAMP_EXCLUSION; + } if (STREQ(type, "DARKEN")) { return MA_RAMP_DARK; } diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl index 39f3c722dd2..4845524d873 100644 --- a/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl @@ -101,6 +101,12 @@ void mix_diff(float fac, vec4 col1, vec4 col2, out vec4 outcol) outcol.a = col1.a; } +void mix_exclusion(float fac, vec4 col1, vec4 col2, out vec4 outcol) +{ + outcol = max(mix(col1, col1 + col2 - 2.0 * col1 * col2, fac), 0.0); + outcol.a = col1.a; +} + void mix_dark(float fac, vec4 col1, vec4 col2, out vec4 outcol) { outcol.rgb = mix(col1.rgb, min(col1.rgb, col2.rgb), fac); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_mix_color.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_mix_color.glsl index 933a8de9cb7..ec4845c84d1 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_mix_color.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_mix_color.glsl @@ -171,6 +171,23 @@ void node_mix_diff(float fac, outcol.a = col1.a; } +void node_mix_exclusion(float fac, + vec3 facvec, + float f1, + float f2, + vec3 v1, + vec3 v2, + vec4 col1, + vec4 col2, + out float outfloat, + out vec3 outvec, + out vec4 outcol) +{ + + outcol = max(mix(col1, col1 + col2 - 2.0 * col1 * col2, fac), 0.0); + outcol.a = col1.a; +} + void node_mix_dark(float fac, vec3 facvec, float f1, diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h index 460670225c8..3b8ccc2bc38 100644 --- a/source/blender/makesdna/DNA_material_types.h +++ b/source/blender/makesdna/DNA_material_types.h @@ -269,6 +269,7 @@ typedef struct Material { #define MA_RAMP_COLOR 15 #define MA_RAMP_SOFT 16 #define MA_RAMP_LINEAR 17 +#define MA_RAMP_EXCLUSION 18 /* texco */ #define TEXCO_ORCO (1 << 0) diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index 71a4a4b37f4..7874f06da47 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -40,6 +40,7 @@ const EnumPropertyItem rna_enum_ramp_blend_items[] = { {MA_RAMP_LINEAR, "LINEAR_LIGHT", 0, "Linear Light", ""}, RNA_ENUM_ITEM_SEPR, {MA_RAMP_DIFF, "DIFFERENCE", 0, "Difference", ""}, + {MA_RAMP_EXCLUSION, "EXCLUSION", 0, "Exclusion", ""}, {MA_RAMP_SUB, "SUBTRACT", 0, "Subtract", ""}, {MA_RAMP_DIV, "DIVIDE", 0, "Divide", ""}, RNA_ENUM_ITEM_SEPR, diff --git a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc index a1fbbfe7d40..acdd569971a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc @@ -93,6 +93,8 @@ class MixRGBShaderNode : public ShaderNode { return "mix_div"; case MA_RAMP_DIFF: return "mix_diff"; + case MA_RAMP_EXCLUSION: + return "mix_exclusion"; case MA_RAMP_DARK: return "mix_dark"; case MA_RAMP_LIGHT: diff --git a/source/blender/nodes/shader/nodes/node_shader_mix.cc b/source/blender/nodes/shader/nodes/node_shader_mix.cc index 68344153591..4200041605c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mix.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mix.cc @@ -258,6 +258,8 @@ static const char *gpu_shader_get_name(eNodeSocketDatatype data_type, return "node_mix_div_fallback"; case MA_RAMP_DIFF: return "node_mix_diff"; + case MA_RAMP_EXCLUSION: + return "node_mix_exclusion"; case MA_RAMP_DARK: return "node_mix_dark"; case MA_RAMP_LIGHT: diff --git a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc index 98771098f81..aadbdbe4716 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc @@ -35,6 +35,8 @@ static const char *gpu_shader_get_name(int mode) return "mix_div_fallback"; case MA_RAMP_DIFF: return "mix_diff"; + case MA_RAMP_EXCLUSION: + return "mix_exclusion"; case MA_RAMP_DARK: return "mix_dark"; case MA_RAMP_LIGHT: