SPACE
to play / pauseARROW RIGHT
orL
to go forwardARROW LEFT
orJ
to go backwardARROW UP
to increase volumeARROW DOWN
to decrease volumeF
to toggle fullscreenM
to toggle mute0 to 9
to 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
The halftone is a technique used in printing to create color variations looking like gradients using a grid of points of the same color but of varying size.
Seen from far away, a combination of various halftones results in pleasant shades of colors.
Today, it’s used to create old school vibes and you can appreciate it in modern movies such as Spider‑Man: Into the Spider‑Verse.
Make sure to watch the following images in a high enough resolution to appreciate the effect:
And here is a good example of halftone implementation by @YanSculpts: https://twitter.com/YanSculpts/status/1692251937087832389
In this lesson, we are going to reproduce this effect to add shadow and reflection to our 3D objects:
Setup 02:57
The starter already contains the following:
- 3 rotating objects: a sphere, Suzanne and a torus knot
- A basic shader already included in the
src/shaders/halftone/
folder with avertex.glsl
and afragment.glsl
- Light functions from the previous lessons in the
includes/
folder (we won’t be using them just yet) - An instance of
lil-gui
, with two tweaks, that controls theclearColor
and theuColor
uniform sent to the fragment shader. - The
vite-plugin-glsl
dependency to handle GLSL files OrbitControls
to rotate around
Lighting 04:14
Although the halftone idea is to create shading with uniform color, we are actually going to apply it like in Spider‑Man: Into the Spider‑Verse and add it on top of a subtle light shading.
- The
vNormal
and thevPosition
are already sent from the vertex to the fragment. - The
viewDirection
is already calculated. - The
normal
is already normalized.
We are going to use the ambient light and the directional light.
Include both light sources in fragment.glsl
:
#include ../includes/ambientLight.glsl
#include ../includes/directionalLight.glsl
void main()
{
// ...
}
Create a light
variable to vec3(0.0)
and multiply it by the color
:
void main()
{
vec3 viewDirection = normalize(vPosition - cameraPosition);
vec3 normal = normalize(vNormal);
vec3 color = uColor;
// Lights
vec3 light = vec3(0.0);
color *= light;
// ...
}
Add the following ambientLight()
:
void main()
{
// ...
// Lights
vec3 light = vec3(0.0);
light += ambientLight(
vec3(1.0), // Light color
1.0 // Light intensity,
);
color *= light;
// ...
}
We end up with the initial color because we are using a light intensity of 1.0
.
Add the following directionalLight()
:
void main()
{
// ...
light += directionalLight(
vec3(1.0, 1.0, 1.0), // Light color
1.0, // Light intensity
normal, // Normal
vec3(1.0, 1.0, 0.0), // Light position
viewDirection, // View direction
1.0 // Specular power
);
// ...
}
The directional light adds a subtle shading to our objects.
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