1
$\begingroup$

I have written a function in GLSL that returns the min index (index 0 is the x component, 1 is the y component, and 2 is the z component) of a vec3 variable.

uint getMinIndex(vec3 vector)
{
    float minValue = min(min(vector.x, vector.y), vector.z);
    if(vector.x == minValue)
        return 0;
    if(vector.y == minValue)
        return 1;
    return 2;
}

As you can see, this function causes branching and slows down the whole shader.... Is there already such a function in GLSL? Or is there a better way to do this without branching?

$\endgroup$

2 Answers 2

2
$\begingroup$

No, but the way this is written has a min of 3 branch's and max of 4. It would be better off with the plain old (x<y)&&(x<z) return 0, y<z return 1, return 2.

You could also do a pure logic op approach:

int mx = (y<z)&&(y<x);
int mz = (((z<y) && (z<x))*2;
return mx + mz; // both are false if x is min value and sum is zero

which would turn into this:

uint MinIndex( float x, float y, float z )
{
   return ((y<z)&&(y<x)) + ((((z<y)&&(z<x))*2);
}

If y,z could be equal and less then x then an extra + ((y==z)&&(x<y)) would be needed which would return 1.

There may be some clever way to reduce this down to less operations but it's not popping into my head... at any rate logic operations are often fast but ugly to look at.

You might also try the mix function, something along the lines of...

uint MinIndex(float x, float y, float z)
{
   float YltX = float(y < x);
   float ZltX = float(z < x);

   return uint(mix(YltX, 2.0 * ZltX, ZltX+float(y==z)));
}

I haven't checked this one for accuracy but using mix also tends to be rather speedy.

$\endgroup$
1
  • $\begingroup$ Wow that's very creative =) $\endgroup$
    – Thomas
    Aug 26, 2023 at 18:44
1
$\begingroup$

Here is another approach that handles the case where multiple components may be the minimum/maximum and you want to handle that. You calculate a "mask" that indicates whether the component is a min/max.

bvec3 mask = lessThanEqual(vector.xyz, min(vector.yzx, vector.zxy); // makes a "one-hot" bool vector, where the min component(s) is true and the others are false.

To use it, multiply your operation by the mask, e.g.

vec3f colorMask = greaterThanEqual(color.rgb, min(color.gbr, color.brg));
vec3f maxColor = color * vec3f(colorMask);

You can also convert the vector to a scalar, like so:

uint index = dot(ivec3(mask), ivec3(0, 1, 2));

But that fails if multiple components are minimum/maximum.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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