Page MenuHome

Nodes: Add Root operation to math node (all Editors)
Needs ReviewPublic

Authored by Blender Defender (Blender_Defender) on Mar 2 2021, 11:50 PM.

Details

Reviewers
Brecht Van Lommel (brecht)
Group Reviewers
Nodes
Summary

Nodes: Add Root operation to math node

This operation calculates the Root of A index B, this feature is good
to have in Blender, because Root Operations shouldn't be hidden in the
Power operation and a Square Root Operation might not be enough for
certain cases.


This patch adds a simple Root operation to the math node, as I proposed on right-click select: https://blender.community/c/rightclickselect/cfhbbc/

I tested it on Windows 10 for the following Node Editors:
Shader/Cycles:

Shader/Eevee:

Geometry Nodes Math (New Version 2022):

Compositor:

Texture Nodes:

Message to the reviewers: This is my first patch, please formulate your feedback detailed. Thank you :)

Diff Detail

Repository
rB Blender

Event Timeline

It's a lot of places to change to add a new maths function isn't it! 😆

Hans Goudey (HooglyBoogly) added inline comments.
source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
97–98

By putting the return value here you change the return value for all of the previous operations too. Just put this new value above the existing return true; in this function

It's a lot of places to change to add a new maths function isn't it! 😆

Oh yes, it is (It is a change for five Editors if you count Cycles)! But thanks to VS Code and the Keyword I had to look for, I found them. :)

Blender Defender (Blender_Defender) marked an inline comment as done.

@Hans Goudey (HooglyBoogly) Okay, good to know, thank you. I've put it under the POWER-Case, because a Root is (somehow) the contrary of the Power Operation.

Generally looks fine, but got some remarks anyway:

  • Please apply clang-format to your changes. You can do that by running the make format command or by setting up your editor to apply clang-format automatically when you save a file.
  • Comments should start with a capital letter and end with a dot.
  • Why do some of the implementations handle more cases than others?

Generally looks fine, but got some remarks anyway:

  • Please apply clang-format to your changes. You can do that by running the make format command or by setting up your editor to apply clang-format automatically when you save a file.

How do I set up VS Code to apply clang-format automatically? That would help me very much :)

  • Why do some of the implementations handle more cases than others?

Oh, I guess I forgot to add that for the ones that handle less cases. Could you highlight the code sections that handle less cases than the others?

Well, just from looking at the code it seems like MathRootOperation::executePixelSampled than e.g. the code in svm_math_util.h.
Maybe you can just add a new safe_root function and use that in the cases where it is possible?

Well, just from looking at the code it seems like MathRootOperation::executePixelSampled than e.g. the code in svm_math_util.h.
Maybe you can just add a new safe_root function and use that in the cases where it is possible?

"MathRootOperation::executePixelSampled" / "node_texture_math.c" --> It looks like that's Code left from the code I reused, and I think the second big else statement can be safely removed, since the root of a negative number is not defined (at least not with real numbers)

Maybe you can just add a new safe_root function and use that in the cases where it is possible?

I thought of that, but I didn't find the file where e.g. safe_sqrt() is defined. I'll try to find it, a function like this would be a good idea.

@Jacques Lucke (JacquesLucke) I've updated the 2 comments (and commented the files where the extra cases should be removed). They now start with a capital letter and end with a dot. Is it better that way?

There inconsistency between the different implementations. Please implement all of them as follows:

(b > 0.0) ? safe_pow(a, 1.0 / b) : 0.0

Where safe_pow is exactly the function used by the Power operation. To avoid code duplication with the Power operation, add safe_pow functions where they don't already exist.

Note also that a should not be required to be positive, negative numbers are fine for cube roots.

@Brecht Van Lommel (brecht) Ok, thank you for your feedback. Should I use a safe_root() function, like @Jacques Lucke (JacquesLucke) suggested, or is that unnecessary?

I think that's unnecessary, just safe_pow should be enough.

@Brecht Van Lommel (brecht) Ok, then I'll do it without a save_root() function

Note also that a should not be required to be positive, negative numbers are fine for cube roots.

@Brecht Van Lommel (brecht) The problem with that is, that it's only possible if the index is an odd number. And the different power functions (glsl pow(), osl pow(), pow() and safe_powf()) all return a different solution for e.g. cubic root of -27:

  • glsl pow() [Eevee] returns -3
  • pow() [Compositor, Texture Nodes] returns NaN
  • safe_powf() and osl pow() [Cycles, Geometry Nodes] return 0

Also, from a user perspective, it might be confusing changing the index for a negative number when the result is 0 almost everywhere, except for an odd number as index.
In order to get that working, osl would have to change its pow() function, so I guess we're limited to positive numbers only. ¯\_(ツ)_/¯

Please let me know if you know a better way to make negative cubic (, ...) roots possible. Thanks!

Blender Defender (Blender_Defender) edited the summary of this revision. (Show Details)

@Brecht Van Lommel (brecht) I've updated it, so now it follows the style (a > 0.0 && b > 0.0) ? pow(a, 1 / b): 0.0, but the issue I'm having with not checking if a is greater than zero is, that every power functions returns a different number (for more information read my previous comment) and it only applies for odd indexes, so taking care of that would make it more complex, and a safe_rootf() function might be needed. In order for me to be able to progress, I'd need feedback on what I should do to fit Blender's Style the best. Thank you!

It seems you've found an inconsistency indeed! I did a quick summary of the pow functions which this relies upon. Since this hasn't been fixed it is unlikely that negative numbers have been used much.

AreaPow FunctionValue check
Cyclessafe_powfChecks values then calls compatible_powf which uses fmod
OSLpowCalls native pow function
Eeevee GLSLmath_powerChecks values then calls compatible_pow which uses mod
NOD Mathsafe_powfChecks values then calls powf
Compositor and Texture NodesCustom functionChecks values then calls powf

GLSL
https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/pow.xhtml
pow returns the value of x raised to the y power, i.e. xy. The result is undefined if x<0 or if x=0 and y≤0.

OSL
https://github.com/AcademySoftwareFoundation/OpenShadingLanguage/blob/master/src/doc/osl-languagespec.pdf
type pow (type x, float y)
Computes x^y. This function will return 0 for “undefined” operations, such as pow(-1,0.5).

It seems you've found an inconsistency indeed! I did a quick summary of the pow functions which this relies upon. Since this hasn't been fixed it is unlikely that negative numbers have been used much.

AreaPow FunctionValue check
Cyclessafe_powfChecks values then calls compatible_powf which uses fmod
OSLpowCalls native pow function
Eeevee GLSLmath_powerChecks values then calls compatible_pow which uses mod
NOD Mathsafe_powfChecks values then calls powf
Compositor and Texture NodesCustom functionChecks values then calls powf

GLSL
https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/pow.xhtml
pow returns the value of x raised to the y power, i.e. xy. The result is undefined if x<0 or if x=0 and y≤0.

OSL
https://github.com/AcademySoftwareFoundation/OpenShadingLanguage/blob/master/src/doc/osl-languagespec.pdf
type pow (type x, float y)
Computes x^y. This function will return 0 for “undefined” operations, such as pow(-1,0.5).

Now, the question is, what should we do about it? A cheap workaround would be to make negative numbers positive, then take the root and make it negative again, but this couldn't be done without creating at least two root() functions. That would require more work. The other possibility would be to say "negative roots are undefined" and require a to be positive as well (as it's done here). What do you think would be better?

The other possibility would be to say "negative roots are undefined" and require a to be positive as well (as it's done here).

I think do this then the pow and negative number issue can be addressed in a new patch. At least for the patch make it consistent as to how root works.

I think do this then the pow and negative number issue can be addressed in a new patch. At least for the patch make it consistent as to how root works.

So I guess I have to leave that as it is and wait for the reviewers feedback now...

The reviewer would need to approve/commit the patch anyway so I'm sure they'll add their thoughts at that time.

I updated the Diff to be up to date with Blender 3.0 (Two file extensions changed, one file changed)

Blender Defender (Blender_Defender) retitled this revision from Add Root operation to math node (all Editors) to Nodes: Add Root operation to math node (all Editors).Sep 28 2021, 10:23 PM

I have updated the diff to work with the current Blender Codebase.
@Brecht Van Lommel (brecht) Could you look at this patch again and make a suggestion for handling negative values? As we talked about in the previous comments, due to an inconsistency between the different pow functions it is not possible to allow negative values for a and b. I'm looking forward to hearing your feedback.