SPACEto play / pauseARROW RIGHTorLto go forwardARROW LEFTorJto go backwardARROW UPto increase volumeARROW DOWNto decrease volumeFto toggle fullscreenMto toggle mute0 to 9to go to the corresponding part of the videoSHIFT+,to decrease playback speedSHIFT+.or;to increase playback speed
Shortcuts ⌨️
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!
Introduction 00:00
Let’s continue our particles series with a classic yet difficult exercise. We are going to morph particles:
There are multiple ways of doing it and we could do it in JS by updating the attribute itself. But as we saw in previous lessons, updating the attribute is bad for performance and it would be impossible to maintain a good frame rate with a large amount of particles.
Another solution is by using GPGPU. It’s a good, but difficult solution which is why we won’t use it. Although it’s not for particle morphing, we’ll learn about GPGPU in one of the next lessons.
Finally, there is an easier solution that relies mostly on the vertex shader, and that we are going to discover now.
Setup 01:39
The starter is quite similar to the previous lesson with particles ready to be enhanced:
- A tweakable
clearColor - Some models in one file named
models.glbthat you can find in thestatic/folder (you can download the original Blender file using theResourcesbutton) - A
GLTFLoaderinstance with aDracoLoaderinstance associated with it - A sphere made out of particles using a ShaderMaterial
- The
vite-plugin-glsldependency to handle GLSL files OrbitControlsto rotate aroundgsapin the dependencies
The particle size is already handled in the vertex.glsl. We use a uSize uniform to control it from the JS. Then, perspective is applied and the final size is relative to the height of the render, as seen in previous lessons.
Also, note that everything related to particles has been set as properties of the particles object to keep things organised.
Theory 04:18
The idea is that we are going to send two sets of positions to the vertex shader, one being the initial shape, and another one being the targeted shape. For the initial shape, we can use the classic position attribute. For the targeted shape, we need to send a new attribute. Let’s call it aPositionTarget.
Then, we send a uniform which is going to be a float going from 0 to 1. Let’s name it uProgress. And we are going to use that uProgress to mix between position and aPositionTarget.
Transitioning from one shape to another should be as simple as animating the uProgress.
Obviously, we are going to face difficulties and add cool effects on top of it.
Pattern 06:06
Before tackling the morphing, let’s have some fun and draw a pattern instead of those squares.
We want to draw a bright point at the center of the particle and have that point fade out before it reaches the edges of the particle. We actually did that in the Shader Patterns lesson, but let’s create it from scratch:
In fragment.glsl, retrieve the particle UV using gl_PointCoord, save it as uv and send it to the gl_FragColor to make sure it’s working:
void main()
{
vec2 uv = gl_PointCoord;
gl_FragColor = vec4(uv, 1.0, 1.0);
// ...
}
Next, we need the distance to the center. We are going to use the technique we saw in one of the previous lessons by using the length of the offset uv instead of the distance from vec2(0.5) and send it to gl_FragColor:
void main()
{
vec2 uv = gl_PointCoord;
float distanceToCenter = length(uv - 0.5);
gl_FragColor = vec4(distanceToCenter, distanceToCenter, distanceToCenter, 1.0);
// ...
}
Using distanceToCenter, we can calculate the alpha.
We want it to be very high at first and plunge very fast. We can use the small number division technique.
The idea is that we take a number below 1 and divide it by an increasing value (distanceToCenter in our case):
Create a float alpha variable, to which you assign 0.05 , divided by distanceToCenter. Send it to gl_FragColor:
void main()
{
// ...
float alpha = 0.05 / distanceToCenter;
gl_FragColor = vec4(alpha, alpha, alpha, 1.0);
// ...
}
Not bad, but alpha never reaches 0.0 at the edges.
How to use it 🤔
- Download the Starter pack or Final project
- Unzip it
- Open your terminal and go to the unzip folder
-
Run
npm installto install dependencies
(if your terminal warns you about vulnerabilities, ignore it) -
Run
npm run devto 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