Why Aren't There Logical Operator, ==, <=, And Similar Nodes?

I’m almost completely a non-programmer, but I am interested in a very simple question. We have ‘Less Than’ and ‘Greater Than’ math nodes for the compositor and materials. Why don’t we have nodes for ‘Equals To’, ‘Greater Than Or Equal To’, ‘Less Than Or Equal To’. Also, why don’t we have at least rudimentary logical operator nodes for things like AND, OR, and NOT?

I’m rather perplexed by this, since I could go into the source code for Blender, find the part that seemed to say what math nodes do (COM_MathBaseOperation.cpp and/or node_shader_math.c I think?) and, using a snippet from either of them, come up stuff that at least looked right.

For example, a snippet taken from COM_MathBaseOperation.cpp:
void MathLessThanOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
{
float inputValue1[4];
float inputValue2[4];

this-&gt;m_inputValue1Operation-&gt;readSampled(inputValue1, x, y, sampler);
this-&gt;m_inputValue2Operation-&gt;readSampled(inputValue2, x, y, sampler);

output[0] = inputValue1[0] &lt; inputValue2[0] ? 1.0f : 0.0f;

clampIfNeeded(output);

}

And now, what I cooked up (sorry it’s not formatted right, I couldn’t figure out how to preserve the formatting):
void MathLessThanOETOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
{
float inputValue1[4];
float inputValue2[4];

this-&gt;m_inputValue1Operation-&gt;readSampled(inputValue1, x, y, sampler);
this-&gt;m_inputValue2Operation-&gt;readSampled(inputValue2, x, y, sampler);

output[0] = inputValue1[0] &lt;= inputValue2[0] ? 1.0f : 0.0f;

clampIfNeeded(output);

}
void MathEqualToOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
{
float inputValue1[4];
float inputValue2[4];

this-&gt;m_inputValue1Operation-&gt;readSampled(inputValue1, x, y, sampler);
this-&gt;m_inputValue2Operation-&gt;readSampled(inputValue2, x, y, sampler);

output[0] = inputValue1[0] == inputValue2[0] ? 1.0f : 0.0f;

clampIfNeeded(output);

}
void MathNotEqualToOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
{
float inputValue1[4];
float inputValue2[4];

this-&gt;m_inputValue1Operation-&gt;readSampled(inputValue1, x, y, sampler);
this-&gt;m_inputValue2Operation-&gt;readSampled(inputValue2, x, y, sampler);

output[0] = inputValue1[0] != inputValue2[0] ? 1.0f : 0.0f;

clampIfNeeded(output);

}

For another example, a snippet from node_shader_math.c:
case NODE_MATH_LOG:
{
/* Don’t want any imaginary numbers… */
if (a > 0 && b > 0)
r = log(a) / log(b);
else
r = 0.0;
break;
}

And now, what I cooked up:
case NODE_MATH_AND:
{
if (a != 0 && b != 0)
r = 1.0;
else
r = 0.0;
break;
}
case NODE_MATH_OR:
{
if (a != 0 || b != 0)
r = 1.0;
else
r = 0.0;
break;
}
case NODE_MATH_NOT:
{
if (a == 0)
r = 1.0;
else
r = 0.0;
break;
}

I’ve learned the basics of how to write in a programming language, but was never really taught ‘big picture stuff’ like what I’m sure is needed for a project as big as blender. I’m sorry if this is a dumb question, but is there some reason why seemingly simple stuff like what I came up with can’t be put in?

Hi, I’m no programmer but afaik AND is basically a multiplication of two booleans, OR is addition, etc. so you get this with simple operations already.

I guess the AND/Multiplication and OR/Addition thing can work (just gotta be careful of negative numbers) though personally I think it would be easier to read (and maybe slightly faster since there would be fewer calculations involved) to have separate AND and OR nodes with simpler code. But what about NOT? The only currently available equivalent that I can figure out is a node group (Absolute->Greater Than(0)->Arccos) and, if my understanding of how node systems work is any good, a single node is usually faster than a node group.

Also, I didn’t mean to imply that Blender’s node system should somehow gain a new data type to pass. I reasoned having a standard that said a float value = 0.0 is equivalent to False and a float value equal to anything else meant True would work.

The main reason that i can think of why there is no equal comparison, is that there usually is no equality with float numbers. To understand why you first need to understand how float numbers actually work, and i think they answer both questions in a simple way in this thread.

To truly compare a float you check if it’s in an small interval:


epsilon = 0.0001 

isZero = x &lt; 0 + epsilon && x &gt; 0 - epsilon

Where epsilon is related to how large floats you are comparing!

With integers however an equality operator could be useful but im pretty sure only floats is used for the nodes.

Yeah, I’m pretty sure nodes only deal in Floats, Images, Vectors, and BSDFs. I was vaguely aware of the whole ‘inaccuracy of floats’ thing before, but it was never really an issue for me until now. The thread left me a bit confused though. Wouldn’t the inaccuracy also apply to ‘greater than’ and ‘less than’ comparisons (which we do have as Math nodes) too?

Good question, I am following this.

Yes it would apply for greater/lesser comparisons. However it would require a very extreme case for it to be a problem. When comparing with a value that is either 0 or 1, then you would add the interval writing something like “greater then 0.9999”. If you would compare it on a sliding scale it would be hard to notice since it would affect 1 pixel left or right in the comparison. And when i use nodes it’s usually not precise functions.

The only remotely related problem i’ve had with this is merging two renders from depth buffers where a greater then operator gives you a sharp edge between the images. I suppose a float error could occur, for me however the problem was dealing with the edge.

The major questions is more about equality comparison since an operation on the float could make the expected “1” be something like “1.0000012” and it would very easily mess things up for someone who didn’t know about the problem.

I agree, it definitely could mess things up for those who didn’t know about the problem, but then let’s consider the confusion that arises when a novice user looks at the list of possible math functions and sees ‘Greater than’ and ‘Less Than’ but not ‘Equals To’. It could easily be seen as a rather annoying oversight rather than a deliberate omission made as a preventative measure.

Also, I’m concerned that those who do actually need/want an ‘Equals To’ node will intuitively come up with a node group consisting of:
Value being checked---->Greater Than(Target value-Epsilon)------------>Multiply---->Result
|------------------------->Less Than(Target value+Epsilon)------------------|
(Check if the given value is Greater Than the Target value minus Epsilon, check if the given value is Less Than the Target Value plus Epsilon, pass the results to a multiply node (used as an AND logical operator here) and thus get a value of 1 when the given value is ‘Equal To’ the Target value.)

My chief concern with that workaround is that (as far as I know) it is going to be considerably slower than a simple ‘Equals To’ node. I’m no expert at rendering times and what causes them to get longer or shorter, but I would think that having to execute 3 nodes would take more time than executing one. Especially if, say, they were using it in a compositor node tree where the tree executes for each pixel. Even a moderately-sized image would have thousands if not millions of pixels, and the increased rendering time caused by having to execute 3 nodes instead of 1 would be seen by each pixel. And obviously, the problem could become even more worse than that if they had to use the node group more than once in the same tree.

I’m also a bit worried about the accuracy of that workaround. I can’t quite figure out if it would be more accurate, less accurate, or as accurate as a simple ‘Equals To’ node. My instincts tell me it would be as accurate, but I’m not entirely certain.

Finally, the lack of an ‘Equals To’ node means there’s a lack of a documentation section for it, which means that those who devise a workaround for the ‘missing’ ‘Equals To’ node have no references to read to discover considerations to be made when using such a node such as the inaccuracy of Float values after calculations.

Are these concerns of mine valid or is there nothing to worry about?

Also, and I’m sorry this is such a long post, what if there weren’t any calculations done before checking the values with an ‘Equals To’ node? What if a person were just checking if a light ray had bounced only 1 time before reaching the camera? Or if an Object’s Object Index were equal to 2? I remember reading cases where finding out if those things were true or not was rather critical to entire projects. These values aren’t passed through any calculations before being used in those cases, they simply come from an input node and go directly to an ‘Equals To’ node or such a node’s nearest available equivalent. Would these ‘direct’ values be inaccurate too? Or could they be safely checked for equality?

I have assumed they decided to not include them and you can always debate how things should be done :wink:

Performance issues are more complex then ‘we are doing more here it must impact performance’. I can atleast answer your ‘considerably slower’ with no. And i assume it’s in the range of being ‘less then’/‘a fraction’ of a second if you spent an hour rendering.

I feel you put to much weight in the meaning of “accuracy” in the accuracy we are talking about:

  • Colors is represented by 32 bit float and is usually kept in range 0-1 (operations on floats in a large range is the main reason for accuracy problems) and it gives something like 0.000…001 in error per operation (meaning it does not apply at all).
  • it affects cases where high presicion is required (and then you are probably somewhat interested in math)
  • the whole render system is affected by it already even if it’s minimized

Equals is a precision problem (as it should be an exact match), however when i think about it more if implemented it should “minimize the problem” by including the interval check which means that my reason for not including it is moot.

Summary i’m also slightly annoyed that it is not there :frowning:

However it’s not that much of a hassle to solve it and i can’t see the solution impacting performance by anything that matters :slight_smile:

Today I found this technique that it says you can add a “Driver” into the node and does exactly the work. It might be a “hack” but it will work superb!

Ideally you do not care about it being a “Node” or a “Driver”, you need to think to give it some values and get the result back.

Sweeeeet!!!