The cost of binding shaders may not be trivial, but it's not going to be your bottleneck unless you're rendering thousands of items without batching all the objects that use the same shaders.
Though I'm not sure if this applies to mobile devices, but GPUs aren't horrendously slow with branches if the condition is between a constant and a uniform. Both are valid, both have been used in the past an will continue to be used in the future, pick whichever one you think would be cleaner in your case.
Additionally, there are a few other ways to accomplish this: "Uber-shaders" and a little trickery with the way OpenGL shader programs are linked.
"Uber-shaders" are essentially the first choice, minus the branching, but you'll have multiple shaders. Instead of using
if statements, you use the preprocessor -
#endif, and compile different versions, including the proper
#defines for what you need.
color = in_color;
color = obj_color;
You can also break the shader up into separate functions. Have one shader that defines prototypes for all the functions and calls them, link a bunch of extra shaders that include the proper implementations. I've used this trick for shadow mapping, to make it easy to swap out how filtering is done on all objects without having to modify all the shaders.
//ins, outs, uniforms
//shading stuff goes here
gl_FragColor = color * getShadowCoefficient();
Then, I could have multiple other shader files that define
getShadowCoefficient(), the necessary uniforms, and nothing else. For example,
shadow_simple.glsl contains (simplified from my shader that implements CSMs):
in vec4 eye_position;
uniform sampler2DShadow shad_tex;
uniform mat4 shad_mat;
vec4 shad_coord = shad_mat * eye_position;
return texture(shad_tex, shad_coord).x;
And you could simply choose whether you wanted shading or not by linking a different
shadow_* shader. This solution may very well have more overhead, but I'd like to think that the GLSL compiler is good enough to optimize away any extra overhead compared to other ways of doing this. I haven't ran any tests on this, but it's is the way I like to do it.