How Does GLSL Mod Work with Negative Numbers?
The built-in mod(x, y) function in the OpenGL Shading
Language (GLSL) computes the remainder of division using floored
division rather than truncated division. Unlike standard remainder
operators in languages like C or C++, GLSL defines
mod(x, y) as x - y * floor(x / y). This
distinction causes negative input coordinates or values to wrap
predictably into the positive range \([0,
y)\) whenever the divisor \(y\)
is positive, making it particularly useful for continuous texture
mapping, cyclic procedural patterns, and grid generation.
The Mathematical Definition in GLSL
According to the official GLSL specification, the floating-point
mod function computes:
\[\text{mod}(x, y) = x - y \cdot \text{floor}\left(\frac{x}{y}\right)\]
Because the equation relies on the floor function,
division rounds down toward negative infinity rather than truncating
toward zero. This mathematical approach is known as Knuthâs floored
division.
Step-by-Step Calculation Examples
To see how this formula behaves with negative values, consider the following concrete evaluations:
Case 1: Negative Dividend, Positive Divisor
(x = -0.25, y = 1.0)
- Compute the division: \(-0.25 / 1.0 = -0.25\)
- Apply the floor function: \(\text{floor}(-0.25) = -1.0\)
- Multiply by \(y\): \(1.0 \cdot (-1.0) = -1.0\)
- Subtract from \(x\): \(-0.25 - (-1.0) = 0.75\)
The result is \(0.75\), preserving a seamless repeating pattern across zero.
Case 2: Negative Integer-Like Float (x = -3.0,
y = 2.0)
- Compute the division: \(-3.0 / 2.0 = -1.5\)
- Apply the floor function: \(\text{floor}(-1.5) = -2.0\)
- Multiply by \(y\): \(2.0 \cdot (-2.0) = -4.0\)
- Subtract from \(x\): \(-3.0 - (-4.0) = 1.0\)
Case 3: Positive Dividend, Negative Divisor
(x = 3.0, y = -2.0)
- Compute the division: \(3.0 / -2.0 = -1.5\)
- Apply the floor function: \(\text{floor}(-1.5) = -2.0\)
- Multiply by \(y\): \(-2.0 \cdot (-2.0) = 4.0\)
- Subtract from \(x\): \(3.0 - 4.0 = -1.0\)
When \(y\) is negative, the result falls within the interval \((y, 0]\).
GLSL mod vs.
C-Style Remainder %
In many CPU programming languages such as C, C++, and Java, the
integer modulo operator % and the standard
fmod() function perform truncated division, where the
quotient is rounded toward zero:
\[\text{trunc\_mod}(x, y) = x - y \cdot \text{trunc}\left(\frac{x}{y}\right)\]
Under truncated division, -3.0 % 2.0 evaluates to
-1.0. In contrast, GLSL's mod(-3.0, 2.0)
evaluates to 1.0.
| Expression | C/C++ fmod(x, y) |
GLSL mod(x, y) |
|---|---|---|
x = 1.25, y = 1.0 |
0.25 |
0.25 |
x = -0.25, y = 1.0 |
-0.25 |
0.75 |
x = -1.75, y = 1.0 |
-0.75 |
0.25 |
x = -3.0, y = 2.0 |
-1.0 |
1.0 |
Practical Implications in Shader Programming
The floored division behavior is essential for graphics programming because UV coordinates and world positions frequently cross the coordinate origin into negative space.
- Periodic Patterns and Grids: Using
mod(uv, 1.0)ensures that fractional coordinates wrap smoothly from0.99down to0.00across the entire plane, preventing mirroring artifacts or discontinuities atx = 0andy = 0. - Integer Modulo in Modern GLSL: GLSL 1.30 and later
introduced the integer remainder operator
%forintanduinttypes. Unlike floating-pointmod(), the%operator on signed integers follows standard C99 truncation semantics toward zero. Shaders requiring consistent floored wrapping on integer types must either convert values tofloatand usemod()or manually adjust negative outputs.