3
$\begingroup$

I am creating a color saturation node group in blender as an exercise. This is the code I'm trying to implement:

RGBtoHSV= function(color) {
    var r,g,b,h,s,v;
    r= color[0];
    g= color[1];
    b= color[2];
    min = Math.min( r, g, b );
    max = Math.max( r, g, b );


    v = max;
    delta = max - min;
    if( max != 0 )
        s = delta / max;        // s
    else {
        // r = g = b = 0        // s = 0, v is undefined
        s = 0;
        h = -1;
        return [h, s, undefined];
    }
    if( r === max )
        h = ( g - b ) / delta;      // between yellow & magenta
    else if( g === max )
        h = 2 + ( b - r ) / delta;  // between cyan & yellow
    else
        h = 4 + ( r - g ) / delta;  // between magenta & cyan
    h *= 60;                // degrees
    if( h < 0 )
        h += 360;
    if ( isNaN(h) )
        h = 0;
    return [h,s,v];
};

This is the part that I am struggling to implement:

    if( r === max )
    h = ( g - b ) / delta;      // between yellow & magenta
else if( g === max )
    h = 2 + ( b - r ) / delta;  // between cyan & yellow
else
    h = 4 + ( r - g ) / delta;  // between magenta & cyan

I am trying to figure out how to chain math comparison nodes in the right way but I am just stuck.

Here is the node group so-far (Updated):

Nodes

At the very end is where I am stuck. I'm not sure how to set up a three-way if/else node structure.

If r === max, else if g === max, else catch-all.

Any help would be hugely, hugely, appreciated!

$\endgroup$
6
$\begingroup$

Rewrite the code as

h1 =     (g - b) / delta;  // between yellow & magenta
h2 = 2 + (b - r) / delta;  // between cyan & yellow
h3 = 4 + (r - g) / delta;  // between magenta & cyan

h  = (r==max) * h1
h += (1 - (r==max)) * (g==max) * h2
h += (1 - (r==max)) * (1 - (g==max)) * h3

where (X==Y) means 1 if X and Y are equal, 0 if they're different (ie. what the "Compare" operation on a Math node does).

This is just a bunch of arithmetic operations, so it's straightforward, albeit laborious, to translate into nodes.

edit

Here's how I did it with nodes. This does

tmp = (g==max) ? h2 : h3;
h   = (r==max) ? h1 : tmp;

The If Then Else node can be done with a lerp

$\endgroup$
1
  • $\begingroup$ I seriously appreciate the help. I can kind of see where you're going with this, but I don't understand how to implement the bottom half. Why are you doing += operations instead of comparisons? I realize it's pretty laborious, but is there any way you could possibly put together a sample of how you would go about it? Pretty please? :) $\endgroup$ – Jay Feb 19 at 22:35
3
$\begingroup$

I have an answer here where I use Compare nodes to create a switch with a default case.

enter image description here

At this point I have to point out that there is overlap at the minima and maxima of these nodes, and the winner is dictated by the order of the MixRGB nodes.

For example, a value of .2 activates the code path of both the first and second Compare nodes, but the yellow color would win out.

$\endgroup$
3
$\begingroup$

An If is just a lerp (linear interpolation) with boolean (1 or 0) inputs. That's the easy way to think of it.

And a lerp is, currently, most easily implemented with map range node:

enter image description here

I've labelled value nodes with "ELSE" and "IF" to make it clear which is which. So what I'm doing here is saying, If UV.x > 0.5 then 1.0 else 0.0.

So let's look at one arm of your actual code:

 if( r === max )
     h = ( g - b ) / delta;      // between yellow & magenta
 else if( g === max )
     h = 2 + ( b - r ) / delta;  // between cyan & yellow
 else
     h = 4 + ( r - g ) / delta;  // between magenta & cyan

Now, let's rewrite that a bit, because the order of operations isn't particularly clear from the way that code is written:

 if( r === max ) {
     h = ( g - b ) / delta;      // between yellow & magenta
 }     
 else {
      if( g === max ) {
          h = 2 + ( b - r ) / delta;  // between cyan & yellow
      }
      else {
           h = 4 + ( r - g ) / delta;  // between magenta & cyan
      }
  }

With the reordering, we can see that our else if is really a nested if statement. Let's implement it in nodes:

enter image description here

Using labelled value nodes for our inputs, and assuming that === means equality comparison (I'm not familiar with that usage.) And our output is of course h, which is the only thing that snippet assigns.

$\endgroup$
3
  • $\begingroup$ I'm going to have to study your implementation. The solution I came up with is a lot more complex. This looks much more graceful. I really, really appreciate your help! If you wouldn't mind taking a look at another question of mine: blender.stackexchange.com/questions/212430/… I would really like to see how you would approach that as well. $\endgroup$ – Jay Feb 20 at 0:05
  • $\begingroup$ @Jay I took a quick look. Think about it this way: IF i = 1 then r=v ELSE IF i = 2 r = q ELSE if i = 3.... Operating in parallel with IF i = 1 then g= t.... It's going to be a complete mess, that's just something you're going to have to accept when trying to implement branching logic in nodes. $\endgroup$ – Nathan Feb 20 at 0:08
  • $\begingroup$ Thanks much for looking. I'm still just stuck. I understand that it's just a branching if/else construct. But the part that gets me is assigning multiple different values to the RGB outputs depending on the value of I. $\endgroup$ – Jay Feb 20 at 0:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.