mvilledieu / shadertoy-react

6kB "Shadertoy" like react component letting you easily render your fragment shaders in your React web projects, without having to worry about implementing the WebGL part.
https://codesandbox.io/s/434qm4x4w0
MIT License
124 stars 12 forks source link
canvas creative-coding fragment-shader glsl pixel-shader react shader-effects shaders shadertoy

shadertoy-react :art:

npm version Build Size MIT Licence

Shadertoy like React Component

Small react component letting you easily add responsive full canvas shader to your React page. shadertoy-react supports shadertoy glsl syntax, but also the classic glsl syntax, so you can easily build your shader in shadertoy leveraging the live reload functionality and copy past it to your React app once you are done without having to worry about converting the syntax to the classic glsl syntax.

shadertoy-react also gives you access to almost all the built in uniforms existing on shadertoy plus some new ones like for example the gyroscope data of your phone etc. Start writting code using any of these built in uniforms without having to worry about passing anything to the shader, shadertoy-react takes care of all that for you. Also, you can still pass customs uniforms as a prop if you actually need some more flexibility.

You could for example use postprocessing on images and videos, raytracing, raymarching, etc... the limitation are yours, now you have everything you need to focus on the shader art itself.

The way it works

Same as the Shadertoy implementation. Basically it uses WebGL on a <canvas/> and render a material on a full viewport quad composed of 2 triangles. The canvas size matches the css size of your element, by default it it 100% 100% of your parent element size, this can be changed by passing a custom style={} prop to your component. It is also making sure that anything that is not used in your shader is not being processed in the JS side to avoid useless event listeners, etc. so if you don't use the iMouse uniform the mouse event listener will not be activated and the iMouse uniform will not be added and passed to your shader.

Playground

Try shadertoy-react on codesandbox to get a taste of the functionalities.

How to use it

Basic example:

Example of the simplest React Component using shadertoy-react:

import React from  'react';
import { render} from  'react-dom';
import ShadertoyReact from 'shadertoy-react';

const ExampleApp = () =>
  <Container>
    <ShadertoyReact fs={fs}/>
  </Container>;

Example of working shader with shadertoy like syntax:

void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
  vec2 uv = fragCoord.xy/iResolution.xy;
  vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));
  fragColor = vec4(col,1.0);
}

Example of working shader with classic GLSL syntax:

void main(void) {
  vec2 uv = gl_FragCoord.xy/iResolution.xy;
  vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));
  gl_FragColor = vec4(col,1.0);
}

Available props

Here are a few built in react props you can pass to your component. Feel free to suggest more.

Uniforms

Shadertoy's Built-in:

Built in uniforms are uniforms that are being passed automatically to your shader without having you doing anything. You can start using every single one of them without having to do anything. We are taking care of that for you.

shadertoy-react's only built-in:

Custom uniforms:

Shadertoy React now supports adding your owns uniforms by passing an uniforms props containing uniforms objects. Here is a list of the supported uniforms and their respective formats. Note: If you are whiling to pass multiple Vectors, Matrices, Ints, Floats, make sure to pass flat arrays as shown below.

Type GLSL Type Uniforms values in JS Read in GLSL
1f float val uValue
2f vec2 [x, y] uValue.xy
3f vec3 [x, y, z] uValue.xyz
4f vec4 [x, y, z, w] uValue.xyzw
1fv float or float array val or [val, val, ...] uValue or uValue[n]
2fv vec2 or vec2 array [x, y] or [x, y, x, y, ...] uValue.xy or uValue[n].xy
3fv vec3 or vec3 array [x, y, z] or [x, y, z, x, y, z, ...] uValue.xyz or uValue[n].xyz
4fv vec4 or vec4 array [x, y, z, w] or [x, y, z, w, x, y, z, w ...] uValue.xyzw or uValue[n].xyzw
1i int val uValue
2i ivec2 [x, y] uValue.xy
3i ivec3 [x, y, z] uValue.xyz
4i ivec4 [x, y, z, w] uValue.xyzw
1iv int or int array val or [val, val, val, ...] uValue or uValue[n]
2iv ivec2 or ivec2 array [x, y] or [x, y, x, y, ...] uValue.xy or uValue[n].xy
3iv ivec3 or ivec3 array [x, y, z] or [x, y, z, x, y, z, ...] uValue.xyz or uValue[n].xyz
4iv ivec4 or ivec4 array [x, y, z, w] or [x, y, z, w, x, y, z, w ...] uValue.xyzw or uValue[n].xyzw
Matrix2fv mat2 or mat2 array [m00, m01, m10, m11] or [m00, m01, m10, m11, m00, m01, m10, m11 ...] uValue[0->1][0->1] or uValue[n][0->1][0->1]
Matrix3fv mat3 or mat3 array [m00, m01, m02, m10, m11, m12, m20, m21, m22] or [m00, m01, m02, m10, m11, m10, m12, m20, m21, m22, m00, m01, m02, m10, m11, m10, m12, m20, m21, m22 ...] uValue[0->2][0->2] or uValue[n][0->2][0->2]
Matrix4fv mat4 or mat4 array [m00, m01, m02, m03, m10, m11, m10, m12, m20, m21, m22, m30, m31, m32, m33] or [m00, m01, m02, m03, m10, m11, m10, m12, m20, m21, m22, m30, m31, m32, m33, m00, m01, m02, m03, m10, m11, m10, m12, m20, m21, m22, m30, m31, m32, m33 ...] uValue[0->3][0->3] or uValue[n][0->3][0->3]

How to do it:

import React from  'react';
import { render} from  'react-dom';
import ShadertoyReact from 'shadertoy-react';

const ExampleApp = () => {
  const { scrollY } = this.state;

  const uniforms = {
    uScrollY : {type: '1f', value: scrollY }, // float
    uTestArrayFloats : {type: '1fv', value: [0.2, 0.4, 0.5, 0.5, 0.6] }, // Array of float
    uTestArrayVecs2 : {type: '2fv', value: [0.2, 0.4, 0.5, 0.5] }, // 2 vec2 passed as a flat array
    uTestMatrix : {
        type: 'Matrix2fv', 
        value: [0., 1., 2., 3.] // 2x2 Matrix 
    }
  };

  return  
    (<Container>
      <ShadertoyReact
        fs={fs}
        uniforms={uniforms}
      />
    </Container>);
}

Example of shader you could write using these custom uniforms:

  void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
    // You can then directly use uScrollY, uTestMatrix, uTestArrayFloats without having to worry about anything else.
    gl_FragColor = vec4(uScrollY, uTestMatrix[0][0], uTestArrayFloats[0], uTestArrayVecs2[0].xy); 
  }

Working with textures:

By default shadertoy-react lets you pass an array of texture object, shadertoy-react takes care of loading the textures for you. A callback is available and called once all the textures are done loading. Each texture gets a uniform name iChannel(n) following the same order that in the prop passed to the react component, you can then directly use iChanel(n) in your shader.

import React from  'react';
import { render} from  'react-dom';
import ShadertoyReact, { LinearFilter, RepeatWrapping } from 'shadertoy-react';

const ExampleApp = () =>
  <Container>
    <ShadertoyReact
      fs={fs}
      textures={[
        { url: './mytexture.png' },
      ]}
    />
  </Container>;

In your shader you can directly do for example:

void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
  vec2 uv = fragCoord.xy / iResolution.xy;
  vec4 texture = texture(iChannel0, uv);
  gl_FragColor = texture;
}
Texture Filtering:

By default all of your textures are being squared if they aren't, then the default Texture Filtering and Wrapping are being applied to them, using shadertoy-react you can apply your own filters. shadertoy-react contains all the WebGL texture filtering constants and texture wrapping constants. So you can easily import them in your code and make sure to pass the right one to your texture options.

Example of optionnal texture related imports:

import ShadertoyReact, {
  NearestFilter,
  LinearFilter,
  NearestMipMapNearestFilter,
  LinearMipMapNearestFilter,
  NearestMipMapLinearFilter,
  LinearMipMapLinearFilter,
  ClampToEdgeWrapping,
  MirroredRepeatWrapping,
  RepeatWrapping,
} from 'shadertoy-react';

Example of usage of optionnal texture filtering:

import React from  'react';
import { render} from  'react-dom';
import ShadertoyReact, { LinearFilter, RepeatWrapping } from 'shadertoy-react';

const ExampleApp = () =>
  <Container>
    <ShadertoyReact
      fs={fs}
      textures={[
        { url: ..., minFilter: LinearFilter, magFilter: LinearFilter, wrapS: RepeatWrapping, wrapT: RepeatWrapping },
        { url: ..., minFilter: LinearFilter, magFilter: LinearFilter, wrapS: RepeatWrapping, wrapT: RepeatWrapping },
        { url: ..., minFilter: LinearFilter, magFilter: LinearFilter, wrapS: RepeatWrapping, wrapT: RepeatWrapping },
      ]}
    />
  </Container>;

What's next ordered by priority