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
Others
- Citrix & Redbull website (SSL certificate broken but no risk)
Three.js documentation
- IcosahedronGeometry
- MeshStandardMaterial
- DirectionalLight
- GLTFLoader
- DRACOLoader
- Mesh
- MeshDepthMaterial
- Uniform
- RawShaderMaterial
- common.glsl.js (Three.js shader chunk)
Custom Shader Material
GLSL functions
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
Another technique I get questions about, is how to slice a model like I did for the Citrix & Redbull website (the SSL certificate is broken and you need to scroll down once to see the slicing effect in action):
This effect is actually quite simple and involves concepts that we’ve already tackled, like how to enhance a built-in material, how to not display fragments and some trigonometry:
Setup 01:29
The starter already contains the following:
- A IcosahedronGeometry with a MeshStandardMaterial applied to it
- A plane to test the shadow
- An HDR environment map for the background but also to light up the scene
- A DirectionalLight to light up the sphere even more and cast a shadow on the plane
- The
vite-plugin-glsl
dependency to handle GLSL files - A
GLTFLoader
instance with aDracoLoader
instance associated with it - A gears model as
gears.glb
in thestatic/
folder OrbitControls
to rotate around- The
three-custom-shader-material
dependency to enhance built-in materials
Model 02:29
The lesson could have started with the model already implemented, but it’s a good opportunity to practice.
The gears model has been created in Blender and you can download the file using the Resources button:
If you open that file, you’ll see that it’s made up of 3 parts: the axle
, the outerHull
and the gears
.
The slicing effect must be applied only to the outerHull
so that it reveals the gears inside.
Since the sphere is a placeholder for the actual model, let’s remove the IcosahedronGeometry and the Mesh, but keep the material
:
/**
* Sliced model
*/
// Material
const material = new THREE.MeshStandardMaterial({
metalness: 0.5,
roughness: 0.25,
envMapIntensity: 0.5,
color: '#858080'
})
Load
The model is located in static/gears.glb
. We already have an instance of GLTFLoader and an instance of DRACOLoader associated with it.
Use the load()
method on the GLTFLoader instance, set the path to './gears.glb'
and add a gltf
parameter to the callback function:
// Model
gltfLoader.load('./gears.glb', (gltf) =>
{
})
Add the gltf.scene
to your scene
:
// Model
gltfLoader.load('./gears.glb', (gltf) =>
{
scene.add(gltf.scene)
})
Rotation
We are going to make the model execute a perpetual rotation so that we can enjoy it from different angles and lighting.
Since we want to rotate gltf.scene
on each frame, we need to do it in the tick
function, but we don’t have access to gltf.scene
outside of the callback function.
It’s a classic scope issue.
To fix that, before the load()
, create a model
variable using a let
, to which you assign null
:
let model = null
gltfLoader.load('./gears.glb', (gltf) =>
{
// ...
})
In the callback, assign gltf.scene
to model
:
let model = null
gltfLoader.load('./gears.glb', (gltf) =>
{
model = gltf.scene
scene.add(model)
})
We now have access to model
from anywhere.
In the tick
function, assign elapsedTime
multiplied by 0.1
to model.rotation.y
:
const tick = () =>
{
const elapsedTime = clock.getElapsedTime()
// Update model
model.rotation.y = elapsedTime * 0.1
// ...
}
We get an error, because it takes time to load the model and the model
variable will be null
for a few frames.
Wrap the model
rotation in an if
statement so that we don’t update it while it’s null
:
const tick = () =>
{
const elapsedTime = clock.getElapsedTime()
// Update model
if(model)
model.rotation.y = elapsedTime * 0.1
// ...
}
Material
The model currently has a white MeshStandardMaterial applied to it because it’s the default one Three.js applies when loading a model without material.
We still have our own MeshStandardMaterial in the material
variable and we want to apply it to the model.
To make sure that it’s applied to every Mesh of the model, we’re going to traverse it.
In the callback function, traverse the model using the traverse()
method on model
:
gltfLoader.load('./gears.glb', (gltf) =>
{
model = gltf.scene
model.traverse((child) =>
{
})
scene.add(model)
})
The function will be called on each child and grandchild of the model.
In that function, test if the child
is a Mesh using the isMesh
property:
gltfLoader.load('./gears.glb', (gltf) =>
{
model = gltf.scene
model.traverse((child) =>
{
if(child.isMesh)
{
}
})
scene.add(model)
})
In that if(child.isMesh)
, assign the material
to the child.material
property:
gltfLoader.load('./gears.glb', (gltf) =>
{
model = gltf.scene
model.traverse((child) =>
{
if(child.isMesh)
{
child.material = material
}
})
scene.add(model)
})
Shadows
We are going to activate both casting and receiving of the shadow on every Mesh of the model.
Still in the if(child.isMesh)
, set the castShadow
and receiveShadow
to true
:
gltfLoader.load('./gears.glb', (gltf) =>
{
model = gltf.scene
model.traverse((child) =>
{
if(child.isMesh)
{
child.material = material
child.castShadow = true
child.receiveShadow = true
}
})
scene.add(model)
})
Custom material 10:51
We have our model ready, so let’s apply our material to it.
We now want to replace the MeshStandardMaterial with an improved version which supports the slicing effect. To improve MeshStandardMaterial, we are going to use Custom Shader Material which is already available in the dependencies.
Instantiate
Import CustomShaderMaterial
from 'three-custom-shader-material/vanilla'
:
import CustomShaderMaterial from 'three-custom-shader-material/vanilla'
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