How to learn shader programming: a step-by-step roadmap
Shader programming has a reputation for being hard, mostly because it is usually taught backwards: someone shows you a wall of math and asks you to be impressed. A better way is to learn it as a short chain of ideas, where every idea is something you can see. Here is the order that works.
Step 1: understand that every pixel runs alone
A shader is one small function, and the graphics card runs it for every pixel at the same time. Nothing else makes sense until this does. Start with Every pixel, one function and Colors are four numbers.
Step 2: learn the pixel's position
The input to your function is where the pixel is. Turn it into a gradient (UV: where am I?), use both axes at once (Two axes) and learn the vector shortcuts (Vectors & swizzling).
Step 3: make shapes
Shapes are the heart of 2D shader art. You need three tools: step() and lerp() to switch and blend, smoothstep() for soft edges, and the idea of a distance field: for every pixel, how far is it from the shape's edge? Almost everything else is built from that.
Step 4: add time
Add a variable that grows every frame and use sin() to make anything pulse, slide or breathe. Time: make it move shows how.
Step 5: repeat, move, scale and rotate
This is where most learners get stuck, so go slowly. The key trick is that you never move a shape: you move the pixel's position before you ask the question. Work through Repeating, Moving a shape, Scaling and the rotation lessons, ending with Rotation & matrices.
Step 6: randomness and noise
Shaders have no random(), but a few lines of math give you something that looks random. Smooth it into noise and stack layers into fractal noise, and you can draw clouds, terrain, fire and marble. See Randomness without random() through Fractal noise (fBm).
Step 7: go 3D with ray marching
You do not need a 3D model to draw 3D. For each pixel, shoot a ray and walk along it until it hits something described by a distance function. The five small steps from every pixel has a ray to putting it all together build it up one idea at a time.
Step 8: build real effects
Now combine everything: an ocean with foam and sun glitter, clouds you can fly through, a smoke grenade that bullets punch holes in, or snow with footprints.
How to practise
- Recreate pictures. Looking at a target and working out how to draw it teaches more than any tutorial. That is what the challenges are for.
- Do something small every day. The daily challenge is a new picture each day, the same for everyone.
- Change one number at a time and watch what moves. If you cannot predict the result, you have found something to learn.
- Print values. Write
print("d = ", d)in a ShaderBattle shader and hover a pixel to see what your function returned there. Debugging by looking is the single biggest speed-up.
Common mistakes
- Writing
2instead of2.0in math (some compilers refuse; most only warn). - Forgetting that the screen is wider than it is tall, so circles come out as ovals. Multiply x by the aspect ratio.
- Using
fmodon negative numbers and getting different results in different languages. - Thinking pixels can talk to each other. They cannot; design around that.
Keep reading
- What is a fragment shader? A plain explanation with code
- HLSL vs GLSL vs WGSL vs Metal: shader languages compared
- What is ray marching? Distance fields explained simply
- Noise in shaders: value noise, fBm and what they are for
- How to make an ocean shader: waves, Fresnel and foam