Reference · GLSL ES 3.0 · WebGL 2

GLSL Shader Cheatsheet

Types, built-in functions, uniforms, precision qualifiers, and common shader patterns for WebGL 2 / Three.js shaders.

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:

GLSL
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
GLSLTypical preamble
#version 300 es
precision highp float;
precision highp int;
precision highp sampler2D;
Note: Fragment shaders have no default float precision in GLSL ES — always declare 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)
ShaderMaterial uniforms: Pass custom uniforms via 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.

GLSLVertex shader (ES 3.0)
#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.

GLSLFragment shader (ES 3.0)
#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

GLSL
// 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)

GLSL
// 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)

JavaScript (Three.js)
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

GLSL
// 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

GLSL
// 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)); }