Adding camera controls in react-three-fiber
In the previous post we set up a simple WebGL scene using r3f (react-three-fiber) to display a light and a cube on the page.
import React from "https://cdn.skypack.dev/react@17.0.1";
import ReactDOM from "https://cdn.skypack.dev/react-dom@17.0.1";
import { Canvas } from "https://cdn.skypack.dev/react-three-fiber@5.3.1";
const App = () => (
<Canvas>
<pointLight position={[5, 5, 5]} />
<mesh rotation={[45, 45, 45]}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="red" />
</mesh>
</Canvas>
);
ReactDOM.render(<App />, document.getElementById("root"));
While this is fun, and could be useful if you're setting up a website about cubes, it needs a bit more interactivity to really shine. The next thing to do is to add a camera controller.
The Three.JS library comes with a set of example camera controllers that work in different ways.
- deviceOrientationControls - orient a scene to match a mobile device
- dragControls - pan around a scene, particularly useful for orthographic cameras
- firstPersonControls - control the camera like it's a first person shooting game
- orbitControls - rotate a camera around a point in space
- pointerLockControls - make the camera look at where the mouse is pointing
- trackballControls - very similar to orbitControls but up is handled differently at poles
These camera controllers have different uses, but usually a simple scene will use orbitControls if you're only interesting in seeing an object from different sides.
Setting up a camera controller is a chore that every Three.JS app has to do, but fortunately react-three-fiber has a helper library called Drei that includes a camera controller component. This makes it much easier.
import React from "https://cdn.skypack.dev/react@17.0.1";
import ReactDOM from "https://cdn.skypack.dev/react-dom@17.0.1";
import { Canvas } from "https://cdn.skypack.dev/react-three-fiber@5.3.1";
import { OrbitControls } from "https://cdn.skypack.dev/@react-three/drei/OrbitControls";
const App = () => (
<Canvas style={{ height: 400, width: 800 }}>
<pointLight position={[5, 5, 5]} />
<mesh rotation={[45, 45, 45]}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="red" />
</mesh>
<OrbitControls />
</Canvas>
);
ReactDOM.render(<App />, document.getElementById("root"));
In addition to the new import of OrbitControls, and adding the component to the App component, there's another change here. style={{ height: 400, width: 800 }} has been added to the Canvas component. This is simply to give the scene more height and make the cube appear bigger.
With the addition of two lines of code we can now use the mouse to click and drag the cube around to move the camera and view it from different angles.
This is where r3f shines. Adding relatively complex features is made very simple.
Half the cube is black
If you orient the camera to look at the cube from behind you'll see that it's not red any more. This is quite obvious really - the light from the scene's pointLight isn't hitting the back of the cube so it's in darkness, and hence black.
We can easily fix that be adding more lights, or by moving the light, but this is something to be very careful about when you're using Three.JS. Adding lights is expensive in terms of computation. Adding lots of lights will make a scene slow down significantly.
The easiest way to tell how quickly your scene is running is to add a stats component that tells you. We'll do that in the next post.
(Hint: It's very similar to adding OrbitControls)


