Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/intern
diff options
context:
space:
mode:
authorCharlie Jolly <charlie>2022-08-30 13:05:46 +0300
committerCharlie Jolly <mistajolly@gmail.com>2022-08-31 02:13:57 +0300
commitbfa0ee13d5395fa2cf622b6808e93d0a7cd4f3ea (patch)
tree60a8fece4797903560d7892d6e388030a3566a2b /intern
parent0331a8c67c8035c29ac6b7655cb4e4d837ddabba (diff)
Node: Mix node
This patch is a response to T92588 and is implemented as a Function/Shader node. This node has support for Float, Vector and Color data types. For Vector it supports uniform and non-uniform mixing. For Color it now has the option to remove factor clamping. It replaces the Mix RGB for Shader and Geometry node trees. As discussed in T96219, this patch converts existing nodes in .blend files. The old node is still available in the Python API but hidden from the menus. Reviewed By: HooglyBoogly, JacquesLucke, simonthommes, brecht Maniphest Tasks: T92588 Differential Revision: https://developer.blender.org/D13749
Diffstat (limited to 'intern')
-rw-r--r--intern/cycles/blender/shader.cpp91
-rw-r--r--intern/cycles/kernel/osl/shaders/CMakeLists.txt5
-rw-r--r--intern/cycles/kernel/osl/shaders/node_color_blend.h264
-rw-r--r--intern/cycles/kernel/osl/shaders/node_mix_color.osl57
-rw-r--r--intern/cycles/kernel/osl/shaders/node_mix_float.osl11
-rw-r--r--intern/cycles/kernel/osl/shaders/node_mix_vector.osl14
-rw-r--r--intern/cycles/kernel/osl/shaders/node_mix_vector_non_uniform.osl14
-rw-r--r--intern/cycles/kernel/svm/color_util.h12
-rw-r--r--intern/cycles/kernel/svm/mix.h86
-rw-r--r--intern/cycles/kernel/svm/node_types_template.h4
-rw-r--r--intern/cycles/kernel/svm/svm.h12
-rw-r--r--intern/cycles/kernel/svm/types.h2
-rw-r--r--intern/cycles/scene/constant_fold.cpp95
-rw-r--r--intern/cycles/scene/constant_fold.h1
-rw-r--r--intern/cycles/scene/shader_nodes.cpp244
-rw-r--r--intern/cycles/scene/shader_nodes.h46
16 files changed, 946 insertions, 12 deletions
diff --git a/intern/cycles/blender/shader.cpp b/intern/cycles/blender/shader.cpp
index 04eb1576330..9505f4ba58f 100644
--- a/intern/cycles/blender/shader.cpp
+++ b/intern/cycles/blender/shader.cpp
@@ -350,6 +350,33 @@ static ShaderNode *add_node(Scene *scene,
mix->set_use_clamp(b_mix_node.use_clamp());
node = mix;
}
+ else if (b_node.is_a(&RNA_ShaderNodeMix)) {
+ BL::ShaderNodeMix b_mix_node(b_node);
+ if (b_mix_node.data_type() == BL::ShaderNodeMix::data_type_VECTOR) {
+ if (b_mix_node.factor_mode() == BL::ShaderNodeMix::factor_mode_UNIFORM) {
+ MixVectorNode *mix_node = graph->create_node<MixVectorNode>();
+ mix_node->set_use_clamp(b_mix_node.clamp_factor());
+ node = mix_node;
+ }
+ else {
+ MixVectorNonUniformNode *mix_node = graph->create_node<MixVectorNonUniformNode>();
+ mix_node->set_use_clamp(b_mix_node.clamp_factor());
+ node = mix_node;
+ }
+ }
+ else if (b_mix_node.data_type() == BL::ShaderNodeMix::data_type_RGBA) {
+ MixColorNode *mix_node = graph->create_node<MixColorNode>();
+ mix_node->set_blend_type((NodeMix)b_mix_node.blend_type());
+ mix_node->set_use_clamp(b_mix_node.clamp_factor());
+ mix_node->set_use_clamp_result(b_mix_node.clamp_result());
+ node = mix_node;
+ }
+ else {
+ MixFloatNode *mix_node = graph->create_node<MixFloatNode>();
+ mix_node->set_use_clamp(b_mix_node.clamp_factor());
+ node = mix_node;
+ }
+ }
else if (b_node.is_a(&RNA_ShaderNodeSeparateRGB)) {
node = graph->create_node<SeparateRGBNode>();
}
@@ -1072,7 +1099,9 @@ static bool node_use_modified_socket_name(ShaderNode *node)
return true;
}
-static ShaderInput *node_find_input_by_name(ShaderNode *node, BL::NodeSocket &b_socket)
+static ShaderInput *node_find_input_by_name(BL::Node b_node,
+ ShaderNode *node,
+ BL::NodeSocket &b_socket)
{
string name = b_socket.identifier();
ShaderInput *input = node->input(name.c_str());
@@ -1082,6 +1111,35 @@ static ShaderInput *node_find_input_by_name(ShaderNode *node, BL::NodeSocket &b_
if (string_startswith(name, "Shader")) {
string_replace(name, "Shader", "Closure");
}
+
+ /* Map mix node internal name for shader. */
+ if (b_node.is_a(&RNA_ShaderNodeMix)) {
+ if (string_endswith(name, "Factor_Float")) {
+ string_replace(name, "Factor_Float", "Factor");
+ }
+ else if (string_endswith(name, "Factor_Vector")) {
+ string_replace(name, "Factor_Vector", "Factor");
+ }
+ else if (string_endswith(name, "A_Float")) {
+ string_replace(name, "A_Float", "A");
+ }
+ else if (string_endswith(name, "B_Float")) {
+ string_replace(name, "B_Float", "B");
+ }
+ else if (string_endswith(name, "A_Color")) {
+ string_replace(name, "A_Color", "A");
+ }
+ else if (string_endswith(name, "B_Color")) {
+ string_replace(name, "B_Color", "B");
+ }
+ else if (string_endswith(name, "A_Vector")) {
+ string_replace(name, "A_Vector", "A");
+ }
+ else if (string_endswith(name, "B_Vector")) {
+ string_replace(name, "B_Vector", "B");
+ }
+ }
+
input = node->input(name.c_str());
if (!input) {
@@ -1111,7 +1169,9 @@ static ShaderInput *node_find_input_by_name(ShaderNode *node, BL::NodeSocket &b_
return input;
}
-static ShaderOutput *node_find_output_by_name(ShaderNode *node, BL::NodeSocket &b_socket)
+static ShaderOutput *node_find_output_by_name(BL::Node b_node,
+ ShaderNode *node,
+ BL::NodeSocket &b_socket)
{
string name = b_socket.identifier();
ShaderOutput *output = node->output(name.c_str());
@@ -1122,6 +1182,21 @@ static ShaderOutput *node_find_output_by_name(ShaderNode *node, BL::NodeSocket &
name = "Closure";
output = node->output(name.c_str());
}
+ /* Map internal name for shader. */
+ if (b_node.is_a(&RNA_ShaderNodeMix)) {
+ if (string_endswith(name, "Result_Float")) {
+ string_replace(name, "Result_Float", "Result");
+ output = node->output(name.c_str());
+ }
+ else if (string_endswith(name, "Result_Color")) {
+ string_replace(name, "Result_Color", "Result");
+ output = node->output(name.c_str());
+ }
+ else if (string_endswith(name, "Result_Vector")) {
+ string_replace(name, "Result_Vector", "Result");
+ output = node->output(name.c_str());
+ }
+ }
}
return output;
@@ -1267,7 +1342,11 @@ static void add_nodes(Scene *scene,
if (node) {
/* map node sockets for linking */
for (BL::NodeSocket &b_input : b_node.inputs) {
- ShaderInput *input = node_find_input_by_name(node, b_input);
+ if (b_input.is_unavailable()) {
+ /* Skip unavailable sockets. */
+ continue;
+ }
+ ShaderInput *input = node_find_input_by_name(b_node, node, b_input);
if (!input) {
/* XXX should not happen, report error? */
continue;
@@ -1277,7 +1356,11 @@ static void add_nodes(Scene *scene,
set_default_value(input, b_input, b_data, b_ntree);
}
for (BL::NodeSocket &b_output : b_node.outputs) {
- ShaderOutput *output = node_find_output_by_name(node, b_output);
+ if (b_output.is_unavailable()) {
+ /* Skip unavailable sockets. */
+ continue;
+ }
+ ShaderOutput *output = node_find_output_by_name(b_node, node, b_output);
if (!output) {
/* XXX should not happen, report error? */
continue;
diff --git a/intern/cycles/kernel/osl/shaders/CMakeLists.txt b/intern/cycles/kernel/osl/shaders/CMakeLists.txt
index 741bce7c399..c79af3f6112 100644
--- a/intern/cycles/kernel/osl/shaders/CMakeLists.txt
+++ b/intern/cycles/kernel/osl/shaders/CMakeLists.txt
@@ -57,6 +57,10 @@ set(SRC_OSL
node_math.osl
node_mix.osl
node_mix_closure.osl
+ node_mix_color.osl
+ node_mix_float.osl
+ node_mix_vector.osl
+ node_mix_vector_non_uniform.osl
node_musgrave_texture.osl
node_noise_texture.osl
node_normal.osl
@@ -109,6 +113,7 @@ file(GLOB SRC_OSL_HEADER_DIST ${OSL_SHADER_DIR}/*.h)
set(SRC_OSL_HEADERS
node_color.h
+ node_color_blend.h
node_fresnel.h
node_hash.h
node_math.h
diff --git a/intern/cycles/kernel/osl/shaders/node_color_blend.h b/intern/cycles/kernel/osl/shaders/node_color_blend.h
new file mode 100644
index 00000000000..ab4b4809a97
--- /dev/null
+++ b/intern/cycles/kernel/osl/shaders/node_color_blend.h
@@ -0,0 +1,264 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright 2011-2022 Blender Foundation */
+
+color node_mix_blend(float t, color col1, color col2)
+{
+ return mix(col1, col2, t);
+}
+
+color node_mix_add(float t, color col1, color col2)
+{
+ return mix(col1, col1 + col2, t);
+}
+
+color node_mix_mul(float t, color col1, color col2)
+{
+ return mix(col1, col1 * col2, t);
+}
+
+color node_mix_screen(float t, color col1, color col2)
+{
+ float tm = 1.0 - t;
+
+ return color(1.0) - (color(tm) + t * (color(1.0) - col2)) * (color(1.0) - col1);
+}
+
+color node_mix_overlay(float t, color col1, color col2)
+{
+ float tm = 1.0 - t;
+
+ color outcol = col1;
+
+ if (outcol[0] < 0.5)
+ outcol[0] *= tm + 2.0 * t * col2[0];
+ else
+ outcol[0] = 1.0 - (tm + 2.0 * t * (1.0 - col2[0])) * (1.0 - outcol[0]);
+
+ if (outcol[1] < 0.5)
+ outcol[1] *= tm + 2.0 * t * col2[1];
+ else
+ outcol[1] = 1.0 - (tm + 2.0 * t * (1.0 - col2[1])) * (1.0 - outcol[1]);
+
+ if (outcol[2] < 0.5)
+ outcol[2] *= tm + 2.0 * t * col2[2];
+ else
+ outcol[2] = 1.0 - (tm + 2.0 * t * (1.0 - col2[2])) * (1.0 - outcol[2]);
+
+ return outcol;
+}
+
+color node_mix_sub(float t, color col1, color col2)
+{
+ return mix(col1, col1 - col2, t);
+}
+
+color node_mix_div(float t, color col1, color col2)
+{
+ float tm = 1.0 - t;
+
+ color outcol = col1;
+
+ if (col2[0] != 0.0)
+ outcol[0] = tm * outcol[0] + t * outcol[0] / col2[0];
+ if (col2[1] != 0.0)
+ outcol[1] = tm * outcol[1] + t * outcol[1] / col2[1];
+ if (col2[2] != 0.0)
+ outcol[2] = tm * outcol[2] + t * outcol[2] / col2[2];
+
+ return outcol;
+}
+
+color node_mix_diff(float t, color col1, color col2)
+{
+ return mix(col1, abs(col1 - col2), t);
+}
+
+color node_mix_dark(float t, color col1, color col2)
+{
+ return mix(col1, min(col1, col2), t);
+}
+
+color node_mix_light(float t, color col1, color col2)
+{
+ return mix(col1, max(col1, col2), t);
+}
+
+color node_mix_dodge(float t, color col1, color col2)
+{
+ color outcol = col1;
+
+ if (outcol[0] != 0.0) {
+ float tmp = 1.0 - t * col2[0];
+ if (tmp <= 0.0)
+ outcol[0] = 1.0;
+ else if ((tmp = outcol[0] / tmp) > 1.0)
+ outcol[0] = 1.0;
+ else
+ outcol[0] = tmp;
+ }
+ if (outcol[1] != 0.0) {
+ float tmp = 1.0 - t * col2[1];
+ if (tmp <= 0.0)
+ outcol[1] = 1.0;
+ else if ((tmp = outcol[1] / tmp) > 1.0)
+ outcol[1] = 1.0;
+ else
+ outcol[1] = tmp;
+ }
+ if (outcol[2] != 0.0) {
+ float tmp = 1.0 - t * col2[2];
+ if (tmp <= 0.0)
+ outcol[2] = 1.0;
+ else if ((tmp = outcol[2] / tmp) > 1.0)
+ outcol[2] = 1.0;
+ else
+ outcol[2] = tmp;
+ }
+
+ return outcol;
+}
+
+color node_mix_burn(float t, color col1, color col2)
+{
+ float tmp, tm = 1.0 - t;
+
+ color outcol = col1;
+
+ tmp = tm + t * col2[0];
+ if (tmp <= 0.0)
+ outcol[0] = 0.0;
+ else if ((tmp = (1.0 - (1.0 - outcol[0]) / tmp)) < 0.0)
+ outcol[0] = 0.0;
+ else if (tmp > 1.0)
+ outcol[0] = 1.0;
+ else
+ outcol[0] = tmp;
+
+ tmp = tm + t * col2[1];
+ if (tmp <= 0.0)
+ outcol[1] = 0.0;
+ else if ((tmp = (1.0 - (1.0 - outcol[1]) / tmp)) < 0.0)
+ outcol[1] = 0.0;
+ else if (tmp > 1.0)
+ outcol[1] = 1.0;
+ else
+ outcol[1] = tmp;
+
+ tmp = tm + t * col2[2];
+ if (tmp <= 0.0)
+ outcol[2] = 0.0;
+ else if ((tmp = (1.0 - (1.0 - outcol[2]) / tmp)) < 0.0)
+ outcol[2] = 0.0;
+ else if (tmp > 1.0)
+ outcol[2] = 1.0;
+ else
+ outcol[2] = tmp;
+
+ return outcol;
+}
+
+color node_mix_hue(float t, color col1, color col2)
+{
+ color outcol = col1;
+ color hsv2 = rgb_to_hsv(col2);
+
+ if (hsv2[1] != 0.0) {
+ color hsv = rgb_to_hsv(outcol);
+ hsv[0] = hsv2[0];
+ color tmp = hsv_to_rgb(hsv);
+
+ outcol = mix(outcol, tmp, t);
+ }
+
+ return outcol;
+}
+
+color node_mix_sat(float t, color col1, color col2)
+{
+ float tm = 1.0 - t;
+
+ color outcol = col1;
+
+ color hsv = rgb_to_hsv(outcol);
+
+ if (hsv[1] != 0.0) {
+ color hsv2 = rgb_to_hsv(col2);
+
+ hsv[1] = tm * hsv[1] + t * hsv2[1];
+ outcol = hsv_to_rgb(hsv);
+ }
+
+ return outcol;
+}
+
+color node_mix_val(float t, color col1, color col2)
+{
+ float tm = 1.0 - t;
+
+ color hsv = rgb_to_hsv(col1);
+ color hsv2 = rgb_to_hsv(col2);
+
+ hsv[2] = tm * hsv[2] + t * hsv2[2];
+
+ return hsv_to_rgb(hsv);
+}
+
+color node_mix_color(float t, color col1, color col2)
+{
+ color outcol = col1;
+ color hsv2 = rgb_to_hsv(col2);
+
+ if (hsv2[1] != 0.0) {
+ color hsv = rgb_to_hsv(outcol);
+ hsv[0] = hsv2[0];
+ hsv[1] = hsv2[1];
+ color tmp = hsv_to_rgb(hsv);
+
+ outcol = mix(outcol, tmp, t);
+ }
+
+ return outcol;
+}
+
+color node_mix_soft(float t, color col1, color col2)
+{
+ float tm = 1.0 - t;
+
+ color one = color(1.0);
+ color scr = one - (one - col2) * (one - col1);
+
+ return tm * col1 + t * ((one - col1) * col2 * col1 + col1 * scr);
+}
+
+color node_mix_linear(float t, color col1, color col2)
+{
+ color outcol = col1;
+
+ if (col2[0] > 0.5)
+ outcol[0] = col1[0] + t * (2.0 * (col2[0] - 0.5));
+ else
+ outcol[0] = col1[0] + t * (2.0 * (col2[0]) - 1.0);
+
+ if (col2[1] > 0.5)
+ outcol[1] = col1[1] + t * (2.0 * (col2[1] - 0.5));
+ else
+ outcol[1] = col1[1] + t * (2.0 * (col2[1]) - 1.0);
+
+ if (col2[2] > 0.5)
+ outcol[2] = col1[2] + t * (2.0 * (col2[2] - 0.5));
+ else
+ outcol[2] = col1[2] + t * (2.0 * (col2[2]) - 1.0);
+
+ return outcol;
+}
+
+color node_mix_clamp(color col)
+{
+ color outcol = col;
+
+ outcol[0] = clamp(col[0], 0.0, 1.0);
+ outcol[1] = clamp(col[1], 0.0, 1.0);
+ outcol[2] = clamp(col[2], 0.0, 1.0);
+
+ return outcol;
+}
diff --git a/intern/cycles/kernel/osl/shaders/node_mix_color.osl b/intern/cycles/kernel/osl/shaders/node_mix_color.osl
new file mode 100644
index 00000000000..3ddd89ed306
--- /dev/null
+++ b/intern/cycles/kernel/osl/shaders/node_mix_color.osl
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright 2011-2022 Blender Foundation */
+
+#include "node_color.h"
+#include "node_color_blend.h"
+#include "stdcycles.h"
+
+shader node_mix_color(string blend_type = "mix",
+ int use_clamp = 0,
+ int use_clamp_result = 0,
+ float Factor = 0.5,
+ color A = 0.0,
+ color B = 0.0,
+ output color Result = 0.0)
+{
+ float t = (use_clamp) ? clamp(Factor, 0.0, 1.0) : Factor;
+
+ if (blend_type == "mix")
+ Result = mix(A, B, t);
+ if (blend_type == "add")
+ Result = node_mix_add(t, A, B);
+ if (blend_type == "multiply")
+ Result = node_mix_mul(t, A, B);
+ if (blend_type == "screen")
+ Result = node_mix_screen(t, A, B);
+ if (blend_type == "overlay")
+ Result = node_mix_overlay(t, A, B);
+ if (blend_type == "subtract")
+ Result = node_mix_sub(t, A, B);
+ if (blend_type == "divide")
+ Result = node_mix_div(t, A, B);
+ if (blend_type == "difference")
+ Result = node_mix_diff(t, A, B);
+ if (blend_type == "darken")
+ Result = node_mix_dark(t, A, B);
+ if (blend_type == "lighten")
+ Result = node_mix_light(t, A, B);
+ if (blend_type == "dodge")
+ Result = node_mix_dodge(t, A, B);
+ if (blend_type == "burn")
+ Result = node_mix_burn(t, A, B);
+ if (blend_type == "hue")
+ Result = node_mix_hue(t, A, B);
+ if (blend_type == "saturation")
+ Result = node_mix_sat(t, A, B);
+ if (blend_type == "value")
+ Result = node_mix_val(t, A, B);
+ if (blend_type == "color")
+ Result = node_mix_color(t, A, B);
+ if (blend_type == "soft_light")
+ Result = node_mix_soft(t, A, B);
+ if (blend_type == "linear_light")
+ Result = node_mix_linear(t, A, B);
+
+ if (use_clamp_result)
+ Result = clamp(Result, 0.0, 1.0);
+}
diff --git a/intern/cycles/kernel/osl/shaders/node_mix_float.osl b/intern/cycles/kernel/osl/shaders/node_mix_float.osl
new file mode 100644
index 00000000000..fdc7b4eff6e
--- /dev/null
+++ b/intern/cycles/kernel/osl/shaders/node_mix_float.osl
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright 2011-2022 Blender Foundation */
+
+#include "stdcycles.h"
+
+shader node_mix_float(
+ int use_clamp = 0, float Factor = 0.5, float A = 0.0, float B = 0.0, output float Result = 0.0)
+{
+ float t = (use_clamp) ? clamp(Factor, 0.0, 1.0) : Factor;
+ Result = mix(A, B, t);
+}
diff --git a/intern/cycles/kernel/osl/shaders/node_mix_vector.osl b/intern/cycles/kernel/osl/shaders/node_mix_vector.osl
new file mode 100644
index 00000000000..d76396afb0d
--- /dev/null
+++ b/intern/cycles/kernel/osl/shaders/node_mix_vector.osl
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright 2011-2022 Blender Foundation */
+
+#include "stdcycles.h"
+
+shader node_mix_vector(int use_clamp = 0,
+ float Factor = 0.5,
+ vector A = 0.0,
+ vector B = 0.0,
+ output vector Result = 0.0)
+{
+ float t = (use_clamp) ? clamp(Factor, 0.0, 1.0) : Factor;
+ Result = mix(A, B, t);
+}
diff --git a/intern/cycles/kernel/osl/shaders/node_mix_vector_non_uniform.osl b/intern/cycles/kernel/osl/shaders/node_mix_vector_non_uniform.osl
new file mode 100644
index 00000000000..217856bcf2a
--- /dev/null
+++ b/intern/cycles/kernel/osl/shaders/node_mix_vector_non_uniform.osl
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright 2011-2022 Blender Foundation */
+
+#include "stdcycles.h"
+
+shader node_mix_vector_non_uniform(int use_clamp = 0,
+ vector Factor = 0.5,
+ vector A = 0.0,
+ vector B = 0.0,
+ output vector Result = 0.0)
+{
+ vector t = (use_clamp) ? clamp(Factor, 0.0, 1.0) : Factor;
+ Result = mix(A, B, t);
+}
diff --git a/intern/cycles/kernel/svm/color_util.h b/intern/cycles/kernel/svm/color_util.h
index 41f44378ff0..96adb6fd64c 100644
--- a/intern/cycles/kernel/svm/color_util.h
+++ b/intern/cycles/kernel/svm/color_util.h
@@ -247,10 +247,8 @@ ccl_device float3 svm_mix_clamp(float3 col)
return saturate(col);
}
-ccl_device_noinline_cpu float3 svm_mix(NodeMix type, float fac, float3 c1, float3 c2)
+ccl_device_noinline_cpu float3 svm_mix(NodeMix type, float t, float3 c1, float3 c2)
{
- float t = saturatef(fac);
-
switch (type) {
case NODE_MIX_BLEND:
return svm_mix_blend(t, c1, c2);
@@ -282,7 +280,7 @@ ccl_device_noinline_cpu float3 svm_mix(NodeMix type, float fac, float3 c1, float
return svm_mix_sat(t, c1, c2);
case NODE_MIX_VAL:
return svm_mix_val(t, c1, c2);
- case NODE_MIX_COLOR:
+ case NODE_MIX_COL:
return svm_mix_color(t, c1, c2);
case NODE_MIX_SOFT:
return svm_mix_soft(t, c1, c2);
@@ -295,6 +293,12 @@ ccl_device_noinline_cpu float3 svm_mix(NodeMix type, float fac, float3 c1, float
return make_float3(0.0f, 0.0f, 0.0f);
}
+ccl_device_noinline_cpu float3 svm_mix_clamped_factor(NodeMix type, float t, float3 c1, float3 c2)
+{
+ float fac = saturatef(t);
+ return svm_mix(type, fac, c1, c2);
+}
+
ccl_device_inline float3 svm_brightness_contrast(float3 color, float brightness, float contrast)
{
float a = 1.0f + contrast;
diff --git a/intern/cycles/kernel/svm/mix.h b/intern/cycles/kernel/svm/mix.h
index a9796096410..ead2fc44685 100644
--- a/intern/cycles/kernel/svm/mix.h
+++ b/intern/cycles/kernel/svm/mix.h
@@ -21,10 +21,94 @@ ccl_device_noinline int svm_node_mix(KernelGlobals kg,
float fac = stack_load_float(stack, fac_offset);
float3 c1 = stack_load_float3(stack, c1_offset);
float3 c2 = stack_load_float3(stack, c2_offset);
- float3 result = svm_mix((NodeMix)node1.y, fac, c1, c2);
+ float3 result = svm_mix_clamped_factor((NodeMix)node1.y, fac, c1, c2);
stack_store_float3(stack, node1.z, result);
return offset;
}
+ccl_device_noinline void svm_node_mix_color(ccl_private ShaderData *sd,
+ ccl_private float *stack,
+ uint options,
+ uint input_offset,
+ uint result_offset)
+{
+ uint use_clamp, blend_type, use_clamp_result;
+ uint fac_in_stack_offset, a_in_stack_offset, b_in_stack_offset;
+ svm_unpack_node_uchar3(options, &use_clamp, &blend_type, &use_clamp_result);
+ svm_unpack_node_uchar3(
+ input_offset, &fac_in_stack_offset, &a_in_stack_offset, &b_in_stack_offset);
+
+ float t = stack_load_float(stack, fac_in_stack_offset);
+ if (use_clamp > 0) {
+ t = saturatef(t);
+ }
+ float3 a = stack_load_float3(stack, a_in_stack_offset);
+ float3 b = stack_load_float3(stack, b_in_stack_offset);
+ float3 result = svm_mix((NodeMix)blend_type, t, a, b);
+ if (use_clamp_result) {
+ result = saturate(result);
+ }
+ stack_store_float3(stack, result_offset, result);
+}
+
+ccl_device_noinline void svm_node_mix_float(ccl_private ShaderData *sd,
+ ccl_private float *stack,
+ uint use_clamp,
+ uint input_offset,
+ uint result_offset)
+{
+ uint fac_in_stack_offset, a_in_stack_offset, b_in_stack_offset;
+ svm_unpack_node_uchar3(
+ input_offset, &fac_in_stack_offset, &a_in_stack_offset, &b_in_stack_offset);
+
+ float t = stack_load_float(stack, fac_in_stack_offset);
+ if (use_clamp > 0) {
+ t = saturatef(t);
+ }
+ float a = stack_load_float(stack, a_in_stack_offset);
+ float b = stack_load_float(stack, b_in_stack_offset);
+ float result = a * (1 - t) + b * t;
+
+ stack_store_float(stack, result_offset, result);
+}
+
+ccl_device_noinline void svm_node_mix_vector(ccl_private ShaderData *sd,
+ ccl_private float *stack,
+ uint input_offset,
+ uint result_offset)
+{
+ uint use_clamp, fac_in_stack_offset, a_in_stack_offset, b_in_stack_offset;
+ svm_unpack_node_uchar4(
+ input_offset, &use_clamp, &fac_in_stack_offset, &a_in_stack_offset, &b_in_stack_offset);
+
+ float t = stack_load_float(stack, fac_in_stack_offset);
+ if (use_clamp > 0) {
+ t = saturatef(t);
+ }
+ float3 a = stack_load_float3(stack, a_in_stack_offset);
+ float3 b = stack_load_float3(stack, b_in_stack_offset);
+ float3 result = a * (one_float3() - t) + b * t;
+ stack_store_float3(stack, result_offset, result);
+}
+
+ccl_device_noinline void svm_node_mix_vector_non_uniform(ccl_private ShaderData *sd,
+ ccl_private float *stack,
+ uint input_offset,
+ uint result_offset)
+{
+ uint use_clamp, fac_in_stack_offset, a_in_stack_offset, b_in_stack_offset;
+ svm_unpack_node_uchar4(
+ input_offset, &use_clamp, &fac_in_stack_offset, &a_in_stack_offset, &b_in_stack_offset);
+
+ float3 t = stack_load_float3(stack, fac_in_stack_offset);
+ if (use_clamp > 0) {
+ t = saturate(t);
+ }
+ float3 a = stack_load_float3(stack, a_in_stack_offset);
+ float3 b = stack_load_float3(stack, b_in_stack_offset);
+ float3 result = a * (one_float3() - t) + b * t;
+ stack_store_float3(stack, result_offset, result);
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/svm/node_types_template.h b/intern/cycles/kernel/svm/node_types_template.h
index 39d279be4cb..aab9b9f1158 100644
--- a/intern/cycles/kernel/svm/node_types_template.h
+++ b/intern/cycles/kernel/svm/node_types_template.h
@@ -103,6 +103,10 @@ SHADER_NODE_TYPE(NODE_AOV_START)
SHADER_NODE_TYPE(NODE_AOV_COLOR)
SHADER_NODE_TYPE(NODE_AOV_VALUE)
SHADER_NODE_TYPE(NODE_FLOAT_CURVE)
+SHADER_NODE_TYPE(NODE_MIX_COLOR)
+SHADER_NODE_TYPE(NODE_MIX_FLOAT)
+SHADER_NODE_TYPE(NODE_MIX_VECTOR)
+SHADER_NODE_TYPE(NODE_MIX_VECTOR_NON_UNIFORM)
/* Padding for struct alignment. */
SHADER_NODE_TYPE(NODE_PAD1)
diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h
index 9d6d3e9222c..3ca632c5f0b 100644
--- a/intern/cycles/kernel/svm/svm.h
+++ b/intern/cycles/kernel/svm/svm.h
@@ -585,6 +585,18 @@ ccl_device void svm_eval_nodes(KernelGlobals kg,
SVM_CASE(NODE_AOV_VALUE)
svm_node_aov_value<node_feature_mask>(kg, state, sd, stack, node, render_buffer);
break;
+ SVM_CASE(NODE_MIX_COLOR)
+ svm_node_mix_color(sd, stack, node.y, node.z, node.w);
+ break;
+ SVM_CASE(NODE_MIX_FLOAT)
+ svm_node_mix_float(sd, stack, node.y, node.z, node.w);
+ break;
+ SVM_CASE(NODE_MIX_VECTOR)
+ svm_node_mix_vector(sd, stack, node.y, node.z);
+ break;
+ SVM_CASE(NODE_MIX_VECTOR_NON_UNIFORM)
+ svm_node_mix_vector_non_uniform(sd, stack, node.y, node.z);
+ break;
default:
kernel_assert(!"Unknown node type was passed to the SVM machine");
return;
diff --git a/intern/cycles/kernel/svm/types.h b/intern/cycles/kernel/svm/types.h
index 98dfe6a4375..9dd8f196e0f 100644
--- a/intern/cycles/kernel/svm/types.h
+++ b/intern/cycles/kernel/svm/types.h
@@ -133,7 +133,7 @@ typedef enum NodeMix {
NODE_MIX_HUE,
NODE_MIX_SAT,
NODE_MIX_VAL,
- NODE_MIX_COLOR,
+ NODE_MIX_COL,
NODE_MIX_SOFT,
NODE_MIX_LINEAR,
NODE_MIX_CLAMP /* used for the clamp UI option */
diff --git a/intern/cycles/scene/constant_fold.cpp b/intern/cycles/scene/constant_fold.cpp
index 4bce5661f9b..1aa4515a087 100644
--- a/intern/cycles/scene/constant_fold.cpp
+++ b/intern/cycles/scene/constant_fold.cpp
@@ -291,6 +291,101 @@ void ConstantFolder::fold_mix(NodeMix type, bool clamp) const
}
}
+void ConstantFolder::fold_mix_color(NodeMix type, bool clamp_factor, bool clamp) const
+{
+ ShaderInput *fac_in = node->input("Factor");
+ ShaderInput *color1_in = node->input("A");
+ ShaderInput *color2_in = node->input("B");
+
+ float fac = clamp_factor ? saturatef(node->get_float(fac_in->socket_type)) :
+ node->get_float(fac_in->socket_type);
+ bool fac_is_zero = !fac_in->link && fac == 0.0f;
+ bool fac_is_one = !fac_in->link && fac == 1.0f;
+
+ /* remove no-op node when factor is 0.0 */
+ if (fac_is_zero) {
+ /* note that some of the modes will clamp out of bounds values even without use_clamp */
+ if (!(type == NODE_MIX_LIGHT || type == NODE_MIX_DODGE || type == NODE_MIX_BURN)) {
+ if (try_bypass_or_make_constant(color1_in, clamp)) {
+ return;
+ }
+ }
+ }
+
+ switch (type) {
+ case NODE_MIX_BLEND:
+ /* remove useless mix colors nodes */
+ if (color1_in->link && color2_in->link) {
+ if (color1_in->link == color2_in->link) {
+ try_bypass_or_make_constant(color1_in, clamp);
+ break;
+ }
+ }
+ else if (!color1_in->link && !color2_in->link) {
+ float3 color1 = node->get_float3(color1_in->socket_type);
+ float3 color2 = node->get_float3(color2_in->socket_type);
+ if (color1 == color2) {
+ try_bypass_or_make_constant(color1_in, clamp);
+ break;
+ }
+ }
+ /* remove no-op mix color node when factor is 1.0 */
+ if (fac_is_one) {
+ try_bypass_or_make_constant(color2_in, clamp);
+ break;
+ }
+ break;
+ case NODE_MIX_ADD:
+ /* 0 + X (fac 1) == X */
+ if (is_zero(color1_in) && fac_is_one) {
+ try_bypass_or_make_constant(color2_in, clamp);
+ }
+ /* X + 0 (fac ?) == X */
+ else if (is_zero(color2_in)) {
+ try_bypass_or_make_constant(color1_in, clamp);
+ }
+ break;
+ case NODE_MIX_SUB:
+ /* X - 0 (fac ?) == X */
+ if (is_zero(color2_in)) {
+ try_bypass_or_make_constant(color1_in, clamp);
+ }
+ /* X - X (fac 1) == 0 */
+ else if (color1_in->link && color1_in->link == color2_in->link && fac_is_one) {
+ make_zero();
+ }
+ break;
+ case NODE_MIX_MUL:
+ /* X * 1 (fac ?) == X, 1 * X (fac 1) == X */
+ if (is_one(color1_in) && fac_is_one) {
+ try_bypass_or_make_constant(color2_in, clamp);
+ }
+ else if (is_one(color2_in)) {
+ try_bypass_or_make_constant(color1_in, clamp);
+ }
+ /* 0 * ? (fac ?) == 0, ? * 0 (fac 1) == 0 */
+ else if (is_zero(color1_in)) {
+ make_zero();
+ }
+ else if (is_zero(color2_in) && fac_is_one) {
+ make_zero();
+ }
+ break;
+ case NODE_MIX_DIV:
+ /* X / 1 (fac ?) == X */
+ if (is_one(color2_in)) {
+ try_bypass_or_make_constant(color1_in, clamp);
+ }
+ /* 0 / ? (fac ?) == 0 */
+ else if (is_zero(color1_in)) {
+ make_zero();
+ }
+ break;
+ default:
+ break;
+ }
+}
+
void ConstantFolder::fold_math(NodeMathType type) const
{
ShaderInput *value1_in = node->input("Value1");
diff --git a/intern/cycles/scene/constant_fold.h b/intern/cycles/scene/constant_fold.h
index 090ce367e6c..246ff2d31ee 100644
--- a/intern/cycles/scene/constant_fold.h
+++ b/intern/cycles/scene/constant_fold.h
@@ -51,6 +51,7 @@ class ConstantFolder {
/* Specific nodes. */
void fold_mix(NodeMix type, bool clamp) const;
+ void fold_mix_color(NodeMix type, bool clamp_factor, bool clamp) const;
void fold_math(NodeMathType type) const;
void fold_vector_math(NodeVectorMathType type) const;
void fold_mapping(NodeMappingType type) const;
diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp
index bedb0fe2902..a9cd453947b 100644
--- a/intern/cycles/scene/shader_nodes.cpp
+++ b/intern/cycles/scene/shader_nodes.cpp
@@ -4950,7 +4950,7 @@ NODE_DEFINE(MixNode)
type_enum.insert("hue", NODE_MIX_HUE);
type_enum.insert("saturation", NODE_MIX_SAT);
type_enum.insert("value", NODE_MIX_VAL);
- type_enum.insert("color", NODE_MIX_COLOR);
+ type_enum.insert("color", NODE_MIX_COL);
type_enum.insert("soft_light", NODE_MIX_SOFT);
type_enum.insert("linear_light", NODE_MIX_LINEAR);
SOCKET_ENUM(mix_type, "Type", type_enum, NODE_MIX_BLEND);
@@ -4999,13 +4999,253 @@ void MixNode::compile(OSLCompiler &compiler)
void MixNode::constant_fold(const ConstantFolder &folder)
{
if (folder.all_inputs_constant()) {
- folder.make_constant_clamp(svm_mix(mix_type, fac, color1, color2), use_clamp);
+ folder.make_constant_clamp(svm_mix_clamped_factor(mix_type, fac, color1, color2), use_clamp);
}
else {
folder.fold_mix(mix_type, use_clamp);
}
}
+/* Mix Color */
+
+NODE_DEFINE(MixColorNode)
+{
+ NodeType *type = NodeType::add("mix_color", create, NodeType::SHADER);
+
+ static NodeEnum type_enum;
+ type_enum.insert("mix", NODE_MIX_BLEND);
+ type_enum.insert("add", NODE_MIX_ADD);
+ type_enum.insert("multiply", NODE_MIX_MUL);
+ type_enum.insert("screen", NODE_MIX_SCREEN);
+ type_enum.insert("overlay", NODE_MIX_OVERLAY);
+ type_enum.insert("subtract", NODE_MIX_SUB);
+ type_enum.insert("divide", NODE_MIX_DIV);
+ type_enum.insert("difference", NODE_MIX_DIFF);
+ type_enum.insert("darken", NODE_MIX_DARK);
+ type_enum.insert("lighten", NODE_MIX_LIGHT);
+ type_enum.insert("dodge", NODE_MIX_DODGE);
+ type_enum.insert("burn", NODE_MIX_BURN);
+ type_enum.insert("hue", NODE_MIX_HUE);
+ type_enum.insert("saturation", NODE_MIX_SAT);
+ type_enum.insert("value", NODE_MIX_VAL);
+ type_enum.insert("color", NODE_MIX_COL);
+ type_enum.insert("soft_light", NODE_MIX_SOFT);
+ type_enum.insert("linear_light", NODE_MIX_LINEAR);
+ SOCKET_ENUM(blend_type, "Type", type_enum, NODE_MIX_BLEND);
+
+ SOCKET_IN_FLOAT(fac, "Factor", 0.5f);
+ SOCKET_IN_COLOR(a, "A", zero_float3());
+ SOCKET_IN_COLOR(b, "B", zero_float3());
+ SOCKET_BOOLEAN(use_clamp_result, "Use Clamp Result", false);
+ SOCKET_BOOLEAN(use_clamp, "Use Clamp", true);
+
+ SOCKET_OUT_COLOR(result, "Result");
+
+ return type;
+}
+
+MixColorNode::MixColorNode() : ShaderNode(get_node_type())
+{
+}
+
+void MixColorNode::compile(SVMCompiler &compiler)
+{
+ ShaderInput *fac_in = input("Factor");
+ ShaderInput *a_in = input("A");
+ ShaderInput *b_in = input("B");
+ ShaderOutput *result_out = output("Result");
+
+ int fac_in_stack_offset = compiler.stack_assign(fac_in);
+ int a_in_stack_offset = compiler.stack_assign(a_in);
+ int b_in_stack_offset = compiler.stack_assign(b_in);
+
+ compiler.add_node(
+ NODE_MIX_COLOR,
+ compiler.encode_uchar4(use_clamp, blend_type, use_clamp_result),
+ compiler.encode_uchar4(fac_in_stack_offset, a_in_stack_offset, b_in_stack_offset),
+ compiler.stack_assign(result_out));
+}
+
+void MixColorNode::compile(OSLCompiler &compiler)
+{
+ compiler.parameter(this, "blend_type");
+ compiler.parameter(this, "use_clamp");
+ compiler.parameter(this, "use_clamp_result");
+ compiler.add(this, "node_mix_color");
+}
+
+void MixColorNode::constant_fold(const ConstantFolder &folder)
+{
+ if (folder.all_inputs_constant()) {
+ if (use_clamp) {
+ fac = clamp(fac, 0.0f, 1.0f);
+ }
+ folder.make_constant_clamp(svm_mix(blend_type, fac, a, b), use_clamp_result);
+ }
+ else {
+ folder.fold_mix_color(blend_type, use_clamp, use_clamp_result);
+ }
+}
+
+/* Mix Float */
+
+NODE_DEFINE(MixFloatNode)
+{
+ NodeType *type = NodeType::add("mix_float", create, NodeType::SHADER);
+
+ SOCKET_IN_FLOAT(fac, "Factor", 0.5f);
+ SOCKET_IN_FLOAT(a, "A", 0.0f);
+ SOCKET_IN_FLOAT(b, "B", 0.0f);
+ SOCKET_BOOLEAN(use_clamp, "Use Clamp", true);
+ SOCKET_OUT_FLOAT(result, "Result");
+
+ return type;
+}
+
+MixFloatNode::MixFloatNode() : ShaderNode(get_node_type())
+{
+}
+
+void MixFloatNode::compile(SVMCompiler &compiler)
+{
+ ShaderInput *fac_in = input("Factor");
+ ShaderInput *a_in = input("A");
+ ShaderInput *b_in = input("B");
+ ShaderOutput *result_out = output("Result");
+
+ int fac_in_stack_offset = compiler.stack_assign(fac_in);
+ int a_in_stack_offset = compiler.stack_assign(a_in);
+ int b_in_stack_offset = compiler.stack_assign(b_in);
+
+ compiler.add_node(
+ NODE_MIX_FLOAT,
+ use_clamp,
+ compiler.encode_uchar4(fac_in_stack_offset, a_in_stack_offset, b_in_stack_offset),
+ compiler.stack_assign(result_out));
+}
+
+void MixFloatNode::compile(OSLCompiler &compiler)
+{
+ compiler.parameter(this, "use_clamp");
+ compiler.add(this, "node_mix_float");
+}
+
+void MixFloatNode::constant_fold(const ConstantFolder &folder)
+{
+ if (folder.all_inputs_constant()) {
+ if (use_clamp) {
+ fac = clamp(fac, 0.0f, 1.0f);
+ }
+ folder.make_constant(a * (1 - fac) + b * fac);
+ }
+}
+
+/* Mix Vector */
+
+NODE_DEFINE(MixVectorNode)
+{
+ NodeType *type = NodeType::add("mix_vector", create, NodeType::SHADER);
+
+ SOCKET_IN_FLOAT(fac, "Factor", 0.5f);
+ SOCKET_IN_VECTOR(a, "A", zero_float3());
+ SOCKET_IN_VECTOR(b, "B", zero_float3());
+ SOCKET_BOOLEAN(use_clamp, "Use Clamp", true);
+
+ SOCKET_OUT_VECTOR(result, "Result");
+
+ return type;
+}
+
+MixVectorNode::MixVectorNode() : ShaderNode(get_node_type())
+{
+}
+
+void MixVectorNode::compile(SVMCompiler &compiler)
+{
+ ShaderInput *fac_in = input("Factor");
+ ShaderInput *a_in = input("A");
+ ShaderInput *b_in = input("B");
+ ShaderOutput *result_out = output("Result");
+
+ int fac_in_stack_offset = compiler.stack_assign(fac_in);
+ int a_in_stack_offset = compiler.stack_assign(a_in);
+ int b_in_stack_offset = compiler.stack_assign(b_in);
+
+ compiler.add_node(
+ NODE_MIX_VECTOR,
+ compiler.encode_uchar4(use_clamp, fac_in_stack_offset, a_in_stack_offset, b_in_stack_offset),
+ compiler.stack_assign(result_out));
+}
+
+void MixVectorNode::compile(OSLCompiler &compiler)
+{
+ compiler.parameter(this, "use_clamp");
+ compiler.add(this, "node_mix_vector");
+}
+
+void MixVectorNode::constant_fold(const ConstantFolder &folder)
+{
+ if (folder.all_inputs_constant()) {
+ if (use_clamp) {
+ fac = clamp(fac, 0.0f, 1.0f);
+ }
+ folder.make_constant(a * (one_float3() - fac) + b * fac);
+ }
+}
+
+/* Mix Vector Non Uniform */
+
+NODE_DEFINE(MixVectorNonUniformNode)
+{
+ NodeType *type = NodeType::add("mix_vector_non_uniform", create, NodeType::SHADER);
+
+ SOCKET_IN_VECTOR(fac, "Factor", make_float3(0.5f, 0.5f, 0.5f));
+ SOCKET_IN_VECTOR(a, "A", zero_float3());
+ SOCKET_IN_VECTOR(b, "B", zero_float3());
+ SOCKET_BOOLEAN(use_clamp, "Use Clamp", true);
+
+ SOCKET_OUT_VECTOR(result, "Result");
+
+ return type;
+}
+
+MixVectorNonUniformNode::MixVectorNonUniformNode() : ShaderNode(get_node_type())
+{
+}
+
+void MixVectorNonUniformNode::compile(SVMCompiler &compiler)
+{
+ ShaderInput *fac_in = input("Factor");
+ ShaderInput *a_in = input("A");
+ ShaderInput *b_in = input("B");
+ ShaderOutput *result_out = output("Result");
+
+ int fac_in_stack_offset = compiler.stack_assign(fac_in);
+ int a_in_stack_offset = compiler.stack_assign(a_in);
+ int b_in_stack_offset = compiler.stack_assign(b_in);
+
+ compiler.add_node(
+ NODE_MIX_VECTOR_NON_UNIFORM,
+ compiler.encode_uchar4(use_clamp, fac_in_stack_offset, a_in_stack_offset, b_in_stack_offset),
+ compiler.stack_assign(result_out));
+}
+
+void MixVectorNonUniformNode::compile(OSLCompiler &compiler)
+{
+ compiler.parameter(this, "use_clamp");
+ compiler.add(this, "node_mix_vector_non_uniform");
+}
+
+void MixVectorNonUniformNode::constant_fold(const ConstantFolder &folder)
+{
+ if (folder.all_inputs_constant()) {
+ if (use_clamp) {
+ fac = saturate(fac);
+ }
+ folder.make_constant(a * (one_float3() - fac) + b * fac);
+ }
+}
+
/* Combine Color */
NODE_DEFINE(CombineColorNode)
diff --git a/intern/cycles/scene/shader_nodes.h b/intern/cycles/scene/shader_nodes.h
index ac40a397c1e..cc3a71a0697 100644
--- a/intern/cycles/scene/shader_nodes.h
+++ b/intern/cycles/scene/shader_nodes.h
@@ -1101,6 +1101,52 @@ class MixNode : public ShaderNode {
NODE_SOCKET_API(float, fac)
};
+class MixColorNode : public ShaderNode {
+ public:
+ SHADER_NODE_CLASS(MixColorNode)
+ void constant_fold(const ConstantFolder &folder);
+
+ NODE_SOCKET_API(float3, a)
+ NODE_SOCKET_API(float3, b)
+ NODE_SOCKET_API(float, fac)
+ NODE_SOCKET_API(bool, use_clamp)
+ NODE_SOCKET_API(bool, use_clamp_result)
+ NODE_SOCKET_API(NodeMix, blend_type)
+};
+
+class MixFloatNode : public ShaderNode {
+ public:
+ SHADER_NODE_CLASS(MixFloatNode)
+ void constant_fold(const ConstantFolder &folder);
+
+ NODE_SOCKET_API(float, a)
+ NODE_SOCKET_API(float, b)
+ NODE_SOCKET_API(float, fac)
+ NODE_SOCKET_API(bool, use_clamp)
+};
+
+class MixVectorNode : public ShaderNode {
+ public:
+ SHADER_NODE_CLASS(MixVectorNode)
+ void constant_fold(const ConstantFolder &folder);
+
+ NODE_SOCKET_API(float3, a)
+ NODE_SOCKET_API(float3, b)
+ NODE_SOCKET_API(float, fac)
+ NODE_SOCKET_API(bool, use_clamp)
+};
+
+class MixVectorNonUniformNode : public ShaderNode {
+ public:
+ SHADER_NODE_CLASS(MixVectorNonUniformNode)
+ void constant_fold(const ConstantFolder &folder);
+
+ NODE_SOCKET_API(float3, a)
+ NODE_SOCKET_API(float3, b)
+ NODE_SOCKET_API(float3, fac)
+ NODE_SOCKET_API(bool, use_clamp)
+};
+
class CombineColorNode : public ShaderNode {
public:
SHADER_NODE_CLASS(CombineColorNode)