How Does the GLSL mix Function Work on Vectors?
In OpenGL Shading Language (GLSL), the mix function
performs linear interpolation between two values based on a third
interpolation factor. When applied to vector data types,
mix operates component-wise according to the algebraic
formula \(x \times (1.0 - a) + y \times
a\). This article examines the core mechanics, signature
variants, component-level execution, extrapolation behavior, and
practical use cases of vector linear interpolation in GLSL shaders.
Mathematical Definition and Vector Semantics
At its core, GLSL's mix(x, y, a) evaluates a standard
linear interpolation (often abbreviated as lerp). For vector
types such as vec2, vec3, and
vec4 (as well as integer and double variants like
ivec or dvec), the operation is mapped across
every individual vector channel independently:
\[result_i = x_i \times (1.0 - a_i) + y_i \times a_i\]
Where \(i\) represents each corresponding vector component (\(x, y, z, w\) or \(r, g, b, a\)).
Function Signatures for Vectors
GLSL provides two primary function signatures when mixing vector types:
1. Vector Blending with a Scalar Factor
genType mix(genType x, genType y, float a);When the third parameter a is a single
float, that same scalar weight applies to all components of
vectors x and y. For example:
vec3 colorA = vec3(1.0, 0.0, 0.0); // Red
vec3 colorB = vec3(0.0, 0.0, 1.0); // Blue
vec3 blended = mix(colorA, colorB, 0.25);The resulting vector evaluates to:
- \(r = 1.0 \times (1.0 - 0.25) + 0.0 \times 0.25 = 0.75\)
- \(g = 0.0 \times (1.0 - 0.25) + 0.0 \times 0.25 = 0.0\)
- \(b = 0.0 \times (1.0 - 0.25) + 1.0 \times 0.25 = 0.25\)
blended = vec3(0.75, 0.0, 0.25)
2. Vector Blending with a Vector Factor
genType mix(genType x, genType y, genType a);When a is a vector of the same dimension as
x and y, each channel receives a unique
blending coefficient:
vec2 startPos = vec2(0.0, 10.0);
vec2 endPos = vec2(100.0, 50.0);
vec2 weight = vec2(0.5, 0.1);
vec2 result = mix(startPos, endPos, weight);
// result.x = mix(0.0, 100.0, 0.5) = 50.0
// result.y = mix(10.0, 50.0, 0.1) = 14.0
// result = vec2(50.0, 14.0)Extrapolation and Value Range
A common misconception is that mix automatically clamps
values within the range \([0.0, 1.0]\).
The standard mix function does not restrict the
interpolation factor \(a\):
- When \(a = 0.0\), the result is strictly \(x\).
- When \(a = 1.0\), the result is strictly \(y\).
- When \(0.0 < a < 1.0\), the result is a weighted blend between \(x\) and \(y\).
- When \(a < 0.0\) or \(a > 1.0\), the function computes linear extrapolation beyond the boundaries of \(x\) and \(y\).
To ensure bounded interpolation, developers frequently wrap the
weight factor with clamp(a, 0.0, 1.0) or
saturate(a).
Boolean Selection Variant
Starting in GLSL 1.30 and GLSL ES 3.00, mix includes an
overload that accepts a boolean vector (e.g., bvec2,
bvec3, bvec4) for the selector argument:
genType mix(genType x, genType y, genBType a);In this mode, mix behaves as a conditional component
selector rather than an arithmetic interpolator:
- If \(a_i\) is
false, the component evaluates to \(x_i\). - If \(a_i\) is
true, the component evaluates to \(y_i\).
This allows conditional vector masking on the GPU without dynamic branching penalties.