Shortcuts ⌨️

  • SPACE to play / pause
  • ARROW RIGHT or L to go forward
  • ARROW LEFT or J to go backward
  • ARROW UP to increase volume
  • ARROW DOWN to decrease volume
  • F to toggle fullscreen
  • M to toggle mute
  • 0 to 9 to go to the corresponding part of the video
  • SHIFT + , to decrease playback speed
  • SHIFT + . or ; to increase playback speed

Unlock content 🔓

To get access to 93 hours of video, a members-only Discord server, subtitles, lesson resources, future updates and much more join us for only $95!

Want to learn more? 👋


That's the end of the free part 😔

To get access to 93 hours of video, a members-only Discord server and future updates, join us for only $95!

Next lesson

Shader patterns

Difficulty Hard

Introduction 00:00

Often, while creating shaders, we need to draw specific patterns like stars, circles, light lenses, waves, etc.

It can help to effectively see those pattern on a geometry or it can be to move the vertices just like we did with the flag in the previous lesson.

We could use textures but drawing the shape gives us more control; we can animate the shape parameters, and there is no texture to load.

It's much more complicated than drawing with other APIs like canvas because the code is the same for every fragment, and all we have are coordinates and our mathematical skills.

Yes, there will be some maths in this lesson. It's one of the most frustrating parts for some people but fear not; even if you are doing poorly with maths, you'll find a solution.

In this lesson, we will try to draw various patterns on a plane. We will start very thoroughly, and things will get more challenging with time. It's the perfect occasion to discover classic techniques and use built-in functions.

For each pattern, we first study the result; then, we try to reproduce it. If you want to get better at this, pause the lesson on each pattern and try to do it yourself. Even if you fail, the solution will make more sense if you tried on you own.

Setup 03:21

Currently, we only have one plane on the scene with a ShaderMaterial as a PlaneGeometry. As a reminder, ShaderMaterial is like RawShaderMaterial, with some code prepended to the shaders like importing the matrices, importing some attributes, or setting the precision.

Send the UV coordinates to the fragment 04:33

Because we will draw the plane patterns, most of our code will be in the fragment shader. But first, we need to send the UV coordinates from the vertex shader to that fragment shader.

To retrieve the uv attribute in the vertex shader, we should have written something like this:

attribute vec2 uv;

But because we are using a ShaderMaterial, this code is already prepended to the vertex shader.

To send this value from the vertex shader to the fragment shader, we need a varying. We are going to call it vUv and assign it with the uv:

varying vec2 vUv;

void main()
    // ...

    vUv = uv;

In the fragment shader, we can retrieve this vUv varying with the same declaration:

varying vec2 vUv;

void main()
    // ...

We now have access to the uv coordinates in our fragment shader as vUv. The values go from 0, 0 on the bottom-left corner to 1, 1 on the top-right corner.

Pattern 1 10:38

This lovely color pattern is the easiest one to get. We just need to use the vUv in the gl_FragColor with the blue value being 1.0:

varying vec2 vUv;

void main()
    gl_FragColor = vec4(vUv, 1.0, 1.0);

Pattern 2 12:37

This is exactly the same pattern but with the blue value being 0.0:

varying vec2 vUv;

void main()
    gl_FragColor = vec4(vUv, 0.0, 1.0);

Pattern 3 13:36

Things get a little more interesting here. To get this gradient, we only use the x property of the vUv, but in all first three values of gl_FragColor:

varying vec2 vUv;

void main()
    gl_FragColor = vec4(vUv.x, vUv.x, vUv.x, 1.0);

From now, we are going to draw black and white patterns like this. Instead of sending the value on r, g, and b separately, we can create a float variable named strength:

varying vec2 vUv;

void main()
    float strength = vUv.x;

    gl_FragColor = vec4(vec3(strength), 1.0);

We will now focus on the strength variable and try to draw the following patterns.

Instead of replacing your previous patterns, you can comment so you can get back to them later.

Pattern 4 16:40

This pattern is exactly the same but on the y axis:

float strength = vUv.y;

Pattern 5 17:35

This pattern is exactly the same but we invert the value with 1.0 - ...:

float strength = 1.0 - vUv.y;

Pattern 6 18:18

To squeeze the gradient like this, we simply multiply the value. The strength will jump quickly to 1, but we can't show a color brighter than white so the rest of the gradient stays white:

float strength = vUv.y * 10.0;

Pattern 7 19:23

Now we are talking. To repeat the gradient, we use a modulo. The modulo operation finds the remainder after a division of the first number by the second one.

  • 0.5 modulo 1.0 will be 0.5
  • 0.8 modulo 1.0 will be 0.8
  • 1.2 module 1.0 will be 0.2
  • 1.7 modulo 1.0 will be 0.7
  • 2.0 modulo 1.0 will be 0.0
  • 2.4 modulo 1.0 will be 0.4

It's like having the first number going back to 0 once it reaches the second number.

In many languages, we can use the % to apply the modulo but in GLSL we have to use the mod(...) function:

float strength = mod(vUv.y * 10.0, 1.0);
Want to learn more?

That's the end of the free part 😔

To get access to 93 hours of video, a members-only Discord server and future updates, join us for only $95!

How to use it 🤔

  • Download the Starter pack or Final project
  • Unzip it
  • Open your terminal and go to the unzip folder
  • Run npm install to install dependencies
    (if your terminal warns you about vulnerabilities, ignore it)
  • Run npm run dev to launch the local server
    (project should open on your default browser automatically)
  • Start coding
  • The JS is located in src/script.js
  • The HTML is located in src/index.html
  • The CSS is located in src/style.css

If you get stuck and need help, join the members-only Discord server: