3D web3 Series
This is the 2nd post of 3D-web3 Series.
1 - Vite config and basic three.js
2 - Three.js (fiber & drei)
3 - Cannon physics
4 - Web3
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" 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
.canvas {
height: 100vh;
width: 100vw;
}
Step2_ Implement "Canvas" element imported from "fiber".
Comments in Code
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.
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"
<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)
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
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
<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
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
Does not render due to an issue of codeSandBox "ide".
error: Unexpected token < in JSON at position 0
For more info, check in stackoverslow or this post
I've deleted gltf model in order to show a quick pre-view
Web3 will be added in next post
I hope it has been helpful.
Top comments (1)
Good Job !