logo

React Three Fiber and Shaders Intro

In this tutorial, we'll explore how to create a 3D cube with a custom shader material using React Three Fiber and Three.js.

Introduction

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.

Prerequisites

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.

Setting Up the Project

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

Creating a Basic 3D Scene with a Cube

Step 1: Setting Up the Box Component

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
Explanation:
  • useRef: A React hook that persists values between renders without causing re-renders.
  • useFrame: A hook from React Three Fiber that allows you to execute code on every rendered frame.
  • OrbitControls: A helper from @react-three/drei that allows orbiting around the object using mouse or touch controls.
  • mesh: Represents the 3D object.
  • boxGeometry: Defines the geometry of the cube.
  • meshStandardMaterial: A material with standard shading.

Step 2: Rendering the Box Component

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
Explanation:
  • Canvas: A component from React Three Fiber that creates a Three.js rendering context.
  • camera: Sets the initial camera position.

Add css in index.css

1#root {
2width: 100vw;
3height: 100vh;
4}
5body {
6margin: 0;
7background-color: #f6f6ff;
8}

Step 3: Running the Project

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.

Adding Custom Shaders to the Cube

Step 4: Defining the Shader Material

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 })
Explanation:
  • shaderMaterial: A utility from drei to create custom shaders in React Three Fiber.
  • Vertex Shader: Transforms vertices and passes UV coordinates to the fragment shader.
  • Fragment Shader: Colors pixels based on a dynamic pattern using trigonometric functions and a uniform variable u_time.

Step 5: Applying the Shader Material to the Cube

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

Explanation:
  • <myShaderMaterial attach="material" />: This line uses the extended custom shader material component (MyShaderMaterial) in place of a standard material.
  • materialRef: A reference to the shader material.
  • materialRef.current.uniforms.u_time.value: Updates the u_time uniform with the elapsed time, creating an animation effect.
  • meshRef.current.rotation: Continues to rotate the cube on each frame.

Step 6: Running the Project with Shaders

Restart your React project:

1npm run dev

You should now see a rotating cube with an animated shader pattern.

Conclusion

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!

Let's Connect

Copyright 2023 © Sandeep