00:00/00:00
3:22
00:03:22

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

⚠️ Update

In the latest versions of Three.js, we need to output the colors in sRGB color space.

To do that, add the following line at the end of the fragment shader.

void main()
{
    // ...
    #include <colorspace_fragment>
}

You’ll discover what this #include is later in the course.

Colors might look slightly different from the lesson, but you’ll be able to tweak them.

Unlock content 🔓

To get access to 91 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? 👍

38%

That's the end of the free part 😔

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

Next lesson
29.

Raging sea

Difficulty Hard

Introduction 00:00

Now that we know how to use shaders and draw some patterns let's make fair use of it and create a raging sea.

We are going to animate the waves and keep control of the parameters with the debug panel.

Setup 00:36

For now, all we have is a rotated plane using MeshBasicMaterial. The geometry has a 128x128 subdivision. We are going to animate the vertices to get the waves and we need quite a lot of vertices. 128x128 might not be enough, but we will increase the value if required.

Base 01:14

Let's replace the material by a ShaderMaterial:

const waterMaterial = new THREE.ShaderMaterial()

Our project configuration already supports GLSL files, but we need to create these files.

Create the vertex shader in /src/shaders/water/vertex.glsl:

void main()
{
    vec4 modelPosition = modelMatrix * vec4(position, 1.0);
    vec4 viewPosition = viewMatrix * modelPosition;
    vec4 projectedPosition = projectionMatrix * viewPosition;
    gl_Position = projectedPosition;
}

Now create the fragment shader in /src/shaders/water/fragment.glsl:

void main()
{
    gl_FragColor = vec4(0.5, 0.8, 1.0, 1.0);
}

Three.js WebGLRenderer requires us to output color in the right color space (by default sRGB). This will improve the colors of the output, but it also means that we need to add some code at the end of the fragment shader in order to convert the current gl_FragColor to a sRGB version.

Fortunately, Three.js already provides this code and all we need to do is add the following line at the very end of our fragment shader:

void main()
{
    // ...
    #include <colorspace_fragment>
}

You’ll discover what this #include is later in the course.

Finally, import those shaders in your script and use them in a ShaderMaterial:

// ...

import waterVertexShader from './shaders/water/vertex.glsl'
import waterFragmentShader from './shaders/water/fragment.glsl'

// ...

const waterMaterial = new THREE.ShaderMaterial({
    vertexShader: waterVertexShader,
    fragmentShader: waterFragmentShader
})

You should get a blue plane. If not, check the logs.

If you did all of this from memory, congratulation, you are a genius. If not, it's perfectly normal, and you only need time.

Big waves 08:35

We begin with the big waves to get significant results quickly. What's better than a sinus to create waves?

In the vertex shader, let's move the y value of the modelPosition with a sin(...) based on the x:

vec4 modelPosition = modelMatrix * vec4(position, 1.0);
modelPosition.y += sin(modelPosition.x);

The displacement and frequency should be way too high. Instead of just multiplying the values numbers coming out from nowhere in the shader, we will use uniforms to have more control over them.

Let's start with the elevation.

Elevation

Add a uBigWavesElevation uniform to the ShaderMaterial:

const waterMaterial = new THREE.ShaderMaterial({
    vertexShader: waterVertexShader,
    fragmentShader: waterFragmentShader,
    uniforms:
    {
        uBigWavesElevation: { value: 0.2 }
    }
})

We can now retrieve and use the uBigWavesElevation uniform in the vertex shader:

uniform float uBigWavesElevation;

void main()
{
    // ...
    modelPosition.y += sin(modelPosition.x) * uBigWavesElevation;

    // ...
}

Want to learn more?

That's the end of the free part 😔

To get access to 91 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:

Discord