Shaders
What are Shaders?
Shaders are small programs that run on your GPU (Graphics Processing Unit) instead of your CPU. They're used to calculate how pixels should be colored on the screen. What makes shaders special is that they run in parallel - processing thousands or millions of pixels simultaneously on the GPU rather than one at a time on the CPU.
Shaders in Three.js
In Three.js, there are two main types of shaders:
- Vertex Shaders: Determine where the points (vertices) of a 3D object should be positioned
- Fragment Shaders: Determine what color each pixel should be
Let's start with a simple example using Three.js to create a shader that animates colors.
Understanding the Shader Code
Let's break down what's happening in our fragment shader:
uniform float u_time;
void main () {
vec2 uv = gl_FragCoord.xy / vec2 (${ width }.0, ${ height }.0);
// Create a pulsing effect based on time
float r = 0.5 + 0.5 * sin (u_time + uv.x * 6.0);
float g = 0.5 + 0.5 * sin (u_time + uv.y * 6.0);
float b = 0.5 + 0.5 * sin (u_time + uv.x * 2.0 + uv.y * 2.0);
gl_FragColor = vec4 (r, g, b, 1.0);
}
Here's what each part does:
uniform float u_time;
- a uniform is a value we pass from JavaScript into our shader. Here we're passing in the current time, in seconds.vec2 uv = gl_FragCoord.xy / vec2(${width}.0, ${height}.0);
- this creates normalized coordinates (0 to 1) across our canvas.gl_FragCoord.xy
gives us the pixel position.The
r
,g
, andb
variables - we use time-driven sinusoids to create pulsing color values. The* 6.0
parts change how quickly the colors cycle across the screen.gl_FragColor = vec4 (r, g, b, 1.0);
- This sets the final color of the pixel. The 1.0 at the end is an alpha value.
Adding Interactions
Let's make a shader that responds to mouse movement:
More Advanced - 3D with Shaders
Now let's apply a custom shader to mutate the geometry of a 3D object:
Key GLSL Language Features
GLSL (OpenGL Shading Language) has some useful features for this sort of work:
Data Types
float
: decimal numbers (1.0
,3.14159
)int
: whole numbers (1
,42
)bool
: true or false (true
,false
)vec2
,vec3
,vec4
: vectors with 2, 3, or 4 componentsmat2
,mat3
,mat4
: 2×2, 3×3, or 4×4 matrices
Operations
You can perform operations on entire vectors at once:
vec3 color1 = vec3 (1.0, 0.0, 0.5);
vec3 color2 = vec3 (0.0, 1.0, 0.5);
vec3 mixedColor = color1 + color2; // Results in (1.0, 1.0, 1.0)
Built-in Functions
GLSL has many useful built-in functions:
sin ()
,cos ()
,tan ()
: trigonometric functionsmix (a, b, t)
: linear interpolation betweena
andb
based ont
smoothstep (edge0, edge1, x)
: smooth transition between 0 and 1distance (p1, p2)
: calculate distance between two points
Shaders in p5.js
p5.js also supports shaders! Let's make a shader work in p5.js:
In this p5.js example:
- we create a p5.js sketch with the instance mode
- we define both vertex and fragment shaders as strings within our JavaScript code
- we use p5.js's
createShader ()
function to compile the shader - we pass uniforms to the shader using
setUniform ()
- the shader creates gradient background with animated circles and mouse interaction
This approach makes shader programming accessible to those who would prefer to use p5.js.
Creating Moiré Effects with Shaders
Moiré patterns are the interference effects that occur when two similar patterns are overlaid with slight differences.
1. Concentric Circles
This example creates a moiré pattern by overlaying two sets of concentric circles:
2. Rotating Grid Patterns
This example creates a moiré effect by overlaying two grid patterns, with one rotating over time.
3. Radial vs Linear Patterns
This example creates a moiré effect by combining radial lines with moving linear lines.
How Moiré Patterns Work
Moiré patterns occur when two similar patterns are overlaid with a slight difference in angle, spacing, or position. The key principles to remember:
Pattern Similarity - The two patterns should be similar but slightly different (two grids, two sets of concentric circles, etc.)
Interaction Methods:
- Multiplication - Multiply the two pattern values together
- Addition - Add the pattern values and then normalize
- Subtraction - Take the absolute difference between patterns
Movement - Animate one pattern relative to the other by:
- Rotating one pattern
- Scaling one pattern
- Moving one pattern's center
- Changing one pattern's frequency
Code and Concept References
The examples in this tutorial draw from several sources, including:
- The Book of Shaders: Shapes
- The Book of Shaders: Colors chapter
- Three.js ShaderMaterial documentation
- Three.js examples: Vertex displacement
- Inigo Quilez's articles on SDFs (signed distance functions)
Resources for Learning More
More resources:
- The Book of Shaders - A gentle step-by-step guide
- Shadertoy - See and experiment with amazing shaders
- Three.js Shaders Documentation - Reference for using shaders in Three.js
- Inigo Quilez's Articles - Deep dives into shader mathematics
- ShaderToy: Art of Code - YouTube channel with a bunch of shader tutorials