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
This commit is contained in:
parent
a8530d31c2
commit
bea5fe6505
Notes:
blender-bot
2025-02-14 01:05:06 +00:00
Referenced by issue #114242, Nodes: Exclusion (Mix Color) clamps negative values even when Clamp Result is disabled
@ -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);
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user