Shader Fundamentals
GLSL (OpenGL Shading Language) runs on the GPU. Vertex shaders transform geometry; fragment shaders color pixels.
Quick Start // Vertex Shader uniform mat4 projectionMatrix; uniform mat4 modelViewMatrix;
attribute vec3 position; attribute vec2 uv;
varying vec2 vUv;
void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }
// Fragment Shader uniform float uTime; varying vec2 vUv;
void main() { vec3 color = vec3(vUv, sin(uTime) * 0.5 + 0.5); gl_FragColor = vec4(color, 1.0); }
Graphics Pipeline Vertex Data → [Vertex Shader] → Primitives → Rasterization → [Fragment Shader] → Pixels ↑ ↑ ↑ attributes transforms per-pixel color
Stage Runs Per Purpose Vertex Shader Vertex Transform positions, pass data to fragment Fragment Shader Pixel Calculate final color Data Types Scalars bool b = true; int i = 42; float f = 3.14;
Vectors vec2 v2 = vec2(1.0, 2.0); vec3 v3 = vec3(1.0, 2.0, 3.0); vec4 v4 = vec4(1.0, 2.0, 3.0, 4.0);
// Integer vectors ivec2 iv2 = ivec2(1, 2); ivec3 iv3 = ivec3(1, 2, 3);
// Boolean vectors bvec2 bv2 = bvec2(true, false);
Swizzling vec4 color = vec4(1.0, 0.5, 0.2, 1.0);
vec3 rgb = color.rgb; // (1.0, 0.5, 0.2) vec2 rg = color.rg; // (1.0, 0.5) float r = color.r; // 1.0
// Reorder vec3 bgr = color.bgr; // (0.2, 0.5, 1.0)
// Duplicate vec3 rrr = color.rrr; // (1.0, 1.0, 1.0)
// Position aliases (xyzw = rgba = stpq) vec3 pos = v4.xyz; vec2 uv = v4.st;
Matrices mat2 m2; // 2x2 mat3 m3; // 3x3 mat4 m4; // 4x4
// Access columns vec4 col0 = m4[0];
// Access element float val = m4[1][2]; // column 1, row 2
Samplers uniform sampler2D uTexture; // 2D texture uniform samplerCube uCubemap; // Cube map
// Sample texture vec4 texColor = texture2D(uTexture, vUv); vec4 cubeColor = textureCube(uCubemap, direction);
Variable Qualifiers Uniforms (CPU → GPU, constant per draw) // Set from JavaScript, same for all vertices/fragments uniform float uTime; uniform vec3 uColor; uniform mat4 uModelMatrix; uniform sampler2D uTexture;
Attributes (Per-vertex data) // Only in vertex shader attribute vec3 position; // Built-in: vertex position attribute vec3 normal; // Built-in: vertex normal attribute vec2 uv; // Built-in: texture coordinates attribute vec3 color; // Built-in: vertex color
// Custom attributes attribute float aScale; attribute vec3 aOffset;
Varyings (Vertex → Fragment, interpolated) // Vertex shader: write varying vec2 vUv; varying vec3 vNormal;
void main() { vUv = uv; vNormal = normal; }
// Fragment shader: read (interpolated across triangle) varying vec2 vUv; varying vec3 vNormal;
void main() { // vUv is interpolated between triangle vertices }
Built-in Variables Vertex Shader // Output (must write) vec4 gl_Position; // Clip-space position
// Output (optional) float gl_PointSize; // Point sprite size (for gl.POINTS)
Fragment Shader // Input vec4 gl_FragCoord; // Window-space position (pixel coordinates) bool gl_FrontFacing; // True if front face vec2 gl_PointCoord; // Point sprite coordinates [0,1]
// Output vec4 gl_FragColor; // Final pixel color
Coordinate Spaces Local/Object Space ↓ modelMatrix World Space ↓ viewMatrix View/Eye/Camera Space ↓ projectionMatrix Clip Space (-1 to 1) ↓ perspective divide NDC (Normalized Device Coordinates) ↓ viewport transform Screen Space (pixels)
Common Matrices (Three.js/R3F) uniform mat4 modelMatrix; // Local → World uniform mat4 viewMatrix; // World → View uniform mat4 projectionMatrix; // View → Clip uniform mat4 modelViewMatrix; // Local → View (modelMatrix * viewMatrix) uniform mat3 normalMatrix; // For transforming normals
uniform vec3 cameraPosition; // Camera world position
Standard Vertex Transform void main() { // Full transform chain vec4 worldPosition = modelMatrix * vec4(position, 1.0); vec4 viewPosition = viewMatrix * worldPosition; vec4 clipPosition = projectionMatrix * viewPosition; gl_Position = clipPosition;
// Or combined (more efficient) gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }
Built-in Functions Math // Trigonometry sin(x), cos(x), tan(x) asin(x), acos(x), atan(x) atan(y, x) // atan2
// Exponential pow(x, y) // x^y exp(x) // e^x log(x) // ln(x) sqrt(x) // √x inversesqrt(x) // 1/√x
// Common abs(x) sign(x) // -1, 0, or 1 floor(x) ceil(x) fract(x) // x - floor(x) mod(x, y) // x % y (floating point) min(x, y) max(x, y) clamp(x, min, max) mix(a, b, t) // Linear interpolation: a(1-t) + bt step(edge, x) // 0 if x < edge, else 1 smoothstep(e0, e1, x) // Smooth Hermite interpolation
Vector length(v) // Vector magnitude distance(a, b) // length(a - b) dot(a, b) // Dot product cross(a, b) // Cross product (vec3 only) normalize(v) // Unit vector reflect(I, N) // Reflection vector refract(I, N, eta) // Refraction vector faceforward(N, I, Nref) // Flip normal if needed
Common Patterns UV Coordinates // vUv ranges from (0,0) at bottom-left to (1,1) at top-right varying vec2 vUv;
void main() { // Center UVs: -0.5 to 0.5 vec2 centered = vUv - 0.5;
// Aspect-corrected (assuming you pass uResolution) vec2 uv = vUv; uv.x *= uResolution.x / uResolution.y;
// Tiling vec2 tiled = fract(vUv * 4.0); // 4x4 tiles
// Polar coordinates float angle = atan(centered.y, centered.x); float radius = length(centered); }
Color Operations // Grayscale (perceptual weights) float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
// Contrast color = (color - 0.5) * contrast + 0.5;
// Brightness color += brightness;
// Saturation float gray = dot(color, vec3(0.299, 0.587, 0.114)); color = mix(vec3(gray), color, saturation);
// Gamma correction color = pow(color, vec3(1.0 / 2.2)); // Linear to sRGB color = pow(color, vec3(2.2)); // sRGB to linear
Smooth Transitions // Hard edge float mask = step(0.5, value);
// Soft edge float mask = smoothstep(0.4, 0.6, value);
// Anti-aliased edge (screen-space) float mask = smoothstep(-fwidth(value), fwidth(value), value);
Debugging Visualize Values // Show UVs as color gl_FragColor = vec4(vUv, 0.0, 1.0);
// Show normals gl_FragColor = vec4(vNormal * 0.5 + 0.5, 1.0);
// Show depth float depth = gl_FragCoord.z; gl_FragColor = vec4(vec3(depth), 1.0);
// Show value range (red=negative, green=positive) gl_FragColor = vec4(max(0.0, value), max(0.0, -value), 0.0, 1.0);
Common Errors Issue Likely Cause Black screen gl_Position not set, or NaN values Uniform not updating Wrong name or type mismatch Texture black Texture not loaded, wrong UV Flickering Z-fighting, precision issues Faceted look Normals not interpolated Precision // Declare precision (required in fragment shader for WebGL 1) precision highp float; precision mediump float; precision lowp float;
Precision Range Use Case highp ~10^38 Positions, matrices mediump ~10^14 UVs, colors lowp ~2 Simple flags File Structure shader-fundamentals/ ├── SKILL.md ├── references/ │ ├── glsl-types.md # Complete type reference │ ├── builtin-functions.md # All built-in functions │ └── coordinate-spaces.md # Transform pipeline └── scripts/ └── templates/ ├── basic.glsl # Starter template └── fullscreen.glsl # Fullscreen quad shader
Reference references/glsl-types.md — Complete data type reference references/builtin-functions.md — All GLSL built-in functions references/coordinate-spaces.md — Transform pipeline deep-dive