00:00/00:00TIME LEFT
Choosing the a bcd
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

Want to learn more?

That's the end of the free part 😔

To get access to 45 hours of video, a members-only Discord server and future updates, join us for only $95!

45.

Environment and Staging

Difficulty Medium

Downloads

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.js 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.js'

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: 50,
        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={ 1.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.

Want to learn more?

That's the end of the free part 😔

To get access to 45 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
    (if your browser detects a menace, do not worry, it is not)
  • Unzip it
  • Open your terminal and go to the unzip folder
  • Run npm install command to install dependencies
  • Run npm run dev to launch the local server
    (your browser should start automatically)

You can now 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

Any trouble? Go to the discord server: