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
This commit is contained in:
Charlie Jolly 2022-08-30 11:05:46 +01:00 committed by Charlie Jolly
parent 0331a8c67c
commit bfa0ee13d5
Notes: blender-bot 2023-10-23 06:33:03 +02:00
Referenced by issue #100846, Problems with materials nodes in Alpha Trees
Referenced by issue #100797, C++ exporters (OBJ/Collada/USD/Alembic) do not remember the path/filename on subsequent exports
Referenced by issue #92588, Mix Node
Referenced by issue #113985, File crash on opening in version 3.6 but not 2.9
31 changed files with 2040 additions and 21 deletions

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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 */

View File

@ -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");

View File

@ -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;

View File

@ -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)

View File

@ -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)

View File

@ -424,7 +424,6 @@ shader_node_categories = [
NodeItem("ShaderNodeTexWhiteNoise"),
]),
ShaderNodeCategory("SH_NEW_OP_COLOR", "Color", items=[
NodeItem("ShaderNodeMixRGB"),
NodeItem("ShaderNodeRGBCurve"),
NodeItem("ShaderNodeInvert"),
NodeItem("ShaderNodeLightFalloff"),
@ -448,6 +447,7 @@ shader_node_categories = [
NodeItem("ShaderNodeFloatCurve"),
NodeItem("ShaderNodeClamp"),
NodeItem("ShaderNodeMath"),
NodeItem("ShaderNodeMix"),
NodeItem("ShaderNodeValToRGB"),
NodeItem("ShaderNodeRGBToBW"),
NodeItem("ShaderNodeShaderToRGB", poll=object_eevee_shader_nodes_poll),
@ -651,7 +651,6 @@ geometry_node_categories = [
NodeItem("GeometryNodeStoreNamedAttribute"),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
NodeItem("ShaderNodeMixRGB"),
NodeItem("ShaderNodeRGBCurve"),
NodeItem("ShaderNodeValToRGB"),
NodeItem("FunctionNodeSeparateColor"),
@ -719,6 +718,7 @@ geometry_node_categories = [
NodeItem("FunctionNodeBooleanMath"),
NodeItem("FunctionNodeRotateEuler"),
NodeItem("FunctionNodeCompare"),
NodeItem("ShaderNodeMix"),
NodeItem("FunctionNodeFloatToInt"),
NodeItem("GeometryNodeSwitch"),
NodeItem("FunctionNodeRandomValue"),

View File

@ -1102,7 +1102,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
//#define SH_NODE_MATERIAL 100
#define SH_NODE_RGB 101
#define SH_NODE_VALUE 102
#define SH_NODE_MIX_RGB 103
#define SH_NODE_MIX_RGB_LEGACY 103
#define SH_NODE_VALTORGB 104
#define SH_NODE_RGBTOBW 105
#define SH_NODE_SHADERTORGB 106
@ -1205,6 +1205,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define SH_NODE_POINT_INFO 710
#define SH_NODE_COMBINE_COLOR 711
#define SH_NODE_SEPARATE_COLOR 712
#define SH_NODE_MIX 713
/** \} */

View File

@ -4583,6 +4583,7 @@ static void registerShaderNodes()
register_node_type_sh_wavelength();
register_node_type_sh_blackbody();
register_node_type_sh_mix_rgb();
register_node_type_sh_mix();
register_node_type_sh_valtorgb();
register_node_type_sh_rgbtobw();
register_node_type_sh_shadertorgb();

View File

@ -1697,6 +1697,27 @@ static void versioning_replace_legacy_combined_and_separate_color_nodes(bNodeTre
}
}
static void versioning_replace_legacy_mix_rgb_node(bNodeTree *ntree)
{
version_node_input_socket_name(ntree, SH_NODE_MIX_RGB_LEGACY, "Fac", "Factor_Float");
version_node_input_socket_name(ntree, SH_NODE_MIX_RGB_LEGACY, "Color1", "A_Color");
version_node_input_socket_name(ntree, SH_NODE_MIX_RGB_LEGACY, "Color2", "B_Color");
version_node_output_socket_name(ntree, SH_NODE_MIX_RGB_LEGACY, "Color", "Result_Color");
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == SH_NODE_MIX_RGB_LEGACY) {
strcpy(node->idname, "ShaderNodeMix");
node->type = SH_NODE_MIX;
NodeShaderMix *data = (NodeShaderMix *)MEM_callocN(sizeof(NodeShaderMix), __func__);
data->blend_type = node->custom1;
data->clamp_result = node->custom2;
data->clamp_factor = 1;
data->data_type = SOCK_RGBA;
data->factor_mode = NODE_MIX_MODE_UNIFORM;
node->storage = data;
}
}
}
static void version_fix_image_format_copy(Main *bmain, ImageFormatData *format)
{
/* Fix bug where curves in image format were not properly copied to file output
@ -3332,5 +3353,13 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
/* Convert mix rgb node to new mix node and add storage. */
{
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
versioning_replace_legacy_mix_rgb_node(ntree);
}
FOREACH_NODETREE_END;
}
}
}

View File

@ -477,7 +477,7 @@ static void node_shader_set_butfunc(bNodeType *ntype)
case SH_NODE_RGB:
ntype->draw_buttons = node_buts_rgb;
break;
case SH_NODE_MIX_RGB:
case SH_NODE_MIX_RGB_LEGACY:
ntype->draw_buttons = node_buts_mix_rgb;
break;
case SH_NODE_VALTORGB:

View File

@ -232,7 +232,7 @@ Material *BlenderStrokeRenderer::GetStrokeShader(Main *bmain,
storage = (NodeShaderAttribute *)input_attr_color->storage;
BLI_strncpy(storage->name, "Color", sizeof(storage->name));
bNode *mix_rgb_color = nodeAddStaticNode(nullptr, ntree, SH_NODE_MIX_RGB);
bNode *mix_rgb_color = nodeAddStaticNode(nullptr, ntree, SH_NODE_MIX_RGB_LEGACY);
mix_rgb_color->custom1 = MA_RAMP_BLEND; // Mix
mix_rgb_color->locx = 200.0f;
mix_rgb_color->locy = -200.0f;
@ -246,7 +246,7 @@ Material *BlenderStrokeRenderer::GetStrokeShader(Main *bmain,
storage = (NodeShaderAttribute *)input_attr_alpha->storage;
BLI_strncpy(storage->name, "Alpha", sizeof(storage->name));
bNode *mix_rgb_alpha = nodeAddStaticNode(nullptr, ntree, SH_NODE_MIX_RGB);
bNode *mix_rgb_alpha = nodeAddStaticNode(nullptr, ntree, SH_NODE_MIX_RGB_LEGACY);
mix_rgb_alpha->custom1 = MA_RAMP_BLEND; // Mix
mix_rgb_alpha->locx = 600.0f;
mix_rgb_alpha->locy = 300.0f;

View File

@ -394,6 +394,7 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_light_path.glsl
shaders/material/gpu_shader_material_mapping.glsl
shaders/material/gpu_shader_material_map_range.glsl
shaders/material/gpu_shader_material_mix_color.glsl
shaders/material/gpu_shader_material_mix_shader.glsl
shaders/material/gpu_shader_material_noise.glsl
shaders/material/gpu_shader_material_normal.glsl

View File

@ -0,0 +1,537 @@
#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
void node_mix_blend(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 = mix(col1, col2, fac);
outcol.a = col1.a;
}
void node_mix_add(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 = mix(col1, col1 + col2, fac);
outcol.a = col1.a;
}
void node_mix_mult(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 = mix(col1, col1 * col2, fac);
outcol.a = col1.a;
}
void node_mix_screen(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)
{
float facm = 1.0 - fac;
outcol = vec4(1.0) - (vec4(facm) + fac * (vec4(1.0) - col2)) * (vec4(1.0) - col1);
outcol.a = col1.a;
}
void node_mix_overlay(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)
{
float facm = 1.0 - fac;
outcol = col1;
if (outcol.r < 0.5) {
outcol.r *= facm + 2.0 * fac * col2.r;
}
else {
outcol.r = 1.0 - (facm + 2.0 * fac * (1.0 - col2.r)) * (1.0 - outcol.r);
}
if (outcol.g < 0.5) {
outcol.g *= facm + 2.0 * fac * col2.g;
}
else {
outcol.g = 1.0 - (facm + 2.0 * fac * (1.0 - col2.g)) * (1.0 - outcol.g);
}
if (outcol.b < 0.5) {
outcol.b *= facm + 2.0 * fac * col2.b;
}
else {
outcol.b = 1.0 - (facm + 2.0 * fac * (1.0 - col2.b)) * (1.0 - outcol.b);
}
}
void node_mix_sub(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 = mix(col1, col1 - col2, fac);
outcol.a = col1.a;
}
/* A variant of mix_div that fallback to the first color upon zero division. */
void node_mix_div_fallback(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)
{
float facm = 1.0 - fac;
outcol = col1;
if (col2.r != 0.0) {
outcol.r = facm * outcol.r + fac * outcol.r / col2.r;
}
if (col2.g != 0.0) {
outcol.g = facm * outcol.g + fac * outcol.g / col2.g;
}
if (col2.b != 0.0) {
outcol.b = facm * outcol.b + fac * outcol.b / col2.b;
}
}
void node_mix_diff(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 = mix(col1, abs(col1 - col2), fac);
outcol.a = col1.a;
}
void node_mix_dark(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.rgb = mix(col1.rgb, min(col1.rgb, col2.rgb), fac);
outcol.a = col1.a;
}
void node_mix_light(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.rgb = mix(col1.rgb, max(col1.rgb, col2.rgb), fac);
outcol.a = col1.a;
}
void node_mix_dodge(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 = col1;
if (outcol.r != 0.0) {
float tmp = 1.0 - fac * col2.r;
if (tmp <= 0.0) {
outcol.r = 1.0;
}
else if ((tmp = outcol.r / tmp) > 1.0) {
outcol.r = 1.0;
}
else {
outcol.r = tmp;
}
}
if (outcol.g != 0.0) {
float tmp = 1.0 - fac * col2.g;
if (tmp <= 0.0) {
outcol.g = 1.0;
}
else if ((tmp = outcol.g / tmp) > 1.0) {
outcol.g = 1.0;
}
else {
outcol.g = tmp;
}
}
if (outcol.b != 0.0) {
float tmp = 1.0 - fac * col2.b;
if (tmp <= 0.0) {
outcol.b = 1.0;
}
else if ((tmp = outcol.b / tmp) > 1.0) {
outcol.b = 1.0;
}
else {
outcol.b = tmp;
}
}
}
void node_mix_burn(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)
{
float tmp, facm = 1.0 - fac;
outcol = col1;
tmp = facm + fac * col2.r;
if (tmp <= 0.0) {
outcol.r = 0.0;
}
else if ((tmp = (1.0 - (1.0 - outcol.r) / tmp)) < 0.0) {
outcol.r = 0.0;
}
else if (tmp > 1.0) {
outcol.r = 1.0;
}
else {
outcol.r = tmp;
}
tmp = facm + fac * col2.g;
if (tmp <= 0.0) {
outcol.g = 0.0;
}
else if ((tmp = (1.0 - (1.0 - outcol.g) / tmp)) < 0.0) {
outcol.g = 0.0;
}
else if (tmp > 1.0) {
outcol.g = 1.0;
}
else {
outcol.g = tmp;
}
tmp = facm + fac * col2.b;
if (tmp <= 0.0) {
outcol.b = 0.0;
}
else if ((tmp = (1.0 - (1.0 - outcol.b) / tmp)) < 0.0) {
outcol.b = 0.0;
}
else if (tmp > 1.0) {
outcol.b = 1.0;
}
else {
outcol.b = tmp;
}
}
void node_mix_hue(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)
{
float facm = 1.0 - fac;
outcol = col1;
vec4 hsv, hsv2, tmp;
rgb_to_hsv(col2, hsv2);
if (hsv2.y != 0.0) {
rgb_to_hsv(outcol, hsv);
hsv.x = hsv2.x;
hsv_to_rgb(hsv, tmp);
outcol = mix(outcol, tmp, fac);
outcol.a = col1.a;
}
}
void node_mix_sat(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)
{
float facm = 1.0 - fac;
outcol = col1;
vec4 hsv, hsv2;
rgb_to_hsv(outcol, hsv);
if (hsv.y != 0.0) {
rgb_to_hsv(col2, hsv2);
hsv.y = facm * hsv.y + fac * hsv2.y;
hsv_to_rgb(hsv, outcol);
}
}
void node_mix_val(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)
{
float facm = 1.0 - fac;
vec4 hsv, hsv2;
rgb_to_hsv(col1, hsv);
rgb_to_hsv(col2, hsv2);
hsv.z = facm * hsv.z + fac * hsv2.z;
hsv_to_rgb(hsv, outcol);
}
void node_mix_color(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)
{
float facm = 1.0 - fac;
outcol = col1;
vec4 hsv, hsv2, tmp;
rgb_to_hsv(col2, hsv2);
if (hsv2.y != 0.0) {
rgb_to_hsv(outcol, hsv);
hsv.x = hsv2.x;
hsv.y = hsv2.y;
hsv_to_rgb(hsv, tmp);
outcol = mix(outcol, tmp, fac);
outcol.a = col1.a;
}
}
void node_mix_soft(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)
{
float facm = 1.0 - fac;
vec4 one = vec4(1.0);
vec4 scr = one - (one - col2) * (one - col1);
outcol = facm * col1 + fac * ((one - col1) * col2 * col1 + col1 * scr);
}
void node_mix_linear(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 = col1 + fac * (2.0 * (col2 - vec4(0.5)));
}
void node_mix_float(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)
{
outfloat = mix(f1, f2, fac);
}
void node_mix_vector(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)
{
outvec = mix(v1, v2, fac);
}
void node_mix_vector_non_uniform(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)
{
outvec = mix(v1, v2, facvec);
}
void node_mix_rgba(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 = mix(col1, col2, fac);
}
void node_mix_clamp_vector(vec3 vec, vec3 min, vec3 max, out vec3 outvec)
{
outvec = clamp(vec, min, max);
}
void node_mix_clamp_value(float value, float min, float max, out float outfloat)
{
outfloat = clamp(value, min, max);
}

View File

@ -1475,6 +1475,17 @@ typedef struct NodeCombSepColor {
int8_t mode;
} NodeCombSepColor;
typedef struct NodeShaderMix {
/* eNodeSocketDatatype */
int8_t data_type;
/* NodeShaderMixMode */
int8_t factor_mode;
int8_t clamp_factor;
int8_t clamp_result;
int8_t blend_type;
char _pad[3];
} NodeShaderMix;
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1
@ -1762,6 +1773,11 @@ typedef enum NodeBooleanMathOperation {
NODE_BOOLEAN_MATH_NIMPLY = 8,
} NodeBooleanMathOperation;
typedef enum NodeShaderMixMode {
NODE_MIX_MODE_UNIFORM = 0,
NODE_MIX_MODE_NON_UNIFORM = 1,
} NodeShaderMixMode;
typedef enum NodeCompareMode {
NODE_COMPARE_MODE_ELEMENT = 0,
NODE_COMPARE_MODE_LENGTH = 1,

View File

@ -4927,6 +4927,54 @@ static void def_compare(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_sh_mix(StructRNA *srna)
{
static const EnumPropertyItem rna_enum_mix_data_type_items[] = {
{SOCK_FLOAT, "FLOAT", 0, "Float", ""},
{SOCK_VECTOR, "VECTOR", 0, "Vector", ""},
{SOCK_RGBA, "RGBA", 0, "Color", ""},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem rna_enum_mix_mode_items[] = {
{NODE_MIX_MODE_UNIFORM, "UNIFORM", 0, "Uniform", "Use a single factor for all components"},
{NODE_MIX_MODE_NON_UNIFORM, "NON_UNIFORM", 0, "Non-Uniform", "Per component factor"},
{0, NULL, 0, NULL, NULL},
};
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeShaderMix", "storage");
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_mix_data_type_items);
RNA_def_property_enum_default(prop, SOCK_FLOAT);
RNA_def_property_ui_text(prop, "Data Type", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
prop = RNA_def_property(srna, "factor_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_mix_mode_items);
RNA_def_property_enum_default(prop, SOCK_FLOAT);
RNA_def_property_ui_text(prop, "Factor Mode", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
prop = RNA_def_property(srna, "blend_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "blend_type");
RNA_def_property_enum_items(prop, rna_enum_ramp_blend_items);
RNA_def_property_ui_text(prop, "Blending Mode", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "clamp_factor", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "clamp_factor", 1);
RNA_def_property_ui_text(prop, "Clamp Factor", "Clamp the factor to [0,1] range");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "clamp_result", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "clamp_result", 1);
RNA_def_property_ui_text(prop, "Clamp Result", "Clamp the result to [0,1] range");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_float_to_int(StructRNA *srna)
{
PropertyRNA *prop;
@ -10964,6 +11012,12 @@ static void rna_def_node_socket(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Linked", "True if the socket is connected");
prop = RNA_def_property(srna, "is_unavailable", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SOCK_UNAVAIL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(
prop, "Unavailable", "True if the socket is unavailable");
prop = RNA_def_property(srna, "is_multi_input", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SOCK_MULTI_INPUT);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);

View File

@ -26,6 +26,7 @@ void register_node_type_sh_camera(void);
void register_node_type_sh_value(void);
void register_node_type_sh_rgb(void);
void register_node_type_sh_mix_rgb(void);
void register_node_type_sh_mix(void);
void register_node_type_sh_valtorgb(void);
void register_node_type_sh_rgbtobw(void);
void register_node_type_sh_shadertorgb(void);

View File

@ -25,7 +25,7 @@ DefNode(Node, NODE_REROUTE, 0, "REROUT
DefNode(ShaderNode, SH_NODE_RGB, 0, "RGB", RGB, "RGB", "A color picker")
DefNode(ShaderNode, SH_NODE_VALUE, 0, "VALUE", Value, "Value", "Used to Input numerical values to other nodes in the tree")
DefNode(ShaderNode, SH_NODE_MIX_RGB, def_mix_rgb, "MIX_RGB", MixRGB, "MixRGB", "Mix two input colors")
DefNode(ShaderNode, SH_NODE_MIX_RGB_LEGACY, def_mix_rgb, "MIX_RGB", MixRGB, "MixRGB", "Mix two input colors")
DefNode(ShaderNode, SH_NODE_VALTORGB, def_colorramp, "VALTORGB", ValToRGB, "ColorRamp", "Map values to colors with the use of a gradient")
DefNode(ShaderNode, SH_NODE_RGBTOBW, 0, "RGBTOBW", RGBToBW, "RGB to BW", "Convert a color's luminance to a grayscale value")
DefNode(ShaderNode, SH_NODE_SHADERTORGB, 0, "SHADERTORGB", ShaderToRGB, "Shader to RGB", "Convert rendering effect (such as light and shadow) to color. Typically used for non-photorealistic rendering, to apply additional effects on the output of BSDFs.\nNote: only supported for Eevee")
@ -122,6 +122,7 @@ DefNode(ShaderNode, SH_NODE_OUTPUT_AOV, def_sh_output_aov, "OUT
DefNode(ShaderNode, SH_NODE_CURVE_FLOAT, def_float_curve, "CURVE_FLOAT", FloatCurve, "Float Curve", "Map an input float to a curve and outputs a float value")
DefNode(ShaderNode, SH_NODE_COMBINE_COLOR, def_sh_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "Create a color from individual components using multiple models")
DefNode(ShaderNode, SH_NODE_SEPARATE_COLOR, def_sh_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "Split a color into its individual components using multiple models")
DefNode(ShaderNode, SH_NODE_MIX, def_sh_mix, "MIX", Mix, "Mix", "Mix values by a factor")
DefNode(CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" )
DefNode(CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" )

View File

@ -68,6 +68,7 @@ set(SRC
nodes/node_shader_mapping.cc
nodes/node_shader_math.cc
nodes/node_shader_mix_rgb.cc
nodes/node_shader_mix.cc
nodes/node_shader_mix_shader.cc
nodes/node_shader_normal.cc
nodes/node_shader_normal_map.cc

View File

@ -0,0 +1,443 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2005 Blender Foundation. All rights reserved. */
/** \file
* \ingroup shdnodes
*/
#include <algorithm>
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_shader_util.hh"
#include "NOD_socket_search_link.hh"
#include "RNA_enum_types.h"
namespace blender::nodes::node_sh_mix_cc {
NODE_STORAGE_FUNCS(NodeShaderMix)
static void sh_node_mix_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Float>(N_("Factor"), "Factor_Float")
.default_value(0.5f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Vector>(N_("Factor"), "Factor_Vector")
.default_value(float3(0.5f))
.subtype(PROP_FACTOR);
b.add_input<decl::Float>(N_("A"), "A_Float")
.min(-10000.0f)
.max(10000.0f)
.is_default_link_socket();
b.add_input<decl::Float>(N_("B"), "B_Float").min(-10000.0f).max(10000.0f);
b.add_input<decl::Vector>(N_("A"), "A_Vector").is_default_link_socket();
b.add_input<decl::Vector>(N_("B"), "B_Vector");
b.add_input<decl::Color>(N_("A"), "A_Color")
.default_value({0.5f, 0.5f, 0.5f, 1.0f})
.is_default_link_socket();
b.add_input<decl::Color>(N_("B"), "B_Color").default_value({0.5f, 0.5f, 0.5f, 1.0f});
b.add_output<decl::Float>(N_("Result"), "Result_Float");
b.add_output<decl::Vector>(N_("Result"), "Result_Vector");
b.add_output<decl::Color>(N_("Result"), "Result_Color");
};
static void sh_node_mix_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
const NodeShaderMix &data = node_storage(*static_cast<const bNode *>(ptr->data));
uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
if (data.data_type == SOCK_VECTOR) {
uiItemR(layout, ptr, "factor_mode", 0, "", ICON_NONE);
}
if (data.data_type == SOCK_RGBA) {
uiItemR(layout, ptr, "blend_type", 0, "", ICON_NONE);
uiItemR(layout, ptr, "clamp_result", 0, nullptr, ICON_NONE);
uiItemR(layout, ptr, "clamp_factor", 0, nullptr, ICON_NONE);
}
else {
uiItemR(layout, ptr, "clamp_factor", 0, nullptr, ICON_NONE);
}
}
static void sh_node_mix_label(const bNodeTree *UNUSED(ntree),
const bNode *node,
char *label,
int maxlen)
{
const NodeShaderMix &storage = node_storage(*node);
if (storage.data_type == SOCK_RGBA) {
const char *name;
bool enum_label = RNA_enum_name(rna_enum_ramp_blend_items, storage.blend_type, &name);
if (!enum_label) {
name = "Unknown";
}
BLI_strncpy(label, IFACE_(name), maxlen);
}
}
static int sh_node_mix_ui_class(const bNode *node)
{
const NodeShaderMix &storage = node_storage(*node);
const eNodeSocketDatatype data_type = static_cast<eNodeSocketDatatype>(storage.data_type);
switch (data_type) {
case SOCK_VECTOR:
return NODE_CLASS_OP_VECTOR;
case SOCK_RGBA:
return NODE_CLASS_OP_COLOR;
default:
return NODE_CLASS_CONVERTER;
}
}
static void sh_node_mix_update(bNodeTree *ntree, bNode *node)
{
const NodeShaderMix &storage = node_storage(*node);
const eNodeSocketDatatype data_type = static_cast<eNodeSocketDatatype>(storage.data_type);
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
nodeSetSocketAvailability(ntree, socket, socket->type == data_type);
}
LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
nodeSetSocketAvailability(ntree, socket, socket->type == data_type);
}
bool use_vector_factor = data_type == SOCK_VECTOR &&
storage.factor_mode != NODE_MIX_MODE_UNIFORM;
bNodeSocket *sock_factor = (bNodeSocket *)BLI_findlink(&node->inputs, 0);
nodeSetSocketAvailability(ntree, sock_factor, !use_vector_factor);
bNodeSocket *sock_factor_vec = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
nodeSetSocketAvailability(ntree, sock_factor_vec, use_vector_factor);
}
static void node_mix_gather_link_searches(GatherLinkSearchOpParams &params)
{
const eNodeSocketDatatype sock_type = static_cast<eNodeSocketDatatype>(
params.other_socket().type);
if (ELEM(sock_type, SOCK_BOOLEAN, SOCK_FLOAT, SOCK_RGBA, SOCK_VECTOR, SOCK_INT)) {
const eNodeSocketDatatype type = ELEM(sock_type, SOCK_BOOLEAN, SOCK_INT) ? SOCK_FLOAT :
sock_type;
if (params.in_out() == SOCK_OUT) {
params.add_item(IFACE_("Result"), [type](LinkSearchOpParams &params) {
bNode &node = params.add_node("ShaderNodeMix");
node_storage(node).data_type = type;
params.update_and_connect_available_socket(node, "Result");
});
}
else {
if (ELEM(sock_type, SOCK_VECTOR, SOCK_RGBA)) {
params.add_item(IFACE_("Factor (Non-Uniform)"), [type](LinkSearchOpParams &params) {
bNode &node = params.add_node("ShaderNodeMix");
node_storage(node).data_type = SOCK_VECTOR;
node_storage(node).factor_mode = NODE_MIX_MODE_NON_UNIFORM;
params.update_and_connect_available_socket(node, "Factor");
});
}
params.add_item(IFACE_("Factor"), [type](LinkSearchOpParams &params) {
bNode &node = params.add_node("ShaderNodeMix");
node_storage(node).data_type = type;
params.update_and_connect_available_socket(node, "Factor");
});
params.add_item(IFACE_("A"), [type](LinkSearchOpParams &params) {
bNode &node = params.add_node("ShaderNodeMix");
node_storage(node).data_type = type;
params.update_and_connect_available_socket(node, "A");
});
params.add_item(IFACE_("B"), [type](LinkSearchOpParams &params) {
bNode &node = params.add_node("ShaderNodeMix");
node_storage(node).data_type = type;
params.update_and_connect_available_socket(node, "B");
});
}
}
}
static void node_mix_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeShaderMix *data = MEM_cnew<NodeShaderMix>(__func__);
data->data_type = SOCK_FLOAT;
data->factor_mode = NODE_MIX_MODE_UNIFORM;
data->clamp_factor = 1;
data->clamp_result = 0;
data->blend_type = MA_RAMP_BLEND;
node->storage = data;
}
static const char *gpu_shader_get_name(eNodeSocketDatatype data_type,
const bool non_uniform,
const int blend_type)
{
switch (data_type) {
case SOCK_FLOAT:
return "node_mix_float";
case SOCK_VECTOR:
return (non_uniform) ? "node_mix_vector_non_uniform" : "node_mix_vector";
case SOCK_RGBA:
switch (blend_type) {
case MA_RAMP_BLEND:
return "node_mix_blend";
case MA_RAMP_ADD:
return "node_mix_add";
case MA_RAMP_MULT:
return "node_mix_mult";
case MA_RAMP_SUB:
return "node_mix_sub";
case MA_RAMP_SCREEN:
return "node_mix_screen";
case MA_RAMP_DIV:
return "node_mix_div_fallback";
case MA_RAMP_DIFF:
return "node_mix_diff";
case MA_RAMP_DARK:
return "node_mix_dark";
case MA_RAMP_LIGHT:
return "node_mix_light";
case MA_RAMP_OVERLAY:
return "node_mix_overlay";
case MA_RAMP_DODGE:
return "node_mix_dodge";
case MA_RAMP_BURN:
return "node_mix_burn";
case MA_RAMP_HUE:
return "node_mix_hue";
case MA_RAMP_SAT:
return "node_mix_sat";
case MA_RAMP_VAL:
return "node_mix_val";
case MA_RAMP_COLOR:
return "node_mix_color";
case MA_RAMP_SOFT:
return "node_mix_soft";
case MA_RAMP_LINEAR:
return "node_mix_linear";
default:
BLI_assert_unreachable();
return nullptr;
}
default:
BLI_assert_unreachable();
return nullptr;
}
}
static int gpu_shader_mix(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
GPUNodeStack *in,
GPUNodeStack *out)
{
const NodeShaderMix &storage = node_storage(*node);
const bool is_non_uniform = storage.factor_mode == NODE_MIX_MODE_NON_UNIFORM;
const bool is_color_mode = storage.data_type == SOCK_RGBA;
const bool is_vector_mode = storage.data_type == SOCK_VECTOR;
const int blend_type = storage.blend_type;
const char *name = gpu_shader_get_name(
(eNodeSocketDatatype)storage.data_type, is_non_uniform, blend_type);
if (name == nullptr) {
return 0;
}
if (storage.clamp_factor) {
if (is_non_uniform && is_vector_mode) {
const float min[3] = {0.0f, 0.0f, 0.0f};
const float max[3] = {1.0f, 1.0f, 1.0f};
const GPUNodeLink *factor_link = in[1].link ? in[1].link : GPU_uniform(in[1].vec);
GPU_link(mat,
"node_mix_clamp_vector",
factor_link,
GPU_constant(min),
GPU_constant(max),
&in[1].link);
}
else {
const float min = 0.0f;
const float max = 1.0f;
const GPUNodeLink *factor_link = in[0].link ? in[0].link : GPU_uniform(in[0].vec);
GPU_link(mat,
"node_mix_clamp_value",
factor_link,
GPU_constant(&min),
GPU_constant(&max),
&in[0].link);
}
}
int ret = GPU_stack_link(mat, node, name, in, out);
if (ret && is_color_mode && storage.clamp_result) {
const float min[3] = {0.0f, 0.0f, 0.0f};
const float max[3] = {1.0f, 1.0f, 1.0f};
GPU_link(mat,
"node_mix_clamp_vector",
out[2].link,
GPU_constant(min),
GPU_constant(max),
&out[2].link);
}
return ret;
}
class MixColorFunction : public fn::MultiFunction {
private:
const bool clamp_factor_;
const bool clamp_result_;
const int blend_type_;
public:
MixColorFunction(const bool clamp_factor, const bool clamp_result, const int blend_type)
: clamp_factor_(clamp_factor), clamp_result_(clamp_result), blend_type_(blend_type)
{
static fn::MFSignature signature = create_signature();
this->set_signature(&signature);
}
static fn::MFSignature create_signature()
{
fn::MFSignatureBuilder signature{"MixColor"};
signature.single_input<float>("Factor");
signature.single_input<ColorGeometry4f>("A");
signature.single_input<ColorGeometry4f>("B");
signature.single_output<ColorGeometry4f>("Result");
return signature.build();
}
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
{
const VArray<float> &fac = params.readonly_single_input<float>(0, "Factor");
const VArray<ColorGeometry4f> &col1 = params.readonly_single_input<ColorGeometry4f>(1, "A");
const VArray<ColorGeometry4f> &col2 = params.readonly_single_input<ColorGeometry4f>(2, "B");
MutableSpan<ColorGeometry4f> results = params.uninitialized_single_output<ColorGeometry4f>(
3, "Result");
if (clamp_factor_) {
for (int64_t i : mask) {
results[i] = col1[i];
ramp_blend(blend_type_, results[i], std::clamp(fac[i], 0.0f, 1.0f), col2[i]);
}
}
else {
for (int64_t i : mask) {
results[i] = col1[i];
ramp_blend(blend_type_, results[i], fac[i], col2[i]);
}
}
if (clamp_result_) {
for (int64_t i : mask) {
clamp_v3(results[i], 0.0f, 1.0f);
}
}
}
};
static const fn::MultiFunction *get_multi_function(bNode &node)
{
const NodeShaderMix *data = (NodeShaderMix *)node.storage;
bool uniform_factor = data->factor_mode == NODE_MIX_MODE_UNIFORM;
const bool clamp_factor = data->clamp_factor;
switch (data->data_type) {
case SOCK_FLOAT: {
if (clamp_factor) {
static fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{
"Clamp Mix Float", [](float t, const float a, const float b) {
return math::interpolate(a, b, std::clamp(t, 0.0f, 1.0f));
}};
return &fn;
}
else {
static fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{
"Mix Float", [](const float t, const float a, const float b) {
return math::interpolate(a, b, t);
}};
return &fn;
}
}
case SOCK_VECTOR: {
if (clamp_factor) {
if (uniform_factor) {
static fn::CustomMF_SI_SI_SI_SO<float, float3, float3, float3> fn{
"Clamp Mix Vector", [](const float t, const float3 a, const float3 b) {
return math::interpolate(a, b, std::clamp(t, 0.0f, 1.0f));
}};
return &fn;
}
else {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
"Clamp Mix Vector Non Uniform", [](float3 t, const float3 a, const float3 b) {
t = math::clamp(t, 0.0f, 1.0f);
return a * (float3(1.0f) - t) + b * t;
}};
return &fn;
}
}
else {
if (uniform_factor) {
static fn::CustomMF_SI_SI_SI_SO<float, float3, float3, float3> fn{
"Mix Vector", [](const float t, const float3 a, const float3 b) {
return math::interpolate(a, b, t);
}};
return &fn;
}
else {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
"Mix Vector Non Uniform", [](const float3 t, const float3 a, const float3 b) {
return a * (float3(1.0f) - t) + b * t;
}};
return &fn;
}
}
}
}
BLI_assert_unreachable();
return nullptr;
}
static void sh_node_mix_build_multi_function(NodeMultiFunctionBuilder &builder)
{
const NodeShaderMix &storage = node_storage(builder.node());
if (storage.data_type == SOCK_RGBA) {
builder.construct_and_set_matching_fn<MixColorFunction>(
storage.clamp_factor, storage.clamp_result, storage.blend_type);
}
else {
const fn::MultiFunction *fn = get_multi_function(builder.node());
builder.set_matching_fn(fn);
}
}
} // namespace blender::nodes::node_sh_mix_cc
void register_node_type_sh_mix()
{
namespace file_ns = blender::nodes::node_sh_mix_cc;
static bNodeType ntype;
sh_fn_node_type_base(&ntype, SH_NODE_MIX, "Mix", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::sh_node_mix_declare;
ntype.ui_class = file_ns::sh_node_mix_ui_class;
node_type_gpu(&ntype, file_ns::gpu_shader_mix);
node_type_update(&ntype, file_ns::sh_node_mix_update);
node_type_init(&ntype, file_ns::node_mix_init);
node_type_storage(
&ntype, "NodeShaderMix", node_free_standard_storage, node_copy_standard_storage);
ntype.build_multi_function = file_ns::sh_node_mix_build_multi_function;
ntype.draw_buttons = file_ns::sh_node_mix_layout;
ntype.labelfunc = file_ns::sh_node_mix_label;
ntype.gather_link_search_ops = file_ns::node_mix_gather_link_searches;
nodeRegisterType(&ntype);
}

View File

@ -150,11 +150,11 @@ void register_node_type_sh_mix_rgb()
static bNodeType ntype;
sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR);
sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB_LEGACY, "Mix", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::sh_node_mix_rgb_declare;
ntype.labelfunc = node_blend_label;
node_type_gpu(&ntype, file_ns::gpu_shader_mix_rgb);
ntype.build_multi_function = file_ns::sh_node_mix_rgb_build_multi_function;
ntype.gather_link_search_ops = nullptr;
nodeRegisterType(&ntype);
}