Search code examples
androidopengl-esglslglsles

Android Opengl: Alternate values for gl_FragColor


I'm writing an Android application that utilizes opengl to perform some changes to the camera output. I've written my code in such a way that I finally figured out what is causing the performance issue.

#extension GL_OES_EGL_image_external : require
precision mediump float;
uniform samplerExternalOES sTexture;
uniform vec4 vColor;
const int MAX_COLORS = 6;
uniform float vHues[MAX_COLORS];
uniform float vOffsets[MAX_COLORS];
varying vec2 v_CamTexCoordinate;

float rgb2hue(vec4 c) {
    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
    vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));

    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return abs(q.z + (q.w - q.y) / (6.0 * d + e));
}

bool isInRange(float a1,float a2, float vOffset) {
    if(a2 < 0.0) {
        return false;
    } else if(abs(a1-a2) < vOffset) {
        return true;
    } else if( a1 > a2) {
        return (1.0 - a1 + a2) < vOffset;
    } else {
        return (1.0 + a1 - a2) < vOffset;
    }
}

vec4 getGrey(vec4 c) {
    float grey = (c.r + c.g + c.b) / 3.0;
    return vec4(grey, grey, grey, c.a);
}

void main() {
    vec4 c = texture2D(sTexture, v_CamTexCoordinate);
    bool hasColor = vHues[0] >= 0.0;
    float hue = rgb2hue(c);
    vec4 test = getGrey(c);
    for(int i=0; i < MAX_COLORS; i++) {
       if(isInRange(hue, vHues[i], vOffsets[i])) {
          //If I uncomment this line the performance gets terrible
          //test = c;
       }
    }
    gl_FragColor = test;
}

There is a significant hit to performance (a lot of frames are skipped) when I uncomment the line above. I basically want to use the original color sometimes and a different color depending on some condition. It works but is there a more efficient way to do this? Also, why does this perform so poorly?


Solution

  • If statements and braching in general are a bad idea because your GPU can't optimise properly. See Efficiency of branching in shaders for more details. You should try avoiding the branch, even if it means more calculation.

    The following code should do the trick

    //summarise your isInRange function
    bool isInRange = a2 >= 0.0 && ((abs(a1-a2) < vOffset) || (1.0 - max(a1, a2) + min(a1, a2)) < vOffset); 
    
    //this will be 0.0 if false and 1.0 if true
    float isInRangef = float(isInRange);
    
    //multiply by the float condition to set the value
    gl_FragColor = isInRangef * c + (1.0 - isInRangef) * getGrey(c);
    

    As a general formula

    • convert the bool you're branching on to a float condition
    • set your target value as (condition)*valueIfTrue + (1.0 - condition)*valueIfFalse