How Does the GLSL sign Function Work?
The sign function in OpenGL Shading Language (GLSL) is a
built-in mathematical function designed to extract the sign of a scalar
or vector input. It evaluates numerical values and maps them strictly to
-1.0, 0.0, or 1.0 (or their
integer/double equivalents), providing a standardized way to determine
polarity and direction without conditional branching.
Core Evaluation Rules
The sign function evaluates input values based on three
distinct mathematical conditions:
- Positive Inputs (\(x >
0\)): Any value strictly greater than zero returns
1.0. - Zero Inputs (\(x =
0\)): Any input equal to zero returns
0.0. - Negative Inputs (\(x <
0\)): Any value strictly less than zero returns
-1.0.
Mathematically, the function is defined as:
\[\text{sign}(x) = \begin{cases} 1.0 & \text{if } x > 0.0 \\ 0.0 & \text{if } x = 0.0 \\ -1.0 & \text{if } x < 0.0 \end{cases}\]
Vector and Component-Wise Execution
In GLSL, sign is overloaded for scalar and vector types,
including float, vec2, vec3,
vec4, as well as integer (int,
ivec2, etc.) and double-precision types where
supported.
When passed a vector, the function operates component-wise. Each element in the vector is evaluated independently according to the standard rules:
vec3 values = vec3(-4.5, 0.0, 12.8);
vec3 result = sign(values);
// result contains vec3(-1.0, 0.0, 1.0)Floating-Point and Edge Cases
Because GLSL adheres to IEEE 754 floating-point standards where supported by hardware, specific edge cases behave predictably:
- Signed Zeros (
+0.0and-0.0): In standard IEEE 754 arithmetic,-0.0 == +0.0evaluates to true. Therefore,sign(-0.0)typically returns0.0, rather than-1.0or-0.0. - Not-a-Number (NaN): Passing
NaNintosign()yields undefined orNaNbehavior depending on the underlying GPU architecture and driver implementation. - Infinities (
+infinityand-infinity): Positive infinity evaluates to1.0, while negative infinity evaluates to-1.0.
Common Shader Applications
Because GPUs execute SIMD (Single Instruction, Multiple Data)
operations most efficiently when avoiding dynamic divergence,
sign() is frequently utilized to write branchless shader
code:
- Normal Flipping and Alignment: Determining whether geometry faces toward or away from a reference plane.
- Branchless Direction Logic: Multiplying motion
vectors or UV offsets by the sign of a control variable instead of using
if/elseblocks. - Signed Distance Fields (SDFs): Differentiating between the interior (\(< 0\)) and exterior (\(> 0\)) regions of procedural shapes.