blender/intern/cycles/kernel/osl/shaders/node_integer_math.osl
Jacques Lucke ba82476ade Shader Nodes: support Integer Math node in EEVEE and Cycles
This adds the Integer Math node to shader nodes. It's the same node that
can already be used in Geometry Nodes and the Compositor.

While creating the regression test, I noticed that the integer math
implementation was a bit broken:
* The integer modulo (`%`) operator on the GPU is undefined for negative
  divisors (the added regression test showed that the behavior is not
  consistent across platforms).
* Use integer power function instead of using float implementation with
  casts.

The MaterialX implementation is omitted for now. I didn't look into it
in detail yet but for the Boolean Math node (#162798) it seemed like
some more work is needed there to support this type.

Pull Request: https://projects.blender.org/blender/blender/pulls/163805
2026-09-14 10:36:19 +02:00

96 lines
2.7 KiB
Text

/* SPDX-FileCopyrightText: 2026 Blender Authors
*
* SPDX-License-Identifier: Apache-2.0 */
#include "stdcycles.h"
int integer_math_divide_floor(int a, int b)
{
int d = a / b;
int r = a % b;
return (r != 0) ? (d - int((a < 0) != (b < 0))) : d;
}
int integer_math_gcd(int a_in, int b_in)
{
int a = abs(a_in);
int b = abs(b_in);
while (b != 0) {
int t = b;
b = a % b;
a = t;
}
return a;
}
int integer_math_power(int base_in, int exponent_in)
{
int base = base_in;
int exponent = exponent_in;
if (exponent < 0) {
if (base == 1 || base == -1) {
return (base < 0 && (exponent & 1) != 0) ? -1 : 1;
}
return 0;
}
int result = 1;
while (exponent != 0) {
if ((exponent & 1) != 0) {
result *= base;
}
exponent = exponent >> 1;
base = base * base;
}
return result;
}
shader node_integer_math(
string math_type = "add", int Value1 = 0, int Value2 = 0, int Value3 = 0, output int Value = 0)
{
if (math_type == "add")
Value = Value1 + Value2;
else if (math_type == "subtract")
Value = Value1 - Value2;
else if (math_type == "multiply")
Value = Value1 * Value2;
else if (math_type == "divide")
Value = (Value2 != 0) ? (Value1 / Value2) : 0;
else if (math_type == "multiply_add")
Value = Value1 * Value2 + Value3;
else if (math_type == "power")
Value = integer_math_power(Value1, Value2);
else if (math_type == "floored_modulo")
Value = (Value2 != 0) ? (((Value1 % Value2) + Value2) % Value2) : 0;
else if (math_type == "absolute")
Value = abs(Value1);
else if (math_type == "minimum")
Value = min(Value1, Value2);
else if (math_type == "maximum")
Value = max(Value1, Value2);
else if (math_type == "gcd")
Value = integer_math_gcd(Value1, Value2);
else if (math_type == "lcm") {
int gcd = integer_math_gcd(Value1, Value2);
Value = (gcd != 0) ? abs(Value1 / gcd * Value2) : 0;
}
else if (math_type == "negate")
Value = -Value1;
else if (math_type == "sign")
Value = int(0 < Value1) - int(Value1 < 0);
else if (math_type == "divide_floor")
Value = (Value2 != 0) ? integer_math_divide_floor(Value1, Value2) : 0;
else if (math_type == "divide_ceil")
Value = (Value2 != 0) ? -integer_math_divide_floor(Value1, -Value2) : 0;
else if (math_type == "divide_round") {
int abs_b = abs(Value2);
int sign_b = int(0 < Value2) - int(Value2 < 0);
if (Value1 >= 0)
Value = ((abs_b != 0) ? ((2 * Value1 + abs_b) / (2 * abs_b)) : 0) * sign_b;
else
Value = -((abs_b != 0) ? ((2 * -Value1 + abs_b) / (2 * abs_b)) : 0) * sign_b;
}
else if (math_type == "modulo")
Value = (Value2 != 0) ? (Value1 % Value2) : 0;
else
Value = 0;
}