3D Graphics in ReactJS with ThreeJS

Mohammad Faisal - Mar 17 '23 - - Dev Community

To read more articles like this, visit my blog

3D graphics can improve the user experience of your application. It can make your application more attractive and engaging. It can also help you to explain your application better.

Today we will demonstrate how to use 3D models in ReactJS using react-fiber-three library.

What is three.js?

three.js is a 3D library that allows you to create 3D models in the browser. It is a wrapper around WebGL and allows you to create 3D models in the browser.

What is react-fiber-three?

react-fiber-three is a library that allows you to use three.js in ReactJS. It is a wrapper around three.js that allows you to use three.js in ReactJS.

How to use 3D models in ReactJS?

First create a new react applicaiton

npx create-vite react-fiber-three-demo
Enter fullscreen mode Exit fullscreen mode

Then install the required dependencies.

npm install three react-fiber-three
Enter fullscreen mode Exit fullscreen mode

Notice we are installing two dependencies.

  1. The first one is three.js which is the 3D library we are using.
  2. The second one is react-fiber-three which is the library we are using to integrate three.js with ReactJS.

react-fiber-three is very easy to use and also very efficient.

Create the Canvas

A canvas is the area where the 3D model will be rendered. We will create a canvas using the Canvas component from react-fiber-three.

import React, { Suspense, useEffect, useState } from "react";
import { Canvas } from "@react-three/fiber";
import { OrbitControls, Preload, useGLTF, Stage } from "@react-three/drei";

const ComputersCanvas = ({ children }: { children: React.ReactNode }) => {
  return (
    <Canvas
      frameloop="demand"
      shadows
      dpr={[1, 2]}
      camera={{ position: [0.2, 0.1, 0.3], fov: 25 }}
      gl={{ preserveDrawingBuffer: true }}
    >
      <OrbitControls
        enableZoom={true}
        maxPolarAngle={Math.PI / 2}
        minPolarAngle={Math.PI / 2}
        dampingFactor={0.3}
      />
      <Stage contactShadow={{ resolution: 1024, scale: 1000 }}>
        {children}
      </Stage>
      <Preload all />
    </Canvas>
  );
};

export default ComputersCanvas;
Enter fullscreen mode Exit fullscreen mode

Let's break down the code piece by piece

1. Canvas

Notice that we are setting some props to the Canvas component.

  1. frameloop is set to demand which means that the canvas will only render when the user interacts with the canvas.
  2. shadows is set to true which means that the canvas will have shadows.
  3. dpr is set to [1, 2] which means that the canvas will have a resolution of 1 or 2 depending on the device.
  4. camera is set to { position: [0.2, 0.1, 0.3], fov: 25 } which means that the camera will be positioned at [0.2, 0.1, 0.3] and the field of view will be 25.
  5. gl is set to { preserveDrawingBuffer: true } which means that the canvas will preserve the drawing buffer.

You should definitely play around with these parameters to get the best results. To learn more about these parameters, you can read the documentation of three.js.

2. Camera Control

To control the camera, we are using the OrbitControls component from @react-three/drei. This component allows us to control the camera using the mouse.

We are setting some props to the OrbitControls component.

  1. enableZoom is set to true which means that the user can zoom in and out using the mouse.

  2. maxPolarAngle and minPolarAngle is set to Math.PI / 2 which means that the user can only rotate the camera vertically.

  3. dampingFactor is set to 0.3 which means that the camera will have a damping factor of 0.3. It will smooth out the camera movement.

3. Stage

This creates a stage for the 3D model with proper ligtning and shadows.

Now Let's load the model

Get the actual 3d model.

There are several websites where you can get the free 3d models. https://sketchfab.com/ is the best one IMO.
Let's go there and download a free downloadable 3d model.

The one we are using today is this

https://sketchfab.com/3d-models/radio-shack-trs-80-model-1-9e3277cd95274f248d12b91dff3ea1cb

Download the glTF version of the model

Get the unzipped version and put it inside the public folder of your project.

Load the model

Let's create a new component called ComputerModel and load the model in it.

import React from "react";
import { useGLTF } from "@react-three/drei";

export const ComputerModel = () => {
  const computer = useGLTF("./radio_shack_trs-80_model_1/scene.gltf");

  return (
    <mesh>
      <hemisphereLight intensity={0.15} groundColor="black" />
      <spotLight
        position={[-20, 50, 10]}
        angle={0.12}
        penumbra={1}
        intensity={1}
        castShadow
        shadow-mapSize={1024}
      />
      <pointLight intensity={1} />
      <primitive
        object={computer.scene}
        scale={0.5}
        position={[0, -3.25, -1.5]}
      />
    </mesh>
  );
};
Enter fullscreen mode Exit fullscreen mode

Let's analyze the code.

1. useGLTF hook

Here we are using the useGLTF hook from @react-three/drei to load the model. We are passing the path to the model as the argument to the useGLTF hook.

Then we are returning a mesh component which is the actual 3D model.

2. Lighting

We are also adding some lights to the model.

  1. hemisphereLight is a light that is emitted from the sky. It is used to simulate the sun.
  2. spotLight is a light that is emitted from a point in a specific direction. It is used to simulate the light from a lamp.

3. Positioning the model

Finally, We are positioning the model using the scale and position props. We are scaling the model by 0.5 and positioning it at [0, -3.25, -1.5].

Feel free to play around with these values.

Render the model

Now let's render the model in the canvas.

import "./App.css";
import CanvasComponent from "./components/CanvasComponent";
import Overlay from "./Overlay";
import { ComputerModel } from "./components/ComputerModel";

function App() {
  return (
    <div id="canvas-container" style={{ width: "100vw", height: "90vh" }}>
      <CanvasComponent>
        <ComputerModel />
      </CanvasComponent>
      <Overlay />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Here we are rendering the CanvasComponent and passing the ComputerModel as a child to it.

Improve the performance

Now you will notice that the model is not loading properly. It is because the model is too big and the browser is not able to load it properly.

So we are going to use the Suspense component from react to render the model only when it is loaded. This will improve the performance.

Following is the final version of the code.

import React, { Suspense, useEffect, useState } from "react";
import { Canvas } from "@react-three/fiber";
import { OrbitControls, Preload, Stage } from "@react-three/drei";
import CanvasLoader from "../Loader";

const ComputersCanvas = ({ children }: { children: React.ReactNode }) => {
  return (
    <Canvas
      frameloop="demand"
      shadows
      dpr={[1, 2]}
      camera={{ position: [0.2, 0.1, 0.3], fov: 25 }}
      gl={{ preserveDrawingBuffer: true }}
    >
      <Suspense fallback={<CanvasLoader />}>
        <OrbitControls
          enableZoom={true}
          maxPolarAngle={Math.PI / 2}
          minPolarAngle={Math.PI / 2}
          dampingFactor={0.3}
        />
        <Stage contactShadow={{ resolution: 1024, scale: 1000 }}>
          {children}
        </Stage>
      </Suspense>
      <Preload all />
    </Canvas>
  );
};

export default ComputersCanvas;
Enter fullscreen mode Exit fullscreen mode

You will notice that we have added a Suspense component and a CanvasLoader component. The CanvasLoader component is just a loader that will be shown when the model is loading. You can use any loader you want.

Here is the code for that.

import { Html, useProgress } from "@react-three/drei";

const CanvasLoader = () => {
  const { progress } = useProgress();
  return (
    <Html
      as="div"
      center
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        flexDirection: "column",
      }}
    >
      <span className="canvas-loader"></span>
      <p
        style={{
          fontSize: 14,
          color: "#F1F1F1",
          fontWeight: 800,
          marginTop: 40,
        }}
      >
        {progress.toFixed(2)}%
      </p>
    </Html>
  );
};

export default CanvasLoader;
Enter fullscreen mode Exit fullscreen mode

Result:

On your browser you should have the following image.

3d model render in ReactJS

Awesome right?

Conclusion

This tutorial taught us how to create a 3D model viewer using React and Three.js.
We have also learned how to load a 3D model in React and how to add lighting and shadows to the model.

I hope you learned something new today.

Have a wonderful day.

You can reach me via LinkedIn or Read more on my website

Source Code:

https://github.com/Mohammad-Faisal/react-threejs-demo

Demo:

https://react-threejs-demo.vercel.app/

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .