Types & Constructors
| Type | Description | Constructor example |
|---|---|---|
bool |
Boolean | bool b = true; |
int |
32-bit signed integer | int n = 42; |
uint ES 3.0 |
32-bit unsigned integer | uint u = 0xFFu; |
float |
32-bit float | float f = 1.0; |
vec2 / vec3 / vec4 |
Float vector (2/3/4 components) | vec3 v = vec3(1.0, 0.5, 0.0); |
ivec2 / ivec3 / ivec4 |
Integer vector | ivec2 uv = ivec2(x, y); |
uvec2 / uvec3 / uvec4
ES 3.0
|
Unsigned integer vector | uvec4 c = uvec4(255u); |
mat2 / mat3 / mat4 |
Float matrix (column-major) | mat4 m = mat4(1.0); // identity |
sampler2D |
2D texture sampler | uniform sampler2D uTex; |
sampler3D ES 3.0
|
3D texture sampler | uniform sampler3D uVol; |
samplerCube |
Cube-map sampler | uniform samplerCube uEnv; |
Swizzling
Components can be accessed with .xyzw,
.rgba, or .stpq — any order, any count:
vec4 col = vec4(1.0, 0.5, 0.2, 1.0);
vec3 rgb = col.rgb; // (1.0, 0.5, 0.2)
vec2 br = col.bra.xy; // (0.2, 1.0)
col.yz = vec2(0.0); // set g and b to 0
Precision Qualifiers
| Qualifier | Float range (approx) | When to use |
|---|---|---|
lowp |
−2 … 2, ~8-bit | Colours, normcol, highly-optimised mobile |
mediump |
−2¹⁴ … 2¹⁴, ~10-bit | Most texture UVs, simple lighting |
highp |
−2⁶² … 2⁶², ~24-bit | World coords, time, depth, simulation data |
#version 300 es
precision highp float;
precision highp int;
precision highp sampler2D;
precision mediump float; (or highp) at the
top.
Built-in Functions
Trigonometric
| Function | Returns |
|---|---|
sin(x), cos(x),
tan(x)
|
Component-wise trig |
asin(x), acos(x),
atan(y,x)
|
Inverse trig; atan(y,x) returns angle in (−π,
π]
|
radians(deg), degrees(rad) |
Unit conversion |
Math & Interpolation
| Function | Returns |
|---|---|
abs(x) |
Absolute value component-wise |
floor(x), ceil(x),
round(x)
|
Floor / ceil / round |
fract(x) |
x − floor(x) — fractional part
|
mod(x, y) |
Float modulo; x − y·floor(x/y)
|
min(x,y), max(x,y) |
Component-wise min/max |
clamp(x, lo, hi) |
Clamp x to [lo, hi] |
mix(a, b, t) |
Linear interpolation: a·(1−t) + b·t
|
step(edge, x) |
0 if x < edge, else 1 |
smoothstep(lo, hi, x) |
Hermite cubic: 3t²−2t³ in [lo,hi] |
pow(x, y), exp(x),
log(x)
|
Exponential |
sqrt(x), inversesqrt(x) |
Square root; 1/√x fast reciprocal |
sign(x) |
−1, 0 or +1 |
Vector & Geometry
| Function | Returns |
|---|---|
length(v) |
Euclidean length √(x²+y²+…) |
distance(a, b) |
length(a−b) |
dot(a, b) |
Dot product scalar |
cross(a, b) |
Cross product of vec3 |
normalize(v) |
Unit vector: v / length(v) |
reflect(I, N) |
Reflection of incident I about normal N |
refract(I, N, eta) |
Snell's law refraction; eta = n₁/n₂ |
faceforward(N, I, Nref) |
Returns N oriented to face away from I |
Texture Sampling
| Function | Returns |
|---|---|
texture(sampler, uv) |
GLSL ES 3.0 unified sampler call (replaces texture2D) |
textureLod(sampler, uv, lod) |
Sample at explicit mip level |
textureOffset(s, uv, off) |
Sample with constant texel offset |
texelFetch(s, iuv, lod)
ES 3.0
|
Fetch exact texel by integer coords — no filtering |
textureSize(s, lod)
ES 3.0
|
Returns ivec2 dimensions of texture at mip
|
Common Uniforms (Three.js)
| Uniform name | Type | Value |
|---|---|---|
uTime |
float |
Elapsed seconds since start — pass
clock.getElapsedTime()
|
uResolution |
vec2 |
Viewport size in physical pixels:
new THREE.Vector2(w * dpr, h * dpr)
|
uMouse |
vec2 |
Normalised mouse position (0–1)² |
projectionMatrix |
mat4 |
Auto-injected by Three.js |
modelViewMatrix |
mat4 |
Auto-injected by Three.js |
normalMatrix |
mat3 |
Auto-injected by Three.js (inverse transpose of MV) |
new THREE.ShaderMaterial({ uniforms: { uTime: { value: 0 } }
})
and update them in the animation loop with
material.uniforms.uTime.value = t.
Vertex Shader
Receives per-vertex attributes, transforms position to clip space, computes varyings for the fragment stage.
#version 300 es
precision highp float;
// Built-in Three.js attributes
in vec3 position; // model-space position
in vec3 normal; // model-space normal
in vec2 uv; // UV coordinate
// Built-in Three.js uniforms (auto-provided)
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
uniform mat3 normalMatrix;
// Custom uniforms
uniform float uTime;
// Varyings — sent to fragment shader
out vec2 vUv;
out vec3 vNormal;
out vec3 vWorldPos;
void main() {
vUv = uv;
vNormal = normalize(normalMatrix * normal);
vec3 pos = position;
// Animate: displace y by sine wave
pos.y += sin(pos.x * 3.0 + uTime) * 0.1;
vec4 worldPos = modelViewMatrix * vec4(pos, 1.0);
vWorldPos = worldPos.xyz;
gl_Position = projectionMatrix * worldPos;
}
Fragment Shader
Receives interpolated varyings, samples textures, computes the pixel colour output.
#version 300 es
precision highp float;
uniform sampler2D uTexture;
uniform float uTime;
in vec2 vUv;
in vec3 vNormal;
in vec3 vWorldPos;
out vec4 fragColor; // replaces gl_FragColor in ES 3.0
void main() {
// Base colour from texture
vec4 albedo = texture(uTexture, vUv);
// Simple diffuse lighting
vec3 lightDir = normalize(vec3(1.0, 2.0, 1.5));
float diff = max(dot(vNormal, lightDir), 0.0);
vec3 lit = albedo.rgb * (0.2 + 0.8 * diff);
// Fresnel rim glow
vec3 viewDir = normalize(-vWorldPos);
float fresnel = pow(1.0 - dot(viewDir, vNormal), 3.0);
vec3 rim = mix(vec3(0.0), vec3(0.4, 0.6, 1.0), fresnel);
fragColor = vec4(lit + rim, albedo.a);
}
Shader Patterns
Hash / Noise
// Simple 2D hash → pseudo-random float in [0,1]
float hash21(vec2 p) {
p = fract(p * vec2(127.1, 311.7));
p += dot(p, p + 19.19);
return fract(p.x * p.y);
}
// Value noise: smooth, 1 texture-free call
float noise(vec2 p) {
vec2 i = floor(p), f = fract(p);
vec2 u = f * f * (3.0 - 2.0 * f); // smoothstep
return mix(
mix(hash21(i), hash21(i + vec2(1,0)), u.x),
mix(hash21(i + vec2(0,1)), hash21(i + vec2(1,1)), u.x), u.y
);
}
Signed Distance Field (SDF)
// SDF primitives (2D)
float sdCircle(vec2 p, float r) { return length(p) - r; }
float sdBox(vec2 p, vec2 b) { vec2 d = abs(p)-b; return length(max(d,0.))+min(max(d.x,d.y),0.); }
float sdSegment(vec2 p,vec2 a,vec2 b) { vec2 pa = p-a, ba = b-a; float h = clamp(dot(pa,ba)/dot(ba,ba),0.,1.); return length(pa-ba*h); }
// Smooth boolean union (k controls blend radius)
float smin(float a, float b, float k) {
float h = clamp(0.5 + 0.5*(b-a)/k, 0., 1.);
return mix(b, a, h) - k*h*(1.-h);
}
Ping-pong (simulation texture swap)
const opts = { width: N, height: N, type: THREE.FloatType };
const rtA = new THREE.WebGLRenderTarget(N, N, opts);
const rtB = new THREE.WebGLRenderTarget(N, N, opts);
let ping = rtA, pong = rtB;
// Each frame: render into pong, reading from ping
simMaterial.uniforms.uState.value = ping.texture;
renderer.setRenderTarget(pong);
renderer.render(simScene, simCamera);
renderer.setRenderTarget(null);
[ping, pong] = [pong, ping]; // swap
Float ↔ RGBA packing
// Pack a float [0,1] into 4 bytes (RGBA8) — useful for
// storing simulation state in 8-bit textures without OES_texture_float
vec4 packFloat(float v) {
vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * v;
enc = fract(enc);
enc -= enc.yzww * vec4(1.0/255.0, 1.0/255.0, 1.0/255.0, 0.0);
return enc;
}
// Unpack RGBA8 back to float [0,1]
float unpackFloat(vec4 rgba) {
return dot(rgba, vec4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/16581375.0));
}
Common GLSL utility snippets
// Remap [a,b] → [c,d]
float remap(float x, float a, float b, float c, float d) {
return c + (d - c) * ((x - a) / (b - a));
}
// Rotation matrix 2D
mat2 rot2(float a) { float c = cos(a), s = sin(a); return mat2(c,-s,s,c); }
// Luminance (perceived brightness)
float luma(vec3 rgb) { return dot(rgb, vec3(0.2126, 0.7152, 0.0722)); }
// sRGB ↔ linear
vec3 toLinear(vec3 c) { return pow(c, vec3(2.2)); }
vec3 toSRGB(vec3 c) { return pow(c, vec3(1.0/2.2)); }