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? 👋

39%

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
30.

Animated galaxy

Difficulty Very hard

Introduction 00:00

You can also use shaders with the particles. As we saw in the Particles lesson, animating each vertex of the geometry isn't an efficient solution for performance reasons. That is where the GPU comes in by animating those vertices directly in the vertex shader.

In this lesson, we are going to start with our particle galaxy. We will animate the particles in the vertex shader to make the stars rotate but at different speeds depending on the center's distance, and we will draw a pattern in the particles instead of those ugly squares.

Setup 00:31

The starter is almost the same as the Galaxy Generator lesson starter. The only difference is the spin formula is missing because we will do the spin animation in the shader.

Replacing PointsMaterial by ShaderMaterial 00:48

The particles are currently using a PointsMaterial, but we need to use a ShaderMaterial if we want to write our own shaders.

Replace PointsMaterial by ShaderMaterial:

material = new THREE.ShaderMaterial({
    // ...
})

If you look at the logs, you should see two warnings telling us that the ShaderMaterial supports neither size nor sizeAttenuation. We will have to add these features on our own. For now, remove these properties:

material = new THREE.ShaderMaterial({
    depthWrite: false,
    blending: THREE.AdditiveBlending,
    vertexColors: true
})

At this exact moment, some might see the particles like tiny red dots, and some might get a black screen. That depends on how your GPU handles the particles when no size is provided. We won't waste time on this because we will give a size anyway, and everyone should see the particles.

Clearly, we need to provide our own shaders. Add the following vertexShader:

material = new THREE.ShaderMaterial({

    // ...

    vertexShader: `
        void main()
        {
            /**
             * Position
             */
            vec4 modelPosition = modelMatrix * vec4(position, 1.0);
            vec4 viewPosition = viewMatrix * modelPosition;
            vec4 projectedPosition = projectionMatrix * viewPosition;
            gl_Position = projectedPosition;

            /**
             * Size
             */
            gl_PointSize = 2.0;
        }
    `
})

The beginning of the shader is the same as we've already seen. We update the position by using successively the modelMatrix, the viewMatrix, and the projectionMatrix. But then, we assign a new variable called gl_PointSize with 2.0 as the value.

gl_PointSize is precisely what you might think. The particles will have a 2x2 size, and you should see 2x2 particles regardless of the distance of the camera.

The unit here are fragments and if you are using a normal screen with a pixel ratio of 1, you'll get 2 pixels by 2 pixels because 1 fragment = 1 pixel. But if you are using a screen with a higher pixel ratio like a retina screen, 1 fragment will be smaller than 1 pixel and you should get smaller particles. We'll fix that later in order to get a consistent result through any pixel ratio.

Before we improve the particles size, let's change the color.

The particles are currently red because we didn't provide any fragmentShader and Three.js uses a default one with a red output.

Add the following fragment shader with a white color:

material = new THREE.ShaderMaterial({

        // ...

        fragmentShader: `
            void main()
            {
                gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
                #include <colorspace_fragment>
            }
        `
    })

All the particles should be white.

As we’ve seen in a previous lesson, #include <colorspace_fragment> output the color in sRGB so that it matches WebGLRenderer color space which improves color management.

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

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

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