--- name: shader-programming-glsl description: Expert guide for writing efficient GLSL shaders (Vertex/Fragment) for web and game engines, covering syntax, uniforms, and common effects. risk: safe source: community --- # Shader Programming GLSL ## Overview A comprehensive guide to writing GPU shaders using GLSL (OpenGL Shading Language). Learn syntax, uniforms, varying variables, and key mathematical concepts like swizzling and vector operations for visual effects. ## When to Use This Skill - Use when creating custom visual effects in WebGL, Three.js, or game engines. - Use when optimizing graphics rendering performance. - Use when implementing post-processing effects (blur, bloom, color correction). - Use when procedurally generating textures or geometry on the GPU. ## Step-by-Step Guide ### 1. Structure: Vertex vs. Fragment Understand the pipeline: - **Vertex Shader**: Transforms 3D coordinates to 2D screen space (`gl_Position`). - **Fragment Shader**: Colors individual pixels (`gl_FragColor`). ```glsl // Vertex Shader (basic) attribute vec3 position; uniform mat4 modelViewMatrix; uniform mat4 projectionMatrix; void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } ``` ```glsl // Fragment Shader (basic) uniform vec3 color; void main() { gl_FragColor = vec4(color, 1.0); } ``` ### 2. Uniforms and Varyings - `uniform`: Data constant for all vertices/fragments (passed from CPU). - `varying`: Data interpolated from vertex to fragment shader. ```glsl // Passing UV coordinates varying vec2 vUv; // In Vertex Shader void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } // In Fragment Shader void main() { // Gradient based on UV gl_FragColor = vec4(vUv.x, vUv.y, 1.0, 1.0); } ``` ### 3. Swizzling & Vector Math Access vector components freely: `vec4 color = vec4(1.0, 0.5, 0.0, 1.0);` - `color.rgb` -> `vec3(1.0, 0.5, 0.0)` - `color.zyx` -> `vec3(0.0, 0.5, 1.0)` (reordering) ## Examples ### Example 1: Simple Raymarching (SDF Sphere) ```glsl float sdSphere(vec3 p, float s) { return length(p) - s; } void mainImage(out vec4 fragColor, in vec2 fragCoord) { vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y; vec3 ro = vec3(0.0, 0.0, -3.0); // Ray Origin vec3 rd = normalize(vec3(uv, 1.0)); // Ray Direction float t = 0.0; for(int i = 0; i < 64; i++) { vec3 p = ro + rd * t; float d = sdSphere(p, 1.0); // Sphere radius 1.0 if(d < 0.001) break; t += d; } vec3 col = vec3(0.0); if(t < 10.0) { vec3 p = ro + rd * t; vec3 normal = normalize(p); col = normal * 0.5 + 0.5; // Color by normal } fragColor = vec4(col, 1.0); } ``` ## Best Practices - ✅ **Do:** Use `mix()` for linear interpolation instead of manual math. - ✅ **Do:** Use `step()` and `smoothstep()` for thresholding and soft edges (avoid `if` branches). - ✅ **Do:** Pack data into vectors (`vec4`) to minimize memory access. - ❌ **Don't:** Use heavy branching (`if-else`) inside loops if possible; it hurts GPU parallelism. - ❌ **Don't:** Calculate constant values inside the shader; pre-calculate them on the CPU (uniforms). ## Troubleshooting **Problem:** Shader compiles but screen is black. **Solution:** Check if `gl_Position.w` is correct (usually 1.0). Check if uniforms are actually being set from the host application. Verify UV coordinates are within [0, 1].