Three.js (fiber & drei) - 3D web3 serie
Tags: web3, three, javascript, vite
Published: 2022-07-28 13:16:00

3D web3 Series
This is the 2nd post of 3D-web3 Series.

1 - [Vite config and basic three.js](https://dev.to/uigla/setting-up-3d-website-using-threejs-fiber-react-web3-and-vite-as-bundler-f1f)
**2 - Three.js (fiber & drei)**
3 - [Cannon physics](https://dev.to/uigla/3d-web-cannon-physics-web3-serie-24jh)
4 - [Web3](https://dev.to/uigla/3d-web-react-web3-44e7)

Hey mate,

Drei pacakge is providing us ready-made abstractions for "Fiber"
Is a collection of useful helpers and fully functional DOM elements

`npm i @react-three/drei`

> ["Drei docs"](https://github.com/pmndrs/drei) has usseful index to check which functionalities are already available.

We'll be using:

- From "Fiber": Canvas, useThree, useFrame
- From "Drei": useGLTF, CameraShake, OrbitControls, Stars and Html
- From "Three": Vector3

This post is divided in the following steps:
1_ Styling
2_ App "Canvas" with all the components
3_ Drei elements to create a background "sky", "stars"
4_ Importing 3D model into "canvas" (using "Suspense" and creation of ilumination)
5_ Creating two different "camera" uses

**Step1_** Define "Canvas" viewport in App.css

```css
.canvas {
  height: 100vh;
  width: 100vw;
}
```

**Step2_ Implement "Canvas"** element imported from "fiber".
Comments in Code

```javascript
import { Suspense, useState } from 'react';
import { Canvas } from '@react-three/fiber';
import { CameraShake, OrbitControls, Sky, Html } from '@react-three/drei';

import './App.css';
import { Lights } from './lights/Lights';
import CameraRig from './camera/CameraRig';
import Stars from './stars/Stars';
import GltfLoader from './gltf/GltfLoader';

const App = () => {

	const [cameraMode, setCameraMode] = useState(true);
	const [move, setmove] = useState(undefined);

	return (
		<div className='row'>
			<div className='col-lg-12 canvas'>
				<Canvas
					camera={{
						position: [0, 0, 0],
						rotation: [0, 0, 0],
					}}>
					{/* Details in "Stars" component */}
					<Stars />
					{/* Drei component, creates a sky background (does not give ilumination to canvas elements) */}
					<Sky distance={450000}
						sunPosition={[2, 1, 10]}
						inclination={0}
						azimuth={0.25} />

					{/* While importing gltf model, we will cath it in a "Suspense" */}
					<Suspense fallback={null}>
						{/* Our "GltfLoader" component takes a "position" and "cameraMove" setter to switch between two different camera controls*/}
						<GltfLoader url={'chair/armchairYellow.gltf'} position={[0, -5, -15]} setCameraMode={() => { setCameraMode(!cameraMode) }} />
					</Suspense>
					{/* Several lights types are implemented in canvas */}
					<Lights />

					{/* switch between two different camera controls */}
					{cameraMode ? <><OrbitControls
						enableZoom={true}
						enablePan={true}
						enableRotate={true}
					/>
						{/* HTML is added only in Orbitcontrols, because creates a conflict using with "CameraShake" or "CameraRig" */}
						<Html
							as='h4'
							position={[0, 0, -15]}
							transform={true}
						>
							<h4 className="htmlcanvas" >Click to switch between two different camera controls</h4>
						</Html>
					</>
						:
						<>
							{/* Drei component, creates a balanced movement in our "camera" position */}
							<CameraShake
								yawFrequency={0.1}
								pitchFrequency={0.1}
								rollFrequency={0.1}
							/>
							{/* Using "CameraRig", "camera" position reacts to our mouse movement */}
							<CameraRig setmove={setmove} />
						</>
					}
				</Canvas>
			</div>
		</div>
	);
}

export default App
```

**Step3_** "sky", "stars" Drei elements to create a background 

**Stars.jsx**
**useRef** to attach to "mesh" which is a built-in "DOM element" by fiber, which allows to manipulate the object inside.

```javascript
import { Stars } from '@react-three/drei';
import { useFrame } from '@react-three/fiber';
import { useRef } from 'react';

const StarsBG = () => {

	const ref = useRef();
	const ref2 = useRef();

	useFrame(() => {
		ref.current.rotation.y += 0.001
		ref2.current.rotation.y -= 0.0005
	})

	return (
		<>

			<Stars
				ref={ref}
				radius={4}
				depth={100}
				count={5000}
				factor={3}
				saturation={0.9}
				fade
				speed={.1}
				color={3}
			/>
			<Stars
				ref={ref2}
				radius={2}
				depth={200}
				count={5000}
				factor={3}
				saturation={0.9}
				fade
				speed={.2}
				color={3}
			/>
		</>
	);
}

export default StarsBG;


```

**Sky** component is already defined in our "Canvas"

```javascript
<Sky distance={450000}
						sunPosition={[2, 1, 10]}
						inclination={0}
						azimuth={0.25} />
```

**Step4_** Importing 3D model into "canvas" (using "Suspense" and creation of ilumination)

We'll be using "useGLTF" from "drei". Provide a url and "/draco-gltf" as parameters and define it in a "primitive" element

**GltfLoader.jsx**

Pass onClick atribute to call setCameraMode method. To switch between two different camera controls (explained in step5)

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

function GltfLoader(props) {
  const gltf = useGLTF(props.url, "/draco-gltf");
  useGLTF.preload(props.url);
  return (
    <primitive
      scale={0.05}
      object={gltf.scene}
      dispose={null}
      position={props.position}
      onClick={props.setCameraMode}
    />
  );
}
export default GltfLoader;

```

> Wrap it using "Suspense" to handle the process while importing gltf model

Now, create a ilumination using several fiber elements.
Use "useRef" to give rotation to the ilumination using "useFrame"

Lights.jsx

```javascript
import { useFrame } from '@react-three/fiber';
import { useRef } from 'react'

export const Lights = () => {

	const ref = useRef();
	useFrame(() => {
		// give rotation to the ilumination
		ref.current.rotation.y -= 0.005
	})

	return (
		<group
			ref={ref}>
			{/* Ambient Light illuminates lights for all objects */}
			<ambientLight intensity={0.3} />
			{/* Diretion light */}
			<directionalLight position={[10, 10, 5]} intensity={1} />
			<directionalLight
				castShadow
				position={[0, 10, 0]}
				intensity={1.5}
				shadow-mapSize-width={1024}
				shadow-mapSize-height={1024}
				shadow-camera-far={50}
				shadow-camera-left={-10}
				shadow-camera-right={10}
				shadow-camera-top={10}
				shadow-camera-bottom={-10}
			/>
			{/* Spotlight Large overhead light */}
			<spotLight intensity={1} position={[1000, 0, 0]} castShadow />
		</group>
	);
};

```

**Step5_** Creating two different "camera" uses
We're going to use to states:
1_ move (to set camera movement each frame)
2_ cameraMode (to switch between two different camera controls)

> Using if else in one line to switch mode
`var variable = (condition) ? (true option1) : (else option2)`

**Option1_** we'll be using following elements from "drei" in our "Canvas": 
"OrbitControls" - Give us camera control: rotation (mouse left botton) and position (mouse right botton)  
"Html" - Enable us using basic DOM Nodes to provide a text

> "Html" creates a conflict if we use with "CameraShake" or "CameraRig". That's why we are only showing text in option1
 
**Option2_** we'll be using following elements:
"CameraShake" from "drei" - creates a balanced movement in our "camera" Vector3
"CameraRig" (created by us). But, first check the code.

> This code is already in App.jsx "canvas" element

```javascript
<Canvas>
...code
{cameraMove ? <><OrbitControls
						enableZoom={true}
						enablePan={true}
						enableRotate={true}
					/>
						{/* HTML is added only in Orbitcontrols, because creates a conflict using with "CameraShake" or "CameraRig" */}
						<Html
							as='h4'
							position={[0, 0, -15]}
							transform={true}
						>
							<h4 className="htmlcanvas" >Click to switch between two different camera controls</h4>
						</Html>
					</>
						:
						<>
							{/* Drei component, creates a balanced movement in our "camera" position */}
							<CameraShake
								yawFrequency={0.1}
								pitchFrequency={0.1}
								rollFrequency={0.1}
							/>
							{/* Using "CameraRig", "camera" position reacts to our mouse movement */}
							<CameraRig setmove={setmove} />
						</>
					}
				</Canvas>
```

Last, use "lerp" algorithm to do a linear interpolation in our camera "Vector3" imported from "three"

This code, allow us to move our camera mimicing our mouse movement

CameraRig.js

```javascript
import { useFrame, useThree } from "@react-three/fiber";
import { Vector3 } from "three";

// Using "CameraRig", "camera" position reacts to our mouse movement
function CameraRig(props) {
  const vec = new Vector3();
  const { camera, mouse } = useThree();
  useFrame(() => {
    // using "lerp" algorithm to do a linear interpolation between three variables (x,y,z) and  given a fraction (0.05 in this case)
    camera.position.lerp(vec.set(mouse.x * 20, mouse.y * 20, mouse.z), 0.05);
    props.setmove(camera.position);
  });
}

export default CameraRig;

```

Resume of components: App, Stars, GltfLoader, Lights, CameraRig

> Code in [sandBox](https://codesandbox.io/s/three-react-part-2-g63hqd?file=/src/App.jsx)
> Does not render due to an issue of codeSandBox "ide".
> error: Unexpected token < in JSON at position 0
> For more info, check in [stackoverslow](https://stackoverflow.com/questions/70736883/gltfloader-js-typeerror-failed-to-fetch-three-js) or this [post](https://daveceddia.com/unexpected-token-in-json-at-position-0/)

I've deleted gltf model in order to show a quick pre-view

{% embed https://codesandbox.io/embed/three-react-part-2-g63hqd?fontsize=14&hidenavigation=1&theme=dark %}


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bxdekoptx9h7tkm55aco.png)

Web3 will be added in next post

I hope it has been helpful.