Search code examples
openglglsl

GLSL point inside box test


Below is a GLSL fragment shader that outputs a texel if the given texture coord is inside a box, otherwise a color is output. This just feels silly and the there must be a way to do this without branching?

uniform sampler2D texUnit;

varying vec4 color;
varying vec2 texCoord;

void main() {
  vec4 texel = texture2D(texUnit, texCoord);
  if (any(lessThan(texCoord, vec2(0.0, 0.0))) ||
      any(greaterThan(texCoord, vec2(1.0, 1.0))))
    gl_FragColor = color;
  else
    gl_FragColor = texel;
}

Below is a version without branching, but it still feels clumsy. What is the best practice for "texture coord clamping"?

uniform sampler2D texUnit;

varying vec4 color;
varying vec4 labelColor;
varying vec2 texCoord;

void main() {
  vec4 texel = texture2D(texUnit, texCoord);
  bool outside = any(lessThan(texCoord, vec2(0.0, 0.0))) ||
                 any(greaterThan(texCoord, vec2(1.0, 1.0)));
  gl_FragColor = mix(texel*labelColor, color,
                     vec4(outside,outside,outside,outside));
}

Here is the rendering result

I am clamping texels to the region with the label is -- the texture s & t coordinates will be between 0 and 1 in this case. Otherwise, I use a brown color where the label ain't.

Note that I could also construct a branching version of the code that does not perform a texture lookup when it doesn't need to. Would this be faster than a non-branching version that always performed a texture lookup? Maybe time for some tests...


Solution

  • Use step function to avoid branching and get the best performance:

    // return 1 if v inside the box, return 0 otherwise
    float insideBox(vec2 v, vec2 bottomLeft, vec2 topRight) {
        vec2 s = step(bottomLeft, v) - step(topRight, v);
        return s.x * s.y;   
    }
    
    float insideBox3D(vec3 v, vec3 bottomLeft, vec3 topRight) {
        vec3 s = step(bottomLeft, v) - step(topRight, v);
        return s.x * s.y * s.z; 
    }
    
    void main() {
        vec4 texel = texture2D(texUnit, texCoord);
    
        float t = insideBox(v_position, vec2(0, 0), vec2(1, 1));
        gl_FragColor = t * texel + (1 - t) * color;
    }