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
- Drei
- Spidersharma03
- Shadowmap PCSS
- Sky (Three.js)
- Poly Haven
- Environment presets
- Lightformer example
Three.js documentation
Shortcuts ⌨️
⚠️ Update
<SoftShadows>
attributes have changed.
<SoftShadows size={ 25 } samples={ 10 } focus={ 0 } />
size
: radius of the softnesssamples
: quality (more samples = less visual noise but worse performance)focus
: distance where the shadow is the sharpest
⚠️ Update
In the latest versions of Three.js, the whole AccumulativeShadow
will look darker with this setting.
You can ignore that because we are going to replace it right after.
⚠️ Update
In the latest versions of Three.js, lights require a higher intensity
and it’s also the case for <RandomizedLight>
<RandomizedLight
amount={ 8 }
radius={ 1 }
ambient={ 0.5 }
intensity={ 3 }
position={ [ 1, 2, 3 ] }
bias={ 0.001 }
/>
⚠️ Update
Use version 0.9.34
of leva
:
npm install leva@0.9.34
⚠️ Update
In the latest versions of Three.js, we can update the envMapIntensity
of the all materials without having to set it manually on each one by updating the envMapIntensity
property of Scene.
Import useThree
from @react-three/fiber
:
import { useThree, useFrame } from '@react-three/fiber'
Right after the useControls()
for envMapIntensity
, retrieve the scene
:
const scene = useThree(state => state.scene)
Import useEffect
from react
:
import { useEffect, useRef } from 'react'
Using useEffect
, only assign envMapIntensity
to scene.environmentIntensity
when envMapIntensity
changes:
const scene = useThree(state => state.scene)
useEffect(() =>
{
scene.environmentIntensity = envMapIntensity
}, [ envMapIntensity ])
⚠️ Update
<Stage>
now supports Accumulative Shadows.
For this reasons, the syntax to tweak the shadows has changed a little.
Here’s an example:
<Stage
shadows={ {
type: 'contact',
opacity: 0.2,
blur: 3
} }
>
⚠️ Update
The intensity
will also update the scene.envMapIntensity
like we did earlier. To prevent conflicts, we are going to comment our implementation.
Comment the useThree
and useEffect
part:
// const scene = useThree(state => state.scene)
// useEffect(() =>
// {
// scene.environmentIntensity = envMapIntensity
// }, [ envMapIntensity ])
Send the envMapIntensity
to the intensity
prop:
<Stage
shadows={ { type: 'contact', opacity: 0.2, blur: 3 } }
environment="sunset"
preset="portrait"
intensity={ envMapIntensity }
>
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
Until now, we’ve been using a very simple environment with one directional light source and one ambient light source.
In this lesson, we are going to see some of the many environmental features available and how to implement them in R3F.
Setup 00:45
The starter is very similar to the previous lesson with a sphere, a cube and a green floor.
In addition, the cube is rotating thanks to the useFrame
and we’ve also added <Perf />
from r3f-perf
in order to keep an eye on performance.
The @react-three/drei
dependency is already installed within the project and we are using the OrbitControls
helper to be able to move the camera around.
Background color 01:32
For the sake of learning, we are going to discover multiple ways of changing the background color.
Note that if all you want is a uniform color, any of those techniques are viable solutions. It’s up to you and your preferences. In some very specific cases, like when using postprocessing, the technique you chose to use can have a different result, but we are going to see that in a future lesson.
With CSS
The default background color seems to be white, but as we saw in a previous lesson, it’s actually transparent and what we are seing is the page HTML background.
This means that we can change the color directly in CSS.
In /src/style.css
, change set the background-color
(or just background
) to red
:
html,
body,
#root
{
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: red;
}
And that’s all. Quite easy, right? But let’s discover the other techniques.
Comment or remove the background property:
html,
body,
#root
{
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
// background: red;
}
With setClearColor on the renderer
The WebGLRenderer has a method named setClearColor
. It’s a way of filling the <canvas>
with a color before rendering the various objects in the scene.
To use setClearColor
, we need to have access to the renderer and we need to do that only once when the renderer has been created.
In index.jsx
create a created
function and send it to the <Canvas>
attribute named onCreated
:
const created = () =>
{
console.log('created')
}
root.render(
<Canvas
camera={ {
fov: 45,
near: 0.1,
far: 200,
position: [ - 4, 3, 6 ]
} }
onCreated={ created }
>
<Experience />
</Canvas>
)
The state
will be sent as an argument of the function and the renderer
will be available in the gl
property:
const created = (state) =>
{
console.log(state.gl)
}
And if you like to destructure:
const created = ({ gl }) =>
{
console.log(gl)
}
We can then call the setClearColor
with the color as the first parameter and the alpha as the second parameter:
const created = ({ gl }) =>
{
gl.setClearColor('#ff0000', 1)
}
With the scene background
That was a viable solution, but let's check out another one.
Instead of doing it on the renderer
, we can do it on the scene
and we can access it in the created
function the same way:
const created = ({ scene }) =>
{
console.log(scene)
}
We can now instantiate a Color
using Three.js and assign it to the background
property (don’t forget to import THREE
or just Color
from three
)
import * as THREE from 'three'
// ...
const created = ({ scene }) =>
{
scene.background = new THREE.Color('#ff0000')
}
With R3F color
That’s also a good solution, but wait, there is an even better alternative.
Instead of listening to events and importing Three.js, we can create the color directly in the JSX.
First, let’s do some clean-up and comment or remove the THREE
import, the created
function and the onCreated
attribute:
import './style.css'
import ReactDOM from 'react-dom/client'
import { Canvas } from '@react-three/fiber'
import Experience from './Experience.jsx'
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(
<Canvas
camera={ {
fov: 45,
near: 0.1,
far: 200,
position: [ - 4, 3, 6 ]
} }
>
<Experience />
</Canvas>
)
Then, create a <color>
inside the <Canvas>
:
<Canvas
camera={ {
fov: 45,
near: 0.1,
far: 200,
position: [ 1, 2, 6 ]
} }
>
<color args={ [ '#ff0000' ] } />
<Experience />
</Canvas>
Doing so will automatically create a Color
instance, but that instance isn’t assigned to the scene.background
.
And here’s the trick.
We can add an attach
attribute to specify what that component should be attached to.
<color args={ [ '#ff0000' ] } attach="background" />
Here, the scene
is implied because it’s the only parent.
We can actually put that code anywhere, as long as the direct parent is the scene
which is still the case if we put it in Experience
:
export default function Experience()
{
// ...
return <>
<color args={ [ '#ff0000' ] } attach="background" />
{/* ... */}
</>
}
Let’s change it to a less annoying color like 'ivory'
:
<color args={ [ 'ivory' ] } attach="background" />
(The following screenshots aren’t using this background color which is why they will look more white).
Lights 13:43
All default Three.js lights are supported in R3F:
<ambientLight />
<hemisphereLight />
<directionalLight />
<pointLight />
<rectAreaLight />
<spotLight />
We are not going to test them since we already saw how they work in the Lights lesson and they work just the same with R3F.
Light Helpers
We can still use Three.js light helpers too.
To do so, we are going to use useHelper
from drei, but first, we need a reference to the <directionalLight>
.
useRef
is already import from react
to animate the cube.
Create a directionalLight
reference:
export default function Experience()
{
const directionalLight = useRef()
// ...
}
Associate it with the <directionalLight>
using the ref
attribute:
<directionalLight ref={ directionalLight } position={ [ 1, 2, 3 ] } intensity={ 4.5 } />
Import useHelper
from @react-three/drei
:
import { useHelper, OrbitControls } from '@react-three/drei'
The first parameter of useHelper
is the reference to the light source and the second parameter is the helper class we want to use from Three.js.
This means that we first need to import THREE
in order to get access to the DirectionalLightHelper
class:
import * as THREE from 'three'
(We could also have imported the DirectionalLightHelper
class only).
Finally, we can call useHelper()
with the reference (directionLight
), the helper class (THREE.DirectionalLightHelper
) and the parameters of the helper (in our case, the size):
export default function Experience()
{
const directionalLight = useRef()
useHelper(directionalLight, THREE.DirectionalLightHelper, 1)
// ...
}
It’s barely visible since it’s composed of white lines, but move the camera and you should see it.
useHelper
isn’t just for the light, as we could have used it for the camera with CameraHelper
as an example.
Shadows 18:28
To make the scene more realistic, we need shadows.
We are going to start with the default Three.js shadows system, but then we are going to see other shadow solutions made easier thanks to R3F and drei.
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