In this tutorial, we'll explore how to create a 3D cube with a custom shader material using React Three Fiber and Three.js.
React Three Fiber is a powerful library that brings the capabilities of Three.js to the React ecosystem. It allows us to create complex 3D graphics using the declarative nature of React. By combining it with custom shaders, we can achieve stunning visual effects that are both performant and visually appealing.
To follow along with this tutorial, you should have a basic understanding of React and some familiarity with Three.js. If you're new to shaders, just check out this blog - https://sandeep-blog-eight.vercel.app/blogs/resources-for-learning-three-js-and-shaders , and get basic understanding of shaders from any tutorial.
First, let's set up a new React project. You can use Vite for this project.
1npm create vite@latest react-three-shaders
2cd react-three-shaders
3npm install
4npm install @react-three/fiber @react-three/drei three
Create a new file named Box.jsx
for our 3D cube component.
1// Box.jsx
2
3import React, { useRef } from 'react';
4import { Canvas, useFrame } from '@react-three/fiber';
5import { OrbitControls } from '@react-three/drei';
6
7const Box = () => {
8 const meshRef = useRef();
9
10 // Rotate the cube on each frame
11 useFrame(() => {
12 if (meshRef.current) {
13 meshRef.current.rotation.x += 0.01;
14 meshRef.current.rotation.y += 0.01;
15 }
16 });
17
18 return (
19 <>
20 <OrbitControls />
21 <mesh ref={meshRef}>
22 <boxGeometry />
23 <meshNormalMaterial />
24 </mesh>
25 </>
26 );
27};
28
29export default Box;
30
@react-three/drei
that allows orbiting around the object using mouse or touch controls.Now, we'll set up our main App
component to render the Box
component inside a Canvas
.
1// App.jsx
2import React from 'react'
3import { Canvas } from '@react-three/fiber'
4import Box from './Box'
5
6const App = () => {
7 return (
8 <Canvas camera={{ position: [0, 0, 2.5] }}>
9 <Box />
10 </Canvas>
11 )
12}
13
14export default App
Add css in index.css
1#root {
2width: 100vw;
3height: 100vh;
4}
5body {
6margin: 0;
7background-color: #f6f6ff;
8}
To see the result, start your React project:
1npm run dev
You should see a rotating cube like above. Now, let's enhance it by adding custom shaders.
We'll define a custom shader material using the shaderMaterial
utility from @react-three/drei
.
Update Box.js
to include the shader material.
1// Box.js
2import React, { useRef } from 'react'
3import { Canvas, useFrame, extend } from '@react-three/fiber'
4import { OrbitControls, shaderMaterial } from '@react-three/drei'
5
6// Define the custom shader material
7const MyShaderMaterial = shaderMaterial(
8 { u_time: 0 },
9 // vertex shader
10 /* glsl */`
11 varying vec2 vUv;
12 uniform float u_time;
13
14 void main() {
15 vUv = uv;
16 gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
17 }
18 `,
19 // fragment shader
20 /* glsl */`
21 varying vec2 vUv;
22 uniform float u_time;
23
24 void main() {
25 vec2 p = vUv * 4.0 - 2.0;
26 float x = p.x;
27 float y = p.y;
28 float mov0 = x + sin(y * 1.0 + u_time * 2.0) * 2.0;
29 float mov1 = y + cos(x * 0.5 + u_time * 3.0) * 2.0;
30 float mov2 = x + sin(y * 2.0 + u_time * 4.0) * 1.0;
31 float mov3 = y + cos(x * 1.0 + u_time * 5.0) * 1.0;
32 float result = sin(mov0 + mov1 + mov2 + mov3);
33 gl_FragColor = vec4(result, result, result, 1.0);
34 }
35 `
36)
37
38// Extend the shader material so it can be used as a React component
39extend({ MyShaderMaterial })
u_time
.Now, we need to use the custom shader material in our Box
component.
1const Box = () => {
2 const meshRef = useRef()
3 const materialRef = useRef()
4
5 useFrame((state) => {
6 const { clock } = state
7 if (materialRef.current) {
8 materialRef.current.uniforms.u_time.value = clock.getElapsedTime()
9 }
10 if (meshRef.current) {
11 meshRef.current.rotation.x += 0.01
12 meshRef.current.rotation.y += 0.01
13 }
14 })
15
16 return (
17 <>
18 <OrbitControls />
19 <mesh ref={meshRef}>
20 <boxGeometry />
21 <myShaderMaterial ref={materialRef} />
22 </mesh>
23 </>
24 )
25}
26
27export default Box
<myShaderMaterial attach="material" />
: This line uses the extended custom shader material component (MyShaderMaterial
) in place of a standard material.u_time
uniform with the elapsed time, creating an animation effect.meshRef.current.rotation
: Continues to rotate the cube on each frame.Restart your React project:
1npm run dev
You should now see a rotating cube with an animated shader pattern.
In this tutorial, we've seen how to use React Three Fiber and custom shaders to create a dynamic 3D scene in a React application. Shaders provide immense flexibility and power for creating stunning visuals, and integrating them with React Three Fiber opens up many possibilities for creative projects.
Feel free to experiment further and share your creations!