• Stars
    star
    1,240
  • Rank 37,884 (Top 0.8 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 5 years ago
  • Updated almost 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

â—¾ React Vertex | Hooks-based WebGL library for React

React Vertex

Hooks-based WebGL library for React

This library is experimental.

npm install @react-vertex/core

I started working on this to try out hooks and learn more about WebGL. It quickly got out of hand. There's a lot to do still.

Documentation and Examples

All the demos on the site are in the demos folder

What's been built

  • Scene renderer using React Reconciler
  • Scene graph which handles matrix multiplication
  • Basic lighting system (only point lights so far)
  • Orbit camera and controls
  • Hooks for geometries and materials
  • Dat Gui like dev controls and scene helpers

CodeSandbox

Keep in mind these sandboxes run in "development" mode and, just like for React, that makes a significant difference in performance for this library.

Packages:

@react-vertex/core

license bundlephobia npm version

React components, renderer and hooks for React Vertex.

npm install @react-vertex/core

Documentation for Core

@react-vertex/geometry-hooks

license bundlephobia npm version

React hooks for working with geometries in React Vertex.

npm install @react-vertex/geometry-hooks

Documentation for Geometry Hooks

@react-vertex/material-hooks

license bundlephobia npm version

React hooks for working with materials in React Vertex.

npm install @react-vertex/material-hooks

Documentation for Material Hooks

@react-vertex/math-hooks

license bundlephobia npm version

React hooks for working with vectors and matrices in React Vertex. Almost all of this is gl-matrix so this adds very little to your bundle. The core also relies on gl-matrix.

npm install @react-vertex/math-hooks

Documentation for Math Hooks

@react-vertex/orbit-camera

license bundlephobia npm version

React hooks for a basic orbit camera and controls. Almost all of this is gl-matrix so this adds very little to your bundle. The core also relies on gl-matrix.

npm install @react-vertex/orbit-camera

Documentation for Orbit Camera

How does it work?

Inside of a <Canvas /> component you are no longer building an HTML document. Instead, a React Vertex scene is built of four primary elements: <camera>, <group>, <material> and <geometry>. You use the elements to build up the WebGL state used by the renderer.

At its most simple, a scene would look like:

<camera>
  <material>
    <geometry />
  </material>
</camera>

Or something like:

<camera>
  <group>
    <material>
      <group>
        <geometry />
        <geometry />
        <geometry />
      </group>
    </material>
    <material>
      <group>
        <material>
          <geometry />
        </material>
        <geometry />
        <geometry />
      </group>
    </material>
  </group>
</camera>

Of course, you can create your own custom components to build up that document however you like:

<camera>
  <group>
    <Asteroids />
    <Robots />
    {showSharks ? (
      <group>
        <Shark weapon={laserBeam} />
        <Shark weapon={laserBeam} />
      </group>
    ) : null}
  </group>
  <SeaBass illTempered={true} />
</camera>

<camera>

The camera takes just two props that define the view (matrix) and the projection (matrix):

<camera view={view} projection={projection}>
  <material>
    <geometry />
    <geometry />
    <geometry />
  </material>
</camera>

The view and projection should be instances of gl-matrix mat4. If you already know how to work with gl-matrix then you can use whatever method you like to keep those props up to date. You can mutate the matrices and the changes will be reflected in the render.

For convenience you can use @react-vertex/math-hooks to create a static camera view:

import {
  useInvertedMatrix,
  usePerspectiveMatrix,
} from '@react-vertex/math-hooks'

function Scene() {
  const view = useInvertedMatrix(0, 0, 50)
  const projection = usePerspectiveMatrix(35, 1.0, 0.1, 1000)

  ...

  return (
    <camera view={view} projection={projection}>
      ...
    </camera>
  )

Or use @react-vertex/orbit-camera to create a dynamic camera:

import { useOrbitCamera, useOrbitControls } from '@react-vertex/orbit-camera'
import { useCanvasSize } from '@react-vertex/core'

function Scene() {
  const { width, height } = useCanvasSize()

  const camera = useOrbitCamera(55, width / height, 1, 5000)
  useOrbitControls(camera)

  ...

  return (
    <camera view={camera.view} projection={camera.projection}>
      ...
    </camera>
  )

<material>

Right now, the material nodes just take a single program prop. The program is a WebGL program returned from a hook. The nearest <camera> ancestor will define the view and projection. The renderer will set viewMatrix, modelMatrix and projectionMatrix uniforms in the program shaders. You can use @react-vertex/material-hooks for some common programs or look at the source to compose your own custom program hooks. The Phong and Lambert programs in @react-vertex/material-hooks make use of lights in the scene.

import React from 'react'
import PropTypes from 'prop-types'
import { useHex } from '@react-vertex/color-hooks'
import { useSphereElements } from '@react-vertex/geometry-hooks'
import { useBasicSolid } from '@react-vertex/material-hooks'

function Example({ position }) {
  const sphere = useSphereElements(0.75, 10, 10)
  const diffuse = useHex('#ffa500', true)
  const program = useBasicSolid(diffuse)

  return (
    <material program={program}>
      <geometry position={position} {...sphere} />
    </material>
  )
}

Example.propTypes = {
  position: PropTypes.array.isRequired,
}

export default Example

<geometry>

The <geometry> element defines the attributes and several other parameters for drawing. You can also set the position, rotation and scale. The nearest <material> ancestor will define what program is applied to the geometries. Probably, the easiest way to get started is to use the hooks from @react-vertex/geometry-hooks.

import React from 'react'
import { useVector3 } from '@react-vertex/math-hooks'
import { useBoxElements } from '@react-vertex/geometry-hooks'

const PI = Math.PI

function Boxes() {
  const boxElements = useBoxElements(10, 10, 10)

  const r1 = useVector3(PI / 4, PI, 0)

  const p1 = useVector3(10, 0, 0)
  const p2 = useVector3(20, 0, 0)
  const p3 = useVector3(30, 0, 0)
  const p4 = useVector3(40, 0, 0)

  return (
    <group rotation={r1}>
      <geometry position={p1} {...boxElements} />
      <geometry position={p2} {...boxElements} />
      <geometry position={p3} {...boxElements} />
      <geometry position={p4} {...boxElements} />
    </group>
  )
}

To get more control over the geometry buffers and attributes you can use some of the more low-level hooks from the @react-vertex/core:

import React, { Fragment, useMemo } from 'react'
import { useVector3 } from '@react-vertex/math-hooks'
import { useBoxGeometry } from '@react-vertex/geometry-hooks'
import {
  useWebGLContext,
  useStaticBuffer,
  useAttribute,
} from '@react-vertex/core'

function Boxes() {
  const geometry = useBoxGeometry(10, 10, 10)

  // this is what "useBoxElements" does internally...
  const gl = useWebGLContext()

  const positionBuffer = useStaticBuffer(gl, geometry.vertices, false, 'F32')
  const position = useAttribute(gl, 3, positionBuffer)

  const normalBuffer = useStaticBuffer(gl, geometry.normals, false, 'F32')
  const normal = useAttribute(gl, 3, normalBuffer)

  const uvBuffer = useStaticBuffer(gl, geometry.uvs, false, 'F32')
  const uv = useAttribute(gl, 2, uvBuffer)

  const indexBuffer = useStaticBuffer(gl, geometry.indices, true, 'U16')

  const boxElements = useMemo(
    () => ({
      index: indexBuffer,
      attributes: { position, normal, uv },
      drawElements: { mode: 'TRIANGLES', count: geometry.indices.length },
    }),
    [indexBuffer, geometry.indices.length, position, normal, uv],
  )

  const r1 = useVector3(PI / 4, PI, 0)

  const p1 = useVector3(10, 0, 0)
  const p2 = useVector3(20, 0, 0)
  const p3 = useVector3(30, 0, 0)
  const p4 = useVector3(40, 0, 0)

  return (
    <group rotation={r1}>
      <geometry position={p1} {...boxElements} />
      <geometry position={p2} {...boxElements} />
      <geometry position={p3} {...boxElements} />
      <geometry position={p4} {...boxElements} />
    </group>
  )
}

Rendering

By default nothing will be rendered. You can set the renderOnUpdate prop on the Canvas component to true to have it work something like a regular react component tree. If the scene has lots of elements or is animating constantly it's going to make more sense to render it in more controlled way with the useRender hook.

Rendering in a loop

You can get a function to render the scene by calling useRender anywhere in a React Vertex component tree. If your scene is animating constantly, it's probably best to have one loop right at the root of the tree that renders on each frame. You can use d3-timer to create a loop like so:

import React, { useEffect } from 'react'
import { timer } from 'd3-timer'
import { useRender } from '@react-vertex/core'

function Scene() {
  const renderScene = useRender()

  useEffect(() => {
    const timerLoop = timer(renderScene)
    return () => timerLoop.stop()
  }, [renderScene])

  ...

Rendering when camera updates

If you want to render when the camera moves. You can do something like the below example. If you look at the "Tuna Wireframe" example, it updates when the camera changes and ALSO sets the renderOnUpdate prop on the canvas to true to make sure it renders when the controls in the scene update. If you are creating more of an app that has less frequent state updates and mainly responding to user input that's a pretty efficient way to approach it.

import React, { useEffect } from 'react'
import { useRender, useCanvasSize } from '@react-vertex/core'
import { useOrbitCamera, useOrbitControls } from '@react-vertex/orbit-camera'

function Scene() {
  const { width, height } = useCanvasSize()

  const renderScene = useRender()

  const camera = useOrbitCamera(55, width / height, 1, 5000)
  useOrbitControls(camera)

  useEffect(() => {
    renderScene()
    camera.addListener(renderScene)
    return () => camera.removeListener(renderScene)
  }, [camera, renderScene])

  ...

Running the repo locally

You must have Yarn (currently version 1.22.4) installed to run the repo.

git clone [email protected]:sghall/react-vertex.git
cd react-vertex
yarn install
npx lerna bootstrap
npx lerna run docs:dev

Go to http://localhost:3000/

More Repositories

1

react-move

React Move | Beautiful, data-driven animations for React
JavaScript
6,585
star
2

resonance

â—¾Resonance | 5kb React animation library
JavaScript
1,005
star
3

react-compound-slider

â—¾ React Compound Slider | A small React slider with no opinion on markup or styles
TypeScript
626
star
4

d3-threejs

CSS 3D Transforms with D3 and THREE.js
JavaScript
304
star
5

threejs-maps

Interactive Maps with D3.js, Three.js and Mapbox
JavaScript
97
star
6

react-d3-transitions

Enter, update and exit pattern using React 15.0, D3 4.0 and Redux
JavaScript
85
star
7

subunit

A small library that gives you D3 style selections in THREE.js.
JavaScript
73
star
8

webgl-globes

JavaScript
64
star
9

d3-chord-diagrams

Example Chord Diagrams in D3
HTML
49
star
10

d3-graphs

An Interactive Force Directed Graph in D3.js
JavaScript
45
star
11

famous-chart-demos

Examples integrating D3.js with Famo.us
JavaScript
43
star
12

chord-transitions

Transitioning Chord Diagram Demo with Angular/D3
JavaScript
39
star
13

d3-multi-series-charts

Some Example Multi-Series Charts in D3.js
JavaScript
26
star
14

last-fm

A demo app using D3 and AngularJS Directives
JavaScript
19
star
15

bundle-inspector-webpack-plugin

Bundle Inspector | Analysis Tool for Webpack
JavaScript
18
star
16

angular-d3

Demo app using Angular, D3, and Neo4j.
JavaScript
13
star
17

physijs-dom-renderer

Demo of JavaScript physics engine simulation with DOM elements. D3.js used to generate HTML.
JavaScript
7
star
18

dragon-curves

JavaScript
5
star
19

chaos-game

JavaScript
5
star
20

javascript-object-instantiation

JavaScript
5
star
21

react-quickie-controls

Quick development controls for React projects. Change values and colors quickly in your development environment or storybooks.
TypeScript
5
star
22

web-scraper

Some node scripts for scraping movie data from the web.
JavaScript
4
star
23

chart-physics

Experiments with Three.js, D3.js and Physics Engines
JavaScript
4
star
24

kapellmeister

Orchestration for Animated Transitions
JavaScript
4
star
25

neo4j-loader

A simple Node module to bulk load data into Neo4j
JavaScript
2
star
26

webgl-flux-chat

JavaScript
2
star
27

bungalow

JavaScript
1
star
28

antigen

JavaScript
1
star