From bfa0ee13d5395fa2cf622b6808e93d0a7cd4f3ea Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Tue, 30 Aug 2022 11:05:46 +0100 Subject: 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 --- intern/cycles/blender/shader.cpp | 91 ++++++- intern/cycles/kernel/osl/shaders/CMakeLists.txt | 5 + .../cycles/kernel/osl/shaders/node_color_blend.h | 264 +++++++++++++++++++++ .../cycles/kernel/osl/shaders/node_mix_color.osl | 57 +++++ .../cycles/kernel/osl/shaders/node_mix_float.osl | 11 + .../cycles/kernel/osl/shaders/node_mix_vector.osl | 14 ++ .../osl/shaders/node_mix_vector_non_uniform.osl | 14 ++ intern/cycles/kernel/svm/color_util.h | 12 +- intern/cycles/kernel/svm/mix.h | 86 ++++++- intern/cycles/kernel/svm/node_types_template.h | 4 + intern/cycles/kernel/svm/svm.h | 12 + intern/cycles/kernel/svm/types.h | 2 +- intern/cycles/scene/constant_fold.cpp | 95 ++++++++ intern/cycles/scene/constant_fold.h | 1 + intern/cycles/scene/shader_nodes.cpp | 244 ++++++++++++++++++- intern/cycles/scene/shader_nodes.h | 46 ++++ 16 files changed, 946 insertions(+), 12 deletions(-) create mode 100644 intern/cycles/kernel/osl/shaders/node_color_blend.h create mode 100644 intern/cycles/kernel/osl/shaders/node_mix_color.osl create mode 100644 intern/cycles/kernel/osl/shaders/node_mix_float.osl create mode 100644 intern/cycles/kernel/osl/shaders/node_mix_vector.osl create mode 100644 intern/cycles/kernel/osl/shaders/node_mix_vector_non_uniform.osl (limited to 'intern') 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(); + mix_node->set_use_clamp(b_mix_node.clamp_factor()); + node = mix_node; + } + else { + MixVectorNonUniformNode *mix_node = graph->create_node(); + 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(); + 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(); + 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(); } @@ -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(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) -- cgit v1.2.3