I’ve been recently playing a little bit with GLSL/Core Image shaders. One of the earliest limitations I ran into was the fact that you cannot have conditional results. In other word, if you do an if/else test, the output of your shader cannot depend on it. (Not entirely sure why you’d want to use if/else at all, then…)

That’s rather annoying. Thankfully, that’s a solved problem. Many CPUs prefer non-branching code, too, and there’s a long history of creating non-branching version of code that technically should branch. The trick here is “creative” use of math and/or bit-logic functions. As a simple example, let’s say I have a threshold shader - values below the minimum value should have a brightness of 0, and values higher than the max value should have a brightness of 1.

step() function to the rescue: step(a,b) returns 0 if a < b, 1 if a > b. So to cut off the low range, we have

brightness = step( low, brightness ) * brightness;

Very similarly, to check against the max value, we force brightness to be greater than 1 for pixels above the maximum threshold and then clamp to 1.0


  brightness = brightness + step(  high, brightness );
  brightness = min( 1.0, brightness );
 

That’s all there is to it. And in case you actually came for the filter code, here it is - with input sanitizing, nicer input values (midpoint/range), grayscale conversion, all the fun stuff.


kernel vec4 threshold(sampler image, float midPoint, float range )
{
  vec4 pixel=unpremultiply( sample(image, samplerCoord(image)) );

  float high = midPoint + range * 0.5;
  float low = midPoint - range * 0.5;
 
  high = min(1.0, high);
  low = max(0.0, low);
 
  float brightness = 0.3 * pixel.x + 0.59 * pixel.y+ 0.11 *pixel.z;
   
  brightness = step( low, brightness ) * brightness;
 
  brightness = brightness + step(  high, brightness );
  brightness = min( 1.0, brightness );
   
  pixel.x = pixel.y =pixel.z = brightness;
 
  return premultiply(pixel);
}

 

Commentary

  1. Patrick Geiller wrote on 02. Jul 2008

    You can also use the ternary operator directly :

    pixel.a = someValue > 4.5 ? 1.0 : 0.5; pixel.a = someValue > 4.5 ? (someValue > 6.0 ? 1.0 : 0.7) : 0.5;

    Good luck with your shaders ! I’ve found the Apple Documentation to be quite lacking, I’m still looking for a list of all available functions.

Leave a reply