Pmndrs.docs

How does it work?

This is an advanced guide on the inner workings of Fiber, if you are just getting started, take a look at our introduction!

React Three Fiber is a React renderer for three.js.

This means that each Fiber component will effectively create a new THREE object that will be added to a scene. Understanding how this works is not necessarily needed to use Fiber, but it will better arm you to deal with anything that you might need in your projects, reading other people's Fiber code and even help you contribute.

Let's take a small React example:

import { Canvas } from '@react-three/fiber'

function MyApp() {
  return (
    <Canvas>
      <group>
        <mesh>
          <meshNormalMaterial />
          <boxBufferGeometry args={[2, 2, 2]} />
        </mesh>
      </group>
    </Canvas>
  )
}

In three.js, this is equivalent to:

import * as THREE from 'three'

const scene = new THREE.Scene() // <Canvas>

const group = new THREE.Group() // <group>

const mesh = new THREE.Mesh() // <mesh />
const material = new THREE.MeshNormalMaterial() // <meshNormalMaterial />
const geometry = new THREE.BoxBufferGeometry(2, 2, 2) // <boxBufferGeometry />

mesh.material = material
mesh.geometry = geometry

group.add(mesh)
scene.add(group)

Our Canvas element will create a new scene, and Fiber will instantiate new objects for each component and correctly compose them together in a scene graph!

Additionally, Fiber will: - Setup a new perspective camera at [0, 0, 0] and set it as default - Setup a **render loop** with automatic render to screen - Setup pointer events via raycasting on all meshes with `onPointer*` props - Setup tone mapping - Automatically handle window resize

Let's break this down!

Creating THREE objects

In three.js, we can create new object using the classic JS API:

const myBox = new THREE.BoxBufferGeometry(1, 2, 3)

Object creation is handled transparently by the Fiber renderer, the name of the constructor BoxBufferGeometry is equivalent to the camel case component <boxBufferGeometry />, while the constructor arguments - in our example [1, 2, 3] - are passed via the args prop:

<boxBufferGeometry args={[1, 2, 3]} />
Note that the object will be created only when first adding the component the React three!

The attach props

Fiber always tries to correctly infer the relationship between components and their parents, for example:

<group>
  <mesh />
</group>

Here, we always know that a group can only have children, so Fiber just calls the add method on the group:

group.add(mesh)

For meshes and other three.js objects, rules can be different. Looking at the three.js documentation, we can see how a THREE.Mesh object is constructed using a material and a geometry.

With the attach prop, we can precisely tell the renderer what property to attach each component to:

<mesh>
  <meshNormalMaterial attach="material" />
  <boxBufferGeometry attach="geometry" />
</mesh>

This will explicitly tell Fiber to render like this:

mesh.material = new THREE.MeshNormalMaterial()
mesh.geometry = new THREE.BoxBufferGeometry()

As you can see, the attach prop is telling Fiber to set the parent's material property to a reference to our <meshNormalMaterial /> object.

Note that while we used geometry and material for this example, Fiber also infers the attach property from the constructor name, so anything with `material` or `geometry` will automatically get attached to the correct property of its parent.

The same is true for attachArray which, instead of setting the property, will push to an existing array:

<effectComposer>
  <renderPass attachArray="passes" scene={scene} camera={camera} />
  <glitchPass attachArray="passes" renderToScreen />

Props

With Fiber, you can pass any three.js property as a React property, and it will be assigned to the constructed object:

<meshBasicMaterial color="red" />

is equivalent to:

const material = new THREE.MeshBasicMaterial()
material.color = 'red'

Fiber will check the type of the property value and either:

  • assign the new value directly
  • if the value is an object with a set method, call that
  • construct a new object if needed.
  • convert between formats
<mesh scale={[1, 2, 3]} />

is equivalent to:

const mesh = new THREE.Mesh()
mesh.scale = new THREE.Vector3(1, 2, 3)

// on update, it will instead `set()` the vector
mesh.scale.set(3, 4, 5)

Pointer Events

Pointer Events are transparently handled by Fiber. On startup, it will create a raycaster for mouse picking.

Every object with onPointer props will be added to the array of objects checked every frame by the raycaster:

<mesh onPointerDown={console.log}>...</mesh>

The ray's origin and direction are updated every time the mouse moves on the <Canvas /> element or the window is resized. Fiber also handles camera switching, meaning that the raycaster will always use the currently active camera.

When using the raycast prop, the object will instead be picked using a custom ray:

import { useCamera } from '@react-three/fiber'

return <mesh raycast={useCamera(anotherCamera)} />

Render Loop

By default, Fiber will setup a render loop that renders the default scene from the default camera to a WebGLRenderer.

The loop is setup using setAnimationLoop, which will execute its callback every time a new frame is renderable. This is what will happen every render:

  1. All global before effects are executed
  2. Clock delta is saved - implying all useFrame calls will share the same delta
  3. useFrame callbacks are executed in order
  4. renderer.render(scene, camera) is called, effectively rendering the scene to screen
  5. All global after effects are executed

Add about opting out of render