GLSL Shader Mastery for Three.js
Use this workflow to create, debug, and optimize GLSL shaders. Load the reference file that matches the task.
Quick Reference
Shader Pipeline Mental Model
Vertex Shader (per vertex) Fragment Shader (per pixel)
───────────────────────── ───────────────────────────
Input: attributes (position, Input: varyings (interpolated),
normal, uv) uniforms, samplers
uniforms (matrices)
Output: gl_Position (clip space) Output: gl_FragColor (RGBA)
varyings to fragment
Essential GLSL Functions
| Category |
Functions |
| Math |
mix, clamp, step, smoothstep, fract, mod, abs, sign, floor, ceil |
| Trig |
sin, cos, tan, atan, radians, degrees |
| Vector |
dot, cross, normalize, length, distance, reflect, refract |
| Texture |
texture2D (GLSL100) / texture (GLSL300) |
| Derivatives |
dFdx, dFdy, fwidth (fragment only) |
Three.js Built-in Uniforms (ShaderMaterial)
// Matrices (auto-provided)
uniform mat4 modelMatrix; // object → world
uniform mat4 viewMatrix; // world → camera
uniform mat4 projectionMatrix; // camera → clip
uniform mat4 modelViewMatrix; // object → camera
uniform mat3 normalMatrix; // for transforming normals
// Camera
uniform vec3 cameraPosition; // world space
// Attributes (auto-provided)
attribute vec3 position;
attribute vec3 normal;
attribute vec2 uv;
Decision Tree: Which Approach?
Need custom shader?
├─ Full control, unlit effect → ShaderMaterial
├─ Bare metal, no Three.js helpers → RawShaderMaterial
├─ Tweak built-in material (Standard, Phong) → onBeforeCompile
└─ Full-screen post-process → EffectComposer + ShaderPass
Core Patterns
1. Basic ShaderMaterial Template
const mat = new THREE.ShaderMaterial({
vertexShader: `
varying vec2 vUv;
varying vec3 vNormal;
void main() {
vUv = uv;
vNormal = normalize(normalMatrix * normal);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
precision highp float;
uniform float u_time;
uniform sampler2D u_texture;
varying vec2 vUv;
varying vec3 vNormal;
void main() {
vec4 tex = texture2D(u_texture, vUv);
gl_FragColor = vec4(tex.rgb, 1.0);
}
`,
uniforms: {
u_time: { value: 0 },
u_texture: { value: myTexture }
}
});
// Update each frame: mat.uniforms.u_time.value = clock.getElapsedTime();
2. Extending Built-in Materials (onBeforeCompile)
const mat = new THREE.MeshStandardMaterial({ map: texture });
mat.onBeforeCompile = (shader) => {
shader.uniforms.u_time = { value: 0 };
// Inject uniform declaration
shader.fragmentShader = shader.fragmentShader.replace(
'#include <common>',
`#include <common>
uniform float u_time;`
);
// Modify output (after dithering is a good hook point)
shader.fragmentShader = shader.fragmentShader.replace(
'#include <dithering_fragment>',
`#include <dithering_fragment>
gl_FragColor.rgb *= 0.5 + 0.5 * sin(u_time);`
);
mat.userData.shader = shader; // Store for updating
};
// Update: mat.userData.shader.uniforms.u_time.value = time;
3. Common Effect Formulas
// Fresnel (rim lighting)
float fresnel = pow(1.0 - max(dot(viewDir, normal), 0.0), 3.0);
// Dissolve threshold
float noise = texture2D(noiseTex, vUv).r;
if (noise > u_dissolve) discard;
// Toon shading (quantized lighting)
float NdotL = max(dot(normal, lightDir), 0.0);
float toon = floor(min(NdotL, 0.999) * 3.0) / 2.0; // 3 bands
// UV scrolling
vec2 scrollUV = vUv + u_time * vec2(0.1, 0.0);
// Pulsing glow
float pulse = 0.5 + 0.5 * sin(u_time * 3.0);
Debugging Checklist
Black screen?
- Check console for compile errors
- Output solid color:
gl_FragColor = vec4(1,0,0,1);
- Check
gl_Position is set correctly
- Verify uniforms are bound (especially textures)
- Check alpha isn't 0 with transparent material
NaN/artifacts?
- Look for:
0.0/0.0, sqrt(negative), normalize(vec3(0))
- Debug:
if(val != val) gl_FragColor = vec4(1,0,1,1); (WebGL1: NaN != NaN)
Visualize values:
gl_FragColor = vec4(vNormal * 0.5 + 0.5, 1.0); // normals
gl_FragColor = vec4(vec3(depth), 1.0); // depth
gl_FragColor = vec4(vec3(fract(u_time)), 1.0); // time flowing
Reference Files
| Need |
File |
| GLSL syntax, types, precision |
glsl-fundamentals.md |
| Three.js integration patterns |
threejs-integration.md |
| Noise, SDFs, patterns |
procedural-techniques.md |
| Effect recipes (dissolve, glow, etc.) |
visual-effects.md |
| Performance & debugging |
optimization-debugging.md |
| Copy-paste code snippets |
code-library.md |
| Lygia, glslify, Shadertoy conversion |
shader-ecosystem.md |
| WebGL2, MRT, GPGPU, instancing |
advanced-topics.md |
Porting Quick Reference
Shadertoy → Three.js
| Shadertoy |
Three.js |
iResolution |
uniform vec2 u_resolution |
iTime |
uniform float u_time |
iMouse |
uniform vec4 u_mouse |
fragCoord |
gl_FragCoord.xy or vUv * resolution |
fragColor |
gl_FragColor |
mainImage(out vec4, in vec2) |
void main() |
HLSL → GLSL
| HLSL |
GLSL |
float4 |
vec4 |
lerp(a,b,t) |
mix(a,b,t) |
saturate(x) |
clamp(x,0.0,1.0) |
frac(x) |
fract(x) |
mul(M,v) |
M * v (may need transpose) |
clip(x) |
if(x<0.0) discard; |
Performance Rules of Thumb
- Avoid per-pixel branches on varying data (use
mix/step instead)
- Minimize texture lookups in loops
- Use
mediump for colors/UVs, highp for positions
- Compute per-vertex when possible, interpolate to fragment
- Unroll small loops or use fixed iteration counts
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: glsl-shaders3description: This skill should be used when the user asks to write/debug GLSL shaders, create custom materials/effects, implement procedural noise/SDFs, port Shadertoy/Unity/HLSL shaders, optimize shader performance, or build post-processing effects in Three.js/WebGL. Use when this capability is needed.4---56# GLSL Shader Mastery for Three.js78Use this workflow to create, debug, and optimize GLSL shaders. Load the reference file that matches the task.910## Quick Reference1112### Shader Pipeline Mental Model1314```15Vertex Shader (per vertex) Fragment Shader (per pixel)16───────────────────────── ───────────────────────────17Input: attributes (position, Input: varyings (interpolated),18 normal, uv) uniforms, samplers19 uniforms (matrices)2021Output: gl_Position (clip space) Output: gl_FragColor (RGBA)22 varyings to fragment23```2425### Essential GLSL Functions2627| Category | Functions |28|----------|-----------|29| Math | `mix`, `clamp`, `step`, `smoothstep`, `fract`, `mod`, `abs`, `sign`, `floor`, `ceil` |30| Trig | `sin`, `cos`, `tan`, `atan`, `radians`, `degrees` |31| Vector | `dot`, `cross`, `normalize`, `length`, `distance`, `reflect`, `refract` |32| Texture | `texture2D` (GLSL100) / `texture` (GLSL300) |33| Derivatives | `dFdx`, `dFdy`, `fwidth` (fragment only) |3435### Three.js Built-in Uniforms (ShaderMaterial)3637```glsl38// Matrices (auto-provided)39uniform mat4 modelMatrix; // object → world40uniform mat4 viewMatrix; // world → camera41uniform mat4 projectionMatrix; // camera → clip42uniform mat4 modelViewMatrix; // object → camera43uniform mat3 normalMatrix; // for transforming normals4445// Camera46uniform vec3 cameraPosition; // world space4748// Attributes (auto-provided)49attribute vec3 position;50attribute vec3 normal;51attribute vec2 uv;52```5354## Decision Tree: Which Approach?5556```57Need custom shader?58├─ Full control, unlit effect → ShaderMaterial59├─ Bare metal, no Three.js helpers → RawShaderMaterial60├─ Tweak built-in material (Standard, Phong) → onBeforeCompile61└─ Full-screen post-process → EffectComposer + ShaderPass62```6364## Core Patterns6566### 1. Basic ShaderMaterial Template6768```javascript69const mat = new THREE.ShaderMaterial({70 vertexShader: `71 varying vec2 vUv;72 varying vec3 vNormal;73 void main() {74 vUv = uv;75 vNormal = normalize(normalMatrix * normal);76 gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);77 }78 `,79 fragmentShader: `80 precision highp float;81 uniform float u_time;82 uniform sampler2D u_texture;83 varying vec2 vUv;84 varying vec3 vNormal;85 void main() {86 vec4 tex = texture2D(u_texture, vUv);87 gl_FragColor = vec4(tex.rgb, 1.0);88 }89 `,90 uniforms: {91 u_time: { value: 0 },92 u_texture: { value: myTexture }93 }94});95// Update each frame: mat.uniforms.u_time.value = clock.getElapsedTime();96```9798### 2. Extending Built-in Materials (onBeforeCompile)99100```javascript101const mat = new THREE.MeshStandardMaterial({ map: texture });102mat.onBeforeCompile = (shader) => {103 shader.uniforms.u_time = { value: 0 };104105 // Inject uniform declaration106 shader.fragmentShader = shader.fragmentShader.replace(107 '#include <common>',108 `#include <common>109 uniform float u_time;`110 );111112 // Modify output (after dithering is a good hook point)113 shader.fragmentShader = shader.fragmentShader.replace(114 '#include <dithering_fragment>',115 `#include <dithering_fragment>116 gl_FragColor.rgb *= 0.5 + 0.5 * sin(u_time);`117 );118119 mat.userData.shader = shader; // Store for updating120};121// Update: mat.userData.shader.uniforms.u_time.value = time;122```123124### 3. Common Effect Formulas125126```glsl127// Fresnel (rim lighting)128float fresnel = pow(1.0 - max(dot(viewDir, normal), 0.0), 3.0);129130// Dissolve threshold131float noise = texture2D(noiseTex, vUv).r;132if (noise > u_dissolve) discard;133134// Toon shading (quantized lighting)135float NdotL = max(dot(normal, lightDir), 0.0);136float toon = floor(min(NdotL, 0.999) * 3.0) / 2.0; // 3 bands137138// UV scrolling139vec2 scrollUV = vUv + u_time * vec2(0.1, 0.0);140141// Pulsing glow142float pulse = 0.5 + 0.5 * sin(u_time * 3.0);143```144145## Debugging Checklist146147**Black screen?**1481. Check console for compile errors1492. Output solid color: `gl_FragColor = vec4(1,0,0,1);`1503. Check `gl_Position` is set correctly1514. Verify uniforms are bound (especially textures)1525. Check alpha isn't 0 with transparent material153154**NaN/artifacts?**155- Look for: `0.0/0.0`, `sqrt(negative)`, `normalize(vec3(0))`156- Debug: `if(val != val) gl_FragColor = vec4(1,0,1,1);` (WebGL1: NaN != NaN)157158**Visualize values:**159```glsl160gl_FragColor = vec4(vNormal * 0.5 + 0.5, 1.0); // normals161gl_FragColor = vec4(vec3(depth), 1.0); // depth162gl_FragColor = vec4(vec3(fract(u_time)), 1.0); // time flowing163```164165## Reference Files166167| Need | File |168|------|------|169| GLSL syntax, types, precision | [glsl-fundamentals.md](references/glsl-fundamentals.md) |170| Three.js integration patterns | [threejs-integration.md](references/threejs-integration.md) |171| Noise, SDFs, patterns | [procedural-techniques.md](references/procedural-techniques.md) |172| Effect recipes (dissolve, glow, etc.) | [visual-effects.md](references/visual-effects.md) |173| Performance & debugging | [optimization-debugging.md](references/optimization-debugging.md) |174| Copy-paste code snippets | [code-library.md](references/code-library.md) |175| Lygia, glslify, Shadertoy conversion | [shader-ecosystem.md](references/shader-ecosystem.md) |176| WebGL2, MRT, GPGPU, instancing | [advanced-topics.md](references/advanced-topics.md) |177178## Porting Quick Reference179180### Shadertoy → Three.js181182| Shadertoy | Three.js |183|-----------|----------|184| `iResolution` | `uniform vec2 u_resolution` |185| `iTime` | `uniform float u_time` |186| `iMouse` | `uniform vec4 u_mouse` |187| `fragCoord` | `gl_FragCoord.xy` or `vUv * resolution` |188| `fragColor` | `gl_FragColor` |189| `mainImage(out vec4, in vec2)` | `void main()` |190191### HLSL → GLSL192193| HLSL | GLSL |194|------|------|195| `float4` | `vec4` |196| `lerp(a,b,t)` | `mix(a,b,t)` |197| `saturate(x)` | `clamp(x,0.0,1.0)` |198| `frac(x)` | `fract(x)` |199| `mul(M,v)` | `M * v` (may need transpose) |200| `clip(x)` | `if(x<0.0) discard;` |201202## Performance Rules of Thumb2032041. **Avoid per-pixel branches** on varying data (use `mix`/`step` instead)2052. **Minimize texture lookups** in loops2063. **Use `mediump`** for colors/UVs, `highp` for positions2074. **Compute per-vertex** when possible, interpolate to fragment2085. **Unroll small loops** or use fixed iteration counts209210---211> Converted and distributed by [TomeVault](https://tomevault.io/claim/treygoff24) — claim your Tome and manage your conversions.212<!-- tomevault:4.0:skill_md:2026-04-13 -->