That’s very subjective I have another solution for you then: pre-compile all possible versions of your shader and then swap them at runtime depending on which one you need.
I am curious though, why do you not find using uniforms for this use case beautiful or intuitive?
uniform float uModeWeights[3]; // [1,0,0] for mode 0, [0,1,0] for mode 1, etc.
vec4 blend(const in vec4 x, const in vec4 y, const in float opacity) {
return uModeWeights[0] * mix(x, y, opacity)
+ uModeWeights[1] * mix(x, y, min(y.a, opacity))
+ uModeWeights[2] * mix(x, x + y - min(x * y, 1.0), opacity);
}
Or something like:
vec4 blend(const in vec4 x, const in vec4 y, const in float opacity) {
// Pre-calculate all possible blend results
vec4 normalBlend = mix(x, y, opacity);
vec4 screenBlend = mix(x, x + y - min(x * y, 1.0), opacity);
vec4 alphaBlend = mix(x, y, min(y.a, opacity));
// Calculate weights for each mode (0 or 1)
float mode1Weight = abs(sign(float(uMode) - 1.5)) - 0.5; // ~1.0 when uMode == 1
float mode2Weight = abs(sign(float(uMode) - 2.5)) - 0.5; // ~1.0 when uMode == 2
float defaultWeight = 1.0 - mode1Weight - mode2Weight; // ~1.0 otherwise
// Combine results using weights
return mode1Weight * alphaBlend
+ mode2Weight * screenBlend
+ defaultWeight * normalBlend;
}