GLSL's operators follow C's precedence. From highest to lowest:
| Levels | Operators, highest first |
|---|---|
| 1-3 | () [] . postfix ++ --, then prefix ++ -- + - ! ~ |
| 4-8 | * / %, + -, << >>, < > <= >=, == != |
| 9-14 | &, ^, |, &&, ^^, || |
| 15-17 | ?:, then = and compound assignments (right to left), then , |
GLSL ES 3.00 adds the integer operators %, <<, >>, &, |, ^ and ~, reserved in 1.00; ^^ is a logical exclusive or. Operands must share a type, though a scalar may combine with a vector. < takes scalars only; compare vectors with lessThan() and reduce with any() or all():
show('int i = 7; result = vec4(i / 2, -i / 2, i % 3, i << 2);');
show(`uint flags = 5u; // binary 101
result = vec4(flags & 4u, flags | 2u, flags ^ 1u, float(true ^^ true));`);
show(`bvec3 below = lessThan(vec3(1.0, 5.0, 3.0), vec3(2.0));
result = vec4(below, any(below));`);Output
3, -3, 1, 28 4, 7, 4, 0 1, 0, 0, 1
Integer division truncates toward zero, and % works only on integers; use mod() for floats.
<!doctype html>
<style>
body { margin: 0; font: 12px system-ui, sans-serif; background: #f7f4ee; color: #333; }
canvas { display: block; width: 100%; max-width: 600px; }
.names { display: flex; max-width: 600px; text-align: center; font-family: monospace; font-size: 11px; }
.names div { flex: 1; padding: 4px 2px; }
</style>
<canvas id="view" width="600" height="150"></canvas>
<div class="names">
<div>(x ^ y) & 16</div><div>(x / 12) % 3</div><div>inA ^^ inB</div><div>any(lessThan(p, t))</div>
</div>
<script>
const gl = document.getElementById('view').getContext('webgl2');
const vs = `#version 300 es
void main() { gl_Position = vec4(gl_VertexID == 1 ? 3.0 : -1.0, gl_VertexID == 2 ? 3.0 : -1.0, 0.0, 1.0); }`;
const fs = `#version 300 es
precision highp float; precision highp int;
uniform float uTime;
out vec4 outColor;
const vec3 ink = vec3(0.12, 0.37, 0.55), paper = vec3(0.93, 0.9, 0.84);
void main() {
int tile = int(gl_FragCoord.x) / 150; // integer division truncates
ivec2 ip = ivec2(gl_FragCoord.xy) % 150; // % works on integers only
vec2 p = vec2(ip) / 150.0; // 0..1 inside the tile
bool on;
if (tile == 0) {
int scroll = int(uTime * 20.0); // float -> int conversion must be explicit
on = (((ip.x + scroll) ^ ip.y) & 16) != 0; // bitwise xor and mask
} else if (tile == 1) {
int band = (ip.x + ip.y) / 12 % 3; // same precedence as C: / before %
outColor = vec4(band == 0 ? ink : band == 1 ? vec3(0.88, 0.6, 0.06) : paper, 1.0);
return;
} else if (tile == 2) {
float t = sin(uTime) * 0.15;
bool inA = distance(p, vec2(0.38 + t, 0.5)) < 0.28;
bool inB = distance(p, vec2(0.62 - t, 0.5)) < 0.28;
on = inA ^^ inB; // logical exclusive or
} else {
// < only takes scalars: compare vectors component-wise, then reduce with any().
vec2 threshold = vec2(0.5 + 0.3 * sin(uTime), 0.5 + 0.3 * cos(uTime));
bvec2 below = lessThan(p, threshold);
on = any(below) && !all(below);
}
outColor = vec4(on ? ink : paper, 1.0);
}`;
const program = gl.createProgram();
for (const [type, src] of [[gl.VERTEX_SHADER, vs], [gl.FRAGMENT_SHADER, fs]]) {
const s = gl.createShader(type);
gl.shaderSource(s, src); gl.compileShader(s);
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw new Error(gl.getShaderInfoLog(s));
gl.attachShader(program, s);
}
gl.linkProgram(program);
gl.useProgram(program);
gl.bindVertexArray(gl.createVertexArray());
const uTime = gl.getUniformLocation(program, 'uTime');
function frame(ms) {
gl.uniform1f(uTime, ms * 0.001);
gl.drawArrays(gl.TRIANGLES, 0, 3);
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
</script>