Implementing 3D graphics in React

Implementing 3D graphics in React

by Favour Ogbonda

3D is an exciting area in computer science, and it could range from creating 3D shapes, vectors, and graphics. In this article, we will see how to do the goal of creating 3D graphics in React.

In React, there are different libraries for rendering 3D. In this article, we will focus on how to use the Three.js Library, React Three Fiber library, and the React Three Drei library for creating 3D objects.

We have three questions to consider.

  • What is 3D Rendering? 3D rendering is the process of using data and models to represent a three-dimensional interface or surface. Its use is to create scenes, vectors, graphics, and planes that look like real-world scenarios. Its use is in game creation and most engineering fields for simulation. Some new website UI now incorporates animations and 3D graphics to make the website look nice. For example, on GitHub, if you look to the right of the site, you will see an image model of the Earth rotating along its x-axis, from left to right.
  • How to Achieve 3D Rendering in React? Achieving 3D Rendering in React is not difficult because you do not need to start calculating the X, Y, and Z axis. There are libraries to help you to create 3D in React.
  • What Processes are there for 3D Rendering? There are different processes when it comes to rendering 3D images in React, and we'll be considering a trio here. We will use the following libraries: Three.js, React Three Fiber, and React Three Drei.

Three.js

Three.js is a JavaScript library that helps to create 3D graphics. You can find their website and documentation using the link: Three.js. Using it in React is simple all you have to do is:

npm install three

You have to do that in your React app directory.

React Three Fiber

React Three Fiber is a React library that helps to render 3D graphics in React. When you create or generate the 3D model using Three.js, you will need to use the React Three Fiber to tell the React app how or to render the 3D model. You can find their site and documentation here: React Three Fiber. It is also simple to use the React Three Fiber library in React; all you have to do is:

npm install @react-three/fiber

React Three Drei

React Three Drei is a 3D library with different functions that help to render 3D React Three Fiber. To install it, you can use the following:

npm install @react-three/drei

And with all that known, let's start learning and creating some 3D models.

3D Rendering

We'll work our way through a series of progressively more complex examples.

Rendering Geometric Shapes

First, let's try to create a geometric shape. A geometric shape is a figure that represents an object. It could be a 2D object on a plane like a square, triangle, or circle with an X and Y axis. It could also be a 3D object like a sphere or cone with an X, Y, and Z axis. To start:

  1. let's create our React app;
    npx create-react-app test-3d
    
  1. Let's install our 3D libraries - I prefer installing them one by one to avoid errors, but you can also install them in a single step. ``` npm install three npm install @react-three/fiber npm install @react-three/drei

...or...

npm install three @react-three/fiber @react-three/drei


3. In your source folder, create a components folder.

4. Create a file named Sphere.js in your components folder, and write the following code.
```javascript
import React from 'react'
function Sphere() {
  return (
    <mesh >
        <sphereBufferGeometry attach='geometry' args={[2,32]}/>
    </mesh>
  )
}

export default Sphere
  1. In your App.js file, import and write the following code. ```javascript
    import React from 'react'; import './App.css'; import Sphere from './components/Sphere'; function App() { return (
     <Sphere/>
    
    ); }

export default App;


Then run the code on your browser. When you run the code, you get a plane circle with black color, like in the image below.

![1](https://cdn.hashnode.com/res/hashnode/image/upload/v1667560665705/a1Q2p11BB.png)


But it does not look like 3D. First, let us understand what the code does.
After installing and importing the needed libraries, we create a component file for our 3D circle. In the component file, we create a sphere geometry or shape; you will observe that the code is in a `mesh` tag. Because when creating a 3D shape, you don't use a div. The `mesh` tag represents the [`THREE.Mesh()`](https://threejs.org/docs/#api/en/objects/Mesh) in React, which is a Three.js function. Also, in the following line of code `<sphereBufferGeometry attach='geometry' args={[2,32]}/>` targets to create a geometric shape, you have to give an `attach='geometry'` attribute to create the shape the `args` attribute targets to set the dimensions of the geometric shape. 
In step 5, we imported the file component `<Sphere/>` into `App.js` and used the  `Canvas` element. 
The `Canvas` element is used instead of a `div` element to wrap 3D `mesh` elements. The element targets rendering 3D Shapes or models representing the scene.

Before going further, it's important to note that when rendering 3D objects, you need to know the four essential elements. They are:
1. Scene
2. Geometry
3. Texture
4. Light

In the codes above, we used two elements: Scene and Geometry.

### Creating Materials 

In this section, we will focus on the two remaining elements: texture and light.
In the image above, you will observe that it looks dark, and that is because there is no light. There are two types of light to add: `Ambient light` and `Directional light`.
The ambient light adds light to the 3D object, while the directional light adds light to the edges to make them visible.
To use them, inside your App.js code, write the following;

```javascript
import React from 'react';
import './App.css';
import Sphere from './components/Sphere';
function App() {
  return (
    <div className="App">
      <ambientLight intensity={0.5}/>
      <directionalLight position={[-2,10,2]} intensity={1}/>
      <Canvas className='canvas1'>
        <Sphere/>
      </Canvas>
    </div>
  );
}

export default App;

You will get this image.

2

Now the image has a light, and it's no longer dark. In the code above, you will notice an intensity and position attribute. We used the intensity to set the brightness of the light, while the position is an array used to set the positions the directional Light will touch. With the light added, we can add materials to the Sphere.js component. The materials can be the texture, color, distortion, etc. We use a meshLambertMaterial to add materials in our Sphere.js component.

import React from 'react'
function Sphere() {
  return (
    <mesh >
        <sphereBufferGeometry attach='geometry' args={[2,32]}/>
        <meshLambertMaterial attach='material' color='red'/>
    </mesh>
  )
}

export default Sphere

There are different types of materials in Three.js, but I'll focus on this one for now. In the meshLambertmaterial I used the attributes attach='material' and color='red'. The attach='material' tells React Three Fiber to use the attributes set in the mesh material object as the property of the geometric shape, like the color, distort.

3

That's what our geometric object will look like when we add a material. Now let's add texture. To do so, let's do the following:

  1. In our Sphere.js component, we import useLoader and useFrame from React Three Fiber.

    import {useLoader, useFrame} from '@react-three/fiber'
    
  2. Import TextureLoader from Three.js.

    import { TextureLoader } from 'three/src/loaders/TextureLoader'
    
  3. Go to textures.com to download free textures images.

  4. After downloading, create an Images folder in your source or SRC folder, and rename and copy the image into it.

  5. Import the image from the Image.js file as a texture.

    import React,{useRef} from 'react'
    import {useLoader, useFrame} from '@react-three/fiber'
    import { TextureLoader } from 'three/src/loaders/TextureLoader'
    import texture from '../Images/Texture.jpg'
    function Sphere() {
    const textureMap= useLoader(TextureLoader, texture);
    return (
     <mesh >
         <sphereBufferGeometry attach='geometry' args={[2,32]}/>
         <meshStandardMaterial map={textureMap}/>
     </mesh>
    )
    }
    export default Sphere
    

In the code above, a meshStandardMaterial was used, and it has a map attribute. The meshStandardMaterial adds texture to a 3D object, while its map attribute adds the texture it will use and takes the constant variable as its parameter. In the code, a useLoader and TextureLoader were imported and passed the texture image using the texture map.

4

The image above shows the circle with the texture image passed to it.
Let's try to make a plane surface for our object. In your App component, add the following code.

<group>
        <mesh rotation={[-Math.PI / 2, 0, 0]} position = {[0, -3, 0]>
          <planeBufferGeometry attach='geometry' args={[100, 100]}/>
          <meshStandardMaterial attach='material' color='green'/>
        </mesh>
</group>

The plane geometry is separate from the sphere geometry, so the group element was used to note its children. The mesh uses a rotation and position to put the plane below the geometry to indicate its surface. After that, add orbit controls to move the object. We must import a 3D function from React Three Drei to do so. So in your App.js import;

import { OrbitControls } from '@react-three/drei';
import React from 'react';
import './App.css';
import Sphere from './components/Sphere';
function App() {
  return (
    <div className="App">
      <OrbitControls enableZoom={false} />
      <ambientLight intensity={0.5}/>
      <directionalLight position={[-2,10,2]} intensity={1}/>
      <Canvas className='canvas1'>
        <Sphere/>
      </Canvas>
      <group>
        <mesh rotation={[-Math.PI / 2, 0, 0]} position = {[0, -3, 0]>
          <planeBufferGeometry attach='geometry' args={[100, 100]}/>
          <meshStandardMaterial attach='material' color='green'/>
        </mesh>
      </group>
    </div>
  );
}

export default App;

You can also disable zoom to allow you to freely move the geometric object around its axis. Lastly, you can add an auto rotation on the object by adding the following code to the sphere component file.

import React,{useRef} from 'react'
import {useLoader, useFrame} from '@react-three/fiber'
import { TextureLoader } from 'three/src/loaders/TextureLoader'
import texture from '../Images/Texture.jpg'
function Sphere() {
  const mesh =useRef(null);
  useFrame(() => (mesh.current.rotation.x = mesh.current.rotation.y += 0.01));
  const textureMap= useLoader(TextureLoader, texture);
  return (
    <mesh ref={mesh}>
        <sphereBufferGeometry attach='geometry' args={[2,32]}/>
        <meshStandardMaterial map={textureMap}/>
    </mesh>
  )
}
export default Sphere

5 Sphere Ball

The screencast above shows a demo of the code above when you run it. I used a texture image that looks like a football. The code from lines 6 to 7 in our sphere component file is what enables our ball to spin. Let's try another geometric shape, and this one will be a cone. To do it:

  1. Create a component called Cone.js in your React app.

  2. In the file, write the following code:

import React from 'react'
function Cone() {
  return (
    <mesh>
        <coneGeometry attach='geometry' args={[2,5]} />
        <meshNormalMaterial attach='material'/>
    </mesh>
  )
}

export default Cone
  1. In the App.js file, write the following code;
import React from 'react';
import './App.css';
import Cone from './components/Cone';
import { Canvas } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
function App() {
  return (
    <div className="App">
      <Canvas className='canvas2'>
      <ambientLight intensity={0.5}/>
      <directionalLight position={[-2,10,2]} intensity={1}/>
      <OrbitControls enableZoom={false}/>
      <Cone/>
      </Canvas>
    </div>
  );
}

export default App;

Then run it.

6 Cone

The image above is what you get when you run it. In step 2, we used different mesh material <meshNormalMaterial attach='material'/>. The material releases different colors on each side of the geometric shape based on light reflecting on its surface. Also, observe the lines on each cone edge due to the directional Light.

Let's create a cuboid and add a different material. This time, we will get them from React Three Drei. In your React app, create the following:

  1. Cuboid.js component file.

  2. Write the following code;

import React, {useRef} from 'react'
import {useLoader, useFrame} from '@react-three/fiber'
import { MeshWobbleMaterial } from '@react-three/drei'
function Cuboid() {
  const mesh =useRef(null);
  useFrame(() => (mesh.current.rotation.x = mesh.current.rotation.y += 0.01));

  return (
    <mesh castShadow ref={mesh}>
        <boxBufferGeometry attach='geometry' args={[1,2,1]}/>
        <meshWobbleMaterial attach='material' distort={0.5} color='red' />
    </mesh>
  )
}
export default Cuboid
  1. Next, we work on our App.js component file. In the App.js component file, write the following code.
import React, {Suspense} from 'react';
import './App.css';
import Cuboid from './components/Cuboid';
import { Canvas } from '@react-three/fiber';
import { OrbitControls} from '@react-three/drei';
function App() {
    return (
    <div className="App">
      <Canvas className='canvas1'  shadowMap camera={{ fov: 120 }}>
        <OrbitControls enableZoom={false} />
        <ambientLight intensity={0.5}/>
        <pointLight position={[-10, 0, -20]} intensity={0.5} />
        <pointLight position={[0, -10, 0]} intensity={1.5} />
        <directionalLight 
          position={[-2,5,3]} 
          intensity={1.5} />
        <group>
          <mesh rotation={[-Math.PI / 2, 0, 0]} position = {[0, -3, 0]}>
            <planeBufferGeometry attach='geometry' args={[100, 100]}/>
            <meshStandardMaterial attach='material' color='green'/>
          </mesh>
        </group>
        <Suspense fallbak={null}>
          <Cuboid/>
        </Suspense>
      </Canvas>
    </div>
  );
}
export default App;

The point light in the code above is used to set the light at different points of the 3d object. The image below shows our cuboid.

7 Cuboid

Open Source Session Replay

_OpenReplay is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data._

OpenReplay

Start enjoying your debugging experience - start using OpenReplay for free.

Importing Modules

Creating geometric shapes is fun, but we can also create 3D models using applications like Blender, Maya, Sketchfab, and TurboSquid. In this section, we will run a 3D model using an already-created model from Sketchfab. We will use the GLTF file model. We will also use the GLTF pipeline, and GLTFJSX packages to convert the GLTF file to Draco GLTF and then to a React JSX file. To start first;

  1. Head to Sketchfab and download a free 3D model or a paid one. Note: when downloading the 3D model, select the GLTF format. The file will download as a zip file; you must extract it. The image below shows the contents of the GLTF model file.

8

  1. After downloading, open your terminal using the cmd command and install the GLTF pipeline using npm install -g gltf-pipeline to install it globally.

  2. Next, open the 3D model you downloaded in your terminal and convert the GLTF file to a Draco file gltf-pipeline -i scene.gltf -o model.gltf -d'. Thescene.gltfis in the content of the 3D model, and you have to change themodel.gltfto the name of the model you want. In my case, I usedearth.gltf`.

9

Now it adds the Earth.gltf file to the file contents as in the image above.

  1. We must convert the GLTF file to a React JSX file. To do that, use the command npx gltfjsx earth.gltf. After that, you get a React JSX file like in the image I get an Earth.js component file.

10

  1. Next, we have to copy the Draco GLTF file to the public folder in your React app and the React JSX file to the components folder, as in the image below.

11

  1. Depending on the 3D model you use, if you open the copied JSX file, you get a code almost like the one below. It will contain a group and a mesh geometry and material. It also imports the useGLTF function from React Three Drei. There is a license for the person that created the 3D model, so you have to give credit to the person.
/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
author: PatelDev (https://sketchfab.com/PatelDev)
license: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
source: https://sketchfab.com/3d-models/earth-f7a76c63ff1846afb2d606e5c8369c15
title: Earth
*/

import React, { useRef } from 'react'
import { useGLTF } from '@react-three/drei'
import {useFrame} from '@react-three/fiber'
export default function Model(props) {
  const { nodes, materials } = useGLTF('/earth.gltf')
  const mesh =useRef(null);
  useFrame(() => (mesh.current.rotation.x = mesh.current.rotation.y += 0.01));
  return (
  <mesh ref={mesh}>
    <group {...props} dispose={null}>
      <group rotation={[-Math.PI / 2, 0, 0]}>
        <group rotation={[Math.PI / 2, 0, 0]}>
          <group scale={2.50}>
            <mesh geometry={nodes.Object_4.geometry} material={materials['Scene_-_Root']} />
          </group>
        </group>
      </group>
    </group>
  </mesh>
  )
}

useGLTF.preload('/earth.gltf')
  1. Next, in our App.js file, to run the code, we write the following;
import React, {Suspense} from 'react';
import './App.css';
import Earth from './components/Earth'
import { Canvas } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
function App() {
  return (
    <div className="App">
      <Canvas className='canvas4'>
      <OrbitControls enableZoom={false} />
      <ambientLight intensity={0.5}/>
      <directionalLight position={[-2,5,3]} intensity={1}/>
        <group>
            <mesh receiveShadow rotation={[-Math.PI / 2, 0, 0]} position = {[0, -3, 0]}>
              <planeBufferGeometry attach='geometry' args={[100, 100]}/>
              <meshStandardMaterial attach='material' color='green'/>
            </mesh>
        </group>
      <Suspense fallbak={null}>
        <Earth/>
      </Suspense>
      </Canvas>
    </div>
  );
}

export default App;

Then run the app.

12 3D-earth

The Screencast shows what my 3D model looks like when I run it. So that's it; you can try out many more 3D models to create web apps or games with them.

Conclusion

Creating 3D graphics in React is fun, and it has many uses to learn about and applications. This article aimed to show some basic knowledge or implementation of how to create 3D models and graphics.

Here are the links for reference;

A TIP FROM THE EDITOR: If 3D is too much, try 2D as in our 2D Sketches With React And The Canvas API article.

newsletter