├── .babelrc
├── .editorconfig
├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── .npmrc
├── LICENCE.txt
├── README.md
├── examples
├── dist
│ ├── bundle.js
│ ├── bundle.js.LICENSE.txt
│ └── index.html
└── src
│ ├── index.html
│ ├── index.jsx
│ └── shaders
│ ├── classicSyntax.js
│ ├── clock.js
│ ├── customUniforms.js
│ ├── deviceorientation.js
│ ├── deviceorientation2.js
│ ├── fs.js
│ ├── fsImages.js
│ ├── keyboard.js
│ └── mouse.js
├── lib
├── shadertoy-react.js
└── shadertoy-react.min.js
├── package-lock.json
├── package.json
├── src
├── Texture.js
├── index.jsx
├── prefixLogs.js
└── uniformsType.js
├── webpack.common.js
└── webpack.lib.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-flow",
4 | "@babel/react",
5 | "@babel/preset-env"
6 | ],
7 | "plugins": [
8 | "@babel/plugin-proposal-class-properties",
9 | "@babel/plugin-proposal-object-rest-spread",
10 | "@babel/plugin-transform-react-jsx"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 | [*.js]
3 | indent_style = space
4 | indent_size = 2
5 | charset = utf-8
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | registry='https://registry.npmjs.org'
--------------------------------------------------------------------------------
/LICENCE.txt:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2018 Morgan Villedieu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | shadertoy-react :art:
2 | ==============
3 |
4 | [](https://badge.fury.io/js/shadertoy-react)
5 | [![Build Size][build-size]][build-size-url]
6 | [](https://opensource.org/licenses/mit-license.php)
7 |
8 | #### Shadertoy like React Component ####
9 |
10 | 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.
11 |
12 | `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.
13 |
14 | 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.
15 |
16 | ## The way it works
17 |
18 | Same as the Shadertoy implementation. Basically it uses WebGL on a ` ` 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.
19 |
20 | ### Playground
21 |
22 | Try `shadertoy-react` on codesandbox to get a taste of the functionalities.
23 |
24 | * [Basic](https://codesandbox.io/s/ojllzxvww6)
25 | * [Demos](https://codesandbox.io/s/434qm4x4w0)
26 |
27 | ## How to use it
28 |
29 | ### Basic example:
30 |
31 | Example of the simplest React Component using `shadertoy-react`:
32 |
33 | ```javascript
34 | import React from 'react';
35 | import { render} from 'react-dom';
36 | import ShadertoyReact from 'shadertoy-react';
37 |
38 | const ExampleApp = () =>
39 |
40 |
41 | ;
42 | ```
43 |
44 | Example of working shader with `shadertoy` like syntax:
45 |
46 | ```c
47 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
48 | vec2 uv = fragCoord.xy/iResolution.xy;
49 | vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));
50 | fragColor = vec4(col,1.0);
51 | }
52 | ```
53 |
54 | Example of working shader with classic GLSL syntax:
55 |
56 | ```c
57 | void main(void) {
58 | vec2 uv = gl_FragCoord.xy/iResolution.xy;
59 | vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));
60 | gl_FragColor = vec4(col,1.0);
61 | }
62 | ```
63 |
64 | ## Available props
65 |
66 | Here are a few built in react props you can pass to your component. Feel free to suggest more.
67 |
68 | * `fs` -- A string containing your fragment shader.
69 | * `textures` -- An array of textures objects following that structure `{url: ... , minFilter: , magFilter: , wrapS: ,wrapT: }` the format supported are (.jpg, .jpeg, .png, .bmp) for images, and (.mp4, .3gp, .webm, .ogv) for videos. Textures needs to be squared otherwise they will be automatically squared for you. minFilter, magFilter, wrapS, wrapT are optionals, by default their values are set to `{url: ... , minFilter: LinearMipMapLinearFilter, magFilter: LinearFilter, wrapS: ReapeatWrapping, wrapT: ReapeatWrapping}`.
70 | * `uniforms` -- An object containing uniforms objects following that structure { uTest: {type: , value: }, uTest2: {type: , value: }, uTest3: {type: , value: } ... }. To know more about the supported types please refer to the [custom uniforms section](#Custom-uniforms).
71 | * `clearColor` -- An array `[red, green, blue, alpha]` Specifies the color values used when clearing color buffers method of the [WebGL API](https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/clearColor) by default `[0, 0, 0, 1]`.
72 | * `precision` -- GLSL Precision qualifier, by default highp, it can be lowp, mediump, highp depending on how much precision the GPU uses when calculating floats.
73 | * `devicePixelRatio` -- A value passed to set the [pixel density](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) of the canvas. By default 1.
74 | * `contextAttributes` -- To customize your [WebGL context attributes.](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext)
75 | * `style` -- Pass a [React Inline style](https://reactjs.org/docs/dom-elements.html#style) to customize the style of your canvas.
76 | * `onDoneLoadingTextures` -- Callback called once all textures are done being loaded, usefull when you want to wait for your shader to have all the needed texture before seeing it on screen. Using that callback you could for example simply fade the canvas in using css.
77 | * `lerp` -- A value in between 0 - 1 used to lerp the mouse position in your fragment shader.
78 |
79 | ## Uniforms
80 |
81 | #### Shadertoy's Built-in:
82 |
83 | 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.
84 |
85 | * `uniform float iTime` -- Shader playback time (in seconds).
86 | * `uniform float iTimeDelta` -- Render time (in seconds).
87 | * `uniform int iFrame` -- Shader playback frame.
88 | * `uniform vec2 iResolution` -- Viewport resolution (in pixels).
89 | * `uniform vec4 iDate` -- (Year, month, day, time in seconds).
90 | * `uniform vec4 iMouse` -- Mouse pixel coords. xy: current (if MLB down), zw: click.
91 | * `uniform sampler2D iChannel^n` -- The textures input channel you've passed; numbered in the same order as the textures passed as prop in your react component.
92 | * `uniform vec3 iChannelResolution[n]` -- An array containing the texture channel resolution (in pixels).
93 |
94 | ##### shadertoy-react's only built-in:
95 |
96 | * `uniform vec4 iDeviceOrientation` -- Raw data from [device orientation](https://developer.mozilla.org/en-US/docs/Web/API/Detecting_device_orientation) where respectively x: Alpha, y: Beta, z: Gamma and w: [window.orientation](https://developer.mozilla.org/en-US/docs/Web/API/Window/orientation).
97 | * `#define DPR 1.0` -- The canvas device pixel ratio (1.0 by default or props.devicePixelRatio).
98 |
99 | #### Custom uniforms:
100 |
101 | 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.
102 | ***Note:*** If you are whiling to pass multiple Vectors, Matrices, Ints, Floats, make sure to pass flat arrays as shown below.
103 |
104 | | Type | GLSL Type | Uniforms values in JS | Read in GLSL |
105 | |---|---|---|---|
106 | | `1f` | `float` | `val` | `uValue` |
107 | | `2f` | `vec2` | `[x, y]` | `uValue.xy` |
108 | | `3f` | `vec3` | `[x, y, z]` | `uValue.xyz` |
109 | | `4f` | `vec4` | `[x, y, z, w]` | `uValue.xyzw` |
110 | | `1fv` | `float` or `float` array | `val` or `[val, val, ...]` | `uValue` or `uValue[n]` |
111 | | `2fv` | `vec2` or `vec2` array | `[x, y]` or `[x, y, x, y, ...]` | `uValue.xy` or `uValue[n].xy` |
112 | | `3fv` | `vec3` or `vec3` array | `[x, y, z]` or `[x, y, z, x, y, z, ...]` | `uValue.xyz` or `uValue[n].xyz` |
113 | | `4fv` | `vec4` or `vec4` array | `[x, y, z, w]` or `[x, y, z, w, x, y, z, w ...]` | `uValue.xyzw` or `uValue[n].xyzw` |
114 | | `1i` | `int` | `val` | `uValue` |
115 | | `2i` | `ivec2` | `[x, y]` | `uValue.xy` |
116 | | `3i` | `ivec3` | `[x, y, z]` | `uValue.xyz` |
117 | | `4i` | `ivec4` | `[x, y, z, w]` | `uValue.xyzw` |
118 | | `1iv` | `int` or `int` array | `val` or `[val, val, val, ...]` | `uValue` or `uValue[n]` |
119 | | `2iv` | `ivec2` or `ivec2` array | `[x, y]` or `[x, y, x, y, ...]` | `uValue.xy` or `uValue[n].xy` |
120 | | `3iv` | `ivec3` or `ivec3` array | `[x, y, z]` or `[x, y, z, x, y, z, ...]` | `uValue.xyz` or `uValue[n].xyz` |
121 | | `4iv` | `ivec4` or `ivec4` array | `[x, y, z, w]` or `[x, y, z, w, x, y, z, w ...]` | `uValue.xyzw` or `uValue[n].xyzw` |
122 | | `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]` |
123 | | `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]` |
124 | | `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]` |
125 |
126 | How to do it:
127 |
128 | ```javascript
129 | import React from 'react';
130 | import { render} from 'react-dom';
131 | import ShadertoyReact from 'shadertoy-react';
132 |
133 | const ExampleApp = () => {
134 | const { scrollY } = this.state;
135 |
136 | const uniforms = {
137 | uScrollY : {type: '1f', value: scrollY }, // float
138 | uTestArrayFloats : {type: '1fv', value: [0.2, 0.4, 0.5, 0.5, 0.6] }, // Array of float
139 | uTestArrayVecs2 : {type: '2fv', value: [0.2, 0.4, 0.5, 0.5] }, // 2 vec2 passed as a flat array
140 | uTestMatrix : {
141 | type: 'Matrix2fv',
142 | value: [0., 1., 2., 3.] // 2x2 Matrix
143 | }
144 | };
145 |
146 | return
147 | (
148 |
152 | );
153 | }
154 | ```
155 |
156 | Example of shader you could write using these custom uniforms:
157 |
158 | ```c
159 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
160 | // You can then directly use uScrollY, uTestMatrix, uTestArrayFloats without having to worry about anything else.
161 | gl_FragColor = vec4(uScrollY, uTestMatrix[0][0], uTestArrayFloats[0], uTestArrayVecs2[0].xy);
162 | }
163 | ```
164 |
165 | #### Working with textures:
166 |
167 | 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.
168 |
169 | ```javascript
170 | import React from 'react';
171 | import { render} from 'react-dom';
172 | import ShadertoyReact, { LinearFilter, RepeatWrapping } from 'shadertoy-react';
173 |
174 | const ExampleApp = () =>
175 |
176 |
182 | ;
183 | ```
184 |
185 | In your shader you can directly do for example:
186 |
187 | ```c
188 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
189 | vec2 uv = fragCoord.xy / iResolution.xy;
190 | vec4 texture = texture(iChannel0, uv);
191 | gl_FragColor = texture;
192 | }
193 | ```
194 |
195 | ##### Texture Filtering:
196 |
197 | 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.
198 |
199 | **Example of optionnal texture related imports:**
200 |
201 | ```javascript
202 | import ShadertoyReact, {
203 | NearestFilter,
204 | LinearFilter,
205 | NearestMipMapNearestFilter,
206 | LinearMipMapNearestFilter,
207 | NearestMipMapLinearFilter,
208 | LinearMipMapLinearFilter,
209 | ClampToEdgeWrapping,
210 | MirroredRepeatWrapping,
211 | RepeatWrapping,
212 | } from 'shadertoy-react';
213 | ```
214 |
215 | **Example of usage of optionnal texture filtering:**
216 |
217 | ```javascript
218 | import React from 'react';
219 | import { render} from 'react-dom';
220 | import ShadertoyReact, { LinearFilter, RepeatWrapping } from 'shadertoy-react';
221 |
222 | const ExampleApp = () =>
223 |
224 |
232 | ;
233 | ```
234 |
235 | ## What's next ordered by priority
236 |
237 | * Module Support for props IntelliSense.
238 | * Dynamically load new texture when textures props changes.
239 | * Add lazy loading logic with 1x 2x 3x etc. so your shader can receive ``` ``` like src files.
240 | * Add support for #define constantes in shader from prop.
241 | * Add camera feed as a texture.
242 | * Add support for Data texture.
243 | * Add support for WebGL2 and GLSL 3.0.
244 | * Add support to multi passes as Shadertoy is doing.
245 | * Add support for Cube texture.
246 | * Add support for keyboard uniforms / inputs.
247 | * Add support for iChannelTime.
248 | * ~~Add possibility to specify gl clearColor in a prop~~ v1.0.4
249 | * ~~Add shader precision as react prop.~~ v1.0.2
250 | * ~~Add support for classic syntax (void main(void)) etc.~~ v1.0.2
251 | * ~~Add support for custom uniforms.~~ v1.0.1
252 | * ~~Add props for optionnal mouse lerping.~~ v1.0.0
253 | * ~~Add built in uniform for phone device orientation / gyroscope based effects.~~ v1.0.0
254 | * ~~Add support for iDate.~~ v1.0.0
255 | * ~~Add support for video textures.~~ v1.0.0
256 | * ~~Add support for iChannelResolution.~~ v1.0.0
257 |
258 | [build-size]: https://badge-size.herokuapp.com/mvilledieu/shadertoy-react/master/lib/shadertoy-react.min.js.svg?compression=gzip
259 | [build-size-url]: https://github.com/mvilledieu/shadertoy-react/master/lib/
--------------------------------------------------------------------------------
/examples/dist/bundle.js.LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*
2 | object-assign
3 | (c) Sindre Sorhus
4 | @license MIT
5 | */
6 |
7 | /** @license React v0.20.2
8 | * scheduler.production.min.js
9 | *
10 | * Copyright (c) Facebook, Inc. and its affiliates.
11 | *
12 | * This source code is licensed under the MIT license found in the
13 | * LICENSE file in the root directory of this source tree.
14 | */
15 |
16 | /** @license React v16.13.1
17 | * react-is.production.min.js
18 | *
19 | * Copyright (c) Facebook, Inc. and its affiliates.
20 | *
21 | * This source code is licensed under the MIT license found in the
22 | * LICENSE file in the root directory of this source tree.
23 | */
24 |
25 | /** @license React v17.0.2
26 | * react-dom.production.min.js
27 | *
28 | * Copyright (c) Facebook, Inc. and its affiliates.
29 | *
30 | * This source code is licensed under the MIT license found in the
31 | * LICENSE file in the root directory of this source tree.
32 | */
33 |
34 | /** @license React v17.0.2
35 | * react.production.min.js
36 | *
37 | * Copyright (c) Facebook, Inc. and its affiliates.
38 | *
39 | * This source code is licensed under the MIT license found in the
40 | * LICENSE file in the root directory of this source tree.
41 | */
42 |
--------------------------------------------------------------------------------
/examples/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Shadertoy React
4 |
5 |
9 |
10 |
11 |
12 | You need to enable JavaScript to run this app.
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Shadertoy React
5 |
6 |
7 |
8 |
9 |
10 |
11 | You need to enable JavaScript to run this app.
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/src/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { render } from "react-dom";
3 | import styled, { createGlobalStyle, keyframes } from "styled-components";
4 | import ShadertoyReact from "../../src/index.jsx";
5 | import fs from "./shaders/fs";
6 | import fsImages from "./shaders/fsImages";
7 | import mouse from "./shaders/mouse";
8 | import clock from "./shaders/clock";
9 | import deviceorientation from "./shaders/deviceorientation.js";
10 | import classicSyntax from "./shaders/classicSyntax.js";
11 | import customUniforms from "./shaders/customUniforms.js";
12 |
13 | const GlobalStyle = createGlobalStyle`
14 | body, html {
15 | width: 100%;
16 | height: 100%;
17 | margin: 0;
18 | padding: 0;
19 | -webkit-tap-highlight-color: rgba(0,0,0,0);
20 | }
21 | `;
22 |
23 | const fadeIn = keyframes`
24 | from {
25 | opacity: 0;
26 | }
27 | to {
28 | opacity: 1;
29 | }
30 | `;
31 |
32 | const fadeOut = keyframes`
33 | opacity: 0;
34 | `;
35 |
36 | const Container = styled.div`
37 | display: flex;
38 | flex-wrap: wrap;
39 | flex-direction: row;
40 | `;
41 |
42 | const Parent = styled.div`
43 | flex-grow: 1;
44 | height: calc(100vh / 3);
45 | width: calc(100vw / 3);
46 | `;
47 |
48 | const TestCallbackFading = styled(Parent)`
49 | opacity: 0;
50 | transition: opacity 500ms;
51 | opacity: ${(props) => (props.fadeIn ? 1 : 0)};
52 | `;
53 |
54 | let counter = 0;
55 | class App extends Component {
56 | constructor(props) {
57 | super(props);
58 | this.state = {
59 | fadeIn: false,
60 | val: 0,
61 | };
62 | setInterval(() => {
63 | this.setState({ val: (counter += 0.1) });
64 | }, 100);
65 | }
66 |
67 | render() {
68 | return (
69 |
70 |
71 | {
76 | this.setState({ fadeIn: true });
77 | console.log("onDoneLoadingTextures");
78 | }}
79 | />
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | );
113 | }
114 | }
115 |
116 | render( , document.getElementById("root"));
117 |
--------------------------------------------------------------------------------
/examples/src/shaders/classicSyntax.js:
--------------------------------------------------------------------------------
1 | export default `
2 | void main(void){
3 | vec2 uv = gl_FragCoord.xy / iResolution.xy;
4 | gl_FragColor = vec4(uv, 0., 1.);
5 | }
6 | `;
--------------------------------------------------------------------------------
/examples/src/shaders/clock.js:
--------------------------------------------------------------------------------
1 | export default `#define PI 3.14159
2 |
3 | float circle(vec2 uv, vec2 pos, float radius, float blur)
4 | {
5 | return 1.0 - smoothstep(radius, radius + blur + 0.001, length(uv - pos));
6 | }
7 |
8 | float defaultBlur = 0.005;
9 |
10 | void mainImage( out vec4 fragColor, in vec2 fragCoord )
11 | {
12 | // Normalized pixel coordinates (from -1 to 1)
13 | vec2 uv = fragCoord/iResolution.xy * 2.0 - 1.0;
14 | uv.y /= iResolution.x / iResolution.y;
15 |
16 | // Time varying pixel color
17 | vec3 psych = 0.5 + 0.5*cos(iTime+(fragCoord/iResolution.xy).xy.xyx+vec3(0.01,2,4));
18 |
19 |
20 | // ClockFace
21 | float circles = circle(uv, vec2(0.0, 0.0), 0.2, defaultBlur);
22 | // Time
23 | float seconds = iDate.w / 60.0 * PI * 2.0;
24 | float minutes = iDate.w / 3600.0 * PI*2.0;
25 | float hours = iDate.w / 216000.0 * PI*2.0;
26 |
27 | // seconds hand
28 | circles += circle(uv, vec2(sin(seconds)/5.0, cos(seconds)/5.0), 0.025, defaultBlur);
29 |
30 | // Minutes hand
31 | circles += circle(uv, vec2(sin(minutes)/3.0, cos(minutes)/3.0), 0.04, defaultBlur);
32 | circles += circle(uv, vec2(sin(minutes)/2.5, cos(minutes)/2.5), 0.04, defaultBlur);
33 |
34 | // hours hand
35 |
36 | circles += circle(uv, vec2(sin(hours)/5.0, cos(minutes)/5.0), 0.03, defaultBlur);
37 |
38 |
39 | // using circle as a mask
40 | vec4 col = mix(vec4(psych,1.0), vec4( 1.0), circles);
41 |
42 | vec4 justCircle = vec4(vec3(circles), 1.0);
43 |
44 | // Output to screen
45 | fragColor = col;
46 | }
47 |
48 | `;
--------------------------------------------------------------------------------
/examples/src/shaders/customUniforms.js:
--------------------------------------------------------------------------------
1 | export default `
2 | void mainImage( out vec4 fragColor, in vec2 fragCoord )
3 | {
4 | fragColor = vec4(sin(uTest), 0., 0., 1.);
5 | }`;
--------------------------------------------------------------------------------
/examples/src/shaders/deviceorientation.js:
--------------------------------------------------------------------------------
1 | export default `
2 | #define PI 3.1415926535898
3 | #define DEGTORAD PI / 180.
4 |
5 | // from https://www.shadertoy.com/view/XlXBRn
6 |
7 | // helper converting your euler values to a rotation matrix.
8 | // code ported over to glsl from https://dev.opera.com/articles/w3c-device-orientation-usage/
9 | mat3 getBaseRotationMatrix( vec3 euler ) {
10 | float _x = euler.y * DEGTORAD;
11 | float _y = euler.z * DEGTORAD;
12 | float _z = euler.x * DEGTORAD;
13 |
14 | float cX = cos( _x );
15 | float cY = cos( _y );
16 | float cZ = cos( _z );
17 |
18 | float sX = sin( _x );
19 | float sY = sin( _y );
20 | float sZ = sin( _z );
21 |
22 | float m11 = cZ * cY - sZ * sX * sY;
23 | float m12 = - cX * sZ;
24 | float m13 = cY * sZ * sX + cZ * sY;
25 |
26 | float m21 = cY * sZ + cZ * sX * sY;
27 | float m22 = cZ * cX;
28 | float m23 = sZ * sY - cZ * cY * sX;
29 |
30 | float m31 = - cX * sY;
31 | float m32 = sX;
32 | float m33 = cX * cY;
33 |
34 | return mat3(
35 | m11, m12, m13,
36 | m21, m22, m23,
37 | m31, m32, m33
38 | );
39 | }
40 |
41 | mat3 getScreenTransformationMatrix( float screenOrientation ) {
42 | float orientationAngle = screenOrientation * DEGTORAD;
43 |
44 | float cA = cos( orientationAngle );
45 | float sA = sin( orientationAngle );
46 |
47 | // Construct our screen transformation matrix
48 | return mat3(
49 | cA, -sA, 0,
50 | sA, cA, 0,
51 | 0, 0, 1
52 | );
53 | }
54 |
55 | mat3 getWorldTransformationMatrix() {
56 | float x = 90. * DEGTORAD;
57 |
58 | float cA = cos( x );
59 | float sA = sin( x );
60 |
61 | return mat3 (
62 | 1, 0, 0,
63 | 0, cA, -sA,
64 | 0, sA, cA
65 | );
66 | }
67 |
68 | float map(vec3 p)
69 | {
70 | vec3 q = fract(p)*2.-1.;
71 | return length(q)-0.25;
72 | }
73 |
74 | float trace(vec3 origin, vec3 ray)
75 | {
76 | float t=0.;
77 | for(int i=0; i<32; i++){
78 | vec3 p = origin + t*ray;
79 | float d=map(p);
80 | t += d*0.5;
81 | }
82 | return t;
83 | }
84 |
85 | void mainImage( out vec4 fragColor, in vec2 fragCoord )
86 | {
87 | vec2 uv = fragCoord.xy / iResolution.xy;
88 | uv = uv*2. -1.;
89 | uv.x *= iResolution.x/iResolution.y;
90 |
91 | //code ported over to glsl from https://dev.opera.com/articles/w3c-device-orientation-usage/
92 | mat3 rotationMatrix = getBaseRotationMatrix(vec3(iDeviceOrientation.x, -iDeviceOrientation.y, -iDeviceOrientation.z)); // R
93 | //mat3 screenTransform = getScreenTransformationMatrix( iDeviceOrientation.w ); // r_s
94 | //mat3 screenAdjustedMatrix = rotationMatrix * screenTransform; // R_s
95 | mat3 screenAdjustedMatrix = rotationMatrix;
96 | mat3 worldTransform = getWorldTransformationMatrix(); // r_w
97 | mat3 finalMatrix = screenAdjustedMatrix * worldTransform; // R_w
98 |
99 | vec3 ray = normalize(vec3(uv,1.)) * finalMatrix; //Multiply the raydirection by our device rotation matrix.
100 |
101 | //vec3 ori = vec3(0.,0.,-3.);
102 | vec3 ori = vec3(0., 0., iTime * 2.);
103 | float t = trace(ori, ray);
104 |
105 | float fog = 1.0/(1.0+t*t*0.1);
106 | vec3 color = vec3(fog);
107 |
108 | fragColor = vec4(color,1.0);
109 | }
110 |
111 | `;
--------------------------------------------------------------------------------
/examples/src/shaders/deviceorientation2.js:
--------------------------------------------------------------------------------
1 | export default `
2 | #define PI 3.1415926535898
3 | #define DEGTORAD PI / 180.
4 |
5 | // This was my shader for the shader showdown at Outline demoparty 2018 in Nederland.
6 | // Shader showdown is a live-coding competition where two participants are
7 | // facing each other during 25 minutes.
8 | // (Round 1)
9 |
10 | // I don't have access to the code I typed at the event, so it might be
11 | // slightly different.
12 |
13 | // Original algorithm on shadertoy from fb39ca4: https://www.shadertoy.com/view/4dX3zl
14 | // I used the implementation from shane: https://www.shadertoy.com/view/MdVSDh
15 |
16 | // Thanks to shadertoy community & shader showdown paris.
17 |
18 | // This is under CC-BY-NC-SA (shadertoy default licence)
19 |
20 | mat2 r2d(float a) {
21 | float c = cos(a), s = sin(a);
22 | return mat2(c, s, -s, c);
23 | }
24 |
25 | vec2 path(float t) {
26 | float a = sin(t*.2 + 1.5), b = sin(t*.2);
27 | return vec2(2.*a, a*b);
28 | }
29 |
30 | float g = 0.;
31 | float de(vec3 p) {
32 | p.xy -= path(p.z);
33 |
34 | float d = -length(p.xy) + 4.;// tunnel (inverted cylinder)
35 |
36 | p.xy += vec2(cos(p.z + iTime)*sin(iTime), cos(p.z + iTime));
37 | p.z -= 6. + iTime * 6.;
38 | d = min(d, dot(p, normalize(sign(p))) - 1.); // octahedron (LJ's formula)
39 | // I added this in the last 1-2 minutes, but I'm not sure if I like it actually!
40 |
41 | // Trick inspired by balkhan's shadertoys.
42 | // Usually, in raymarch shaders it gives a glow effect,
43 | // here, it gives a colors patchwork & transparent voxels effects.
44 | g += .015 / (.01 + d * d);
45 | return d;
46 | }
47 |
48 | // helper converting your euler values to a rotation matrix.
49 | // code ported over to glsl from https://dev.opera.com/articles/w3c-device-orientation-usage/
50 | mat3 getBaseRotationMatrix( vec3 euler ) {
51 | float _x = euler.y * DEGTORAD;
52 | float _y = euler.z * DEGTORAD;
53 | float _z = euler.x * DEGTORAD;
54 |
55 | float cX = cos( _x );
56 | float cY = cos( _y );
57 | float cZ = cos( _z );
58 |
59 | float sX = sin( _x );
60 | float sY = sin( _y );
61 | float sZ = sin( _z );
62 |
63 | float m11 = cZ * cY - sZ * sX * sY;
64 | float m12 = - cX * sZ;
65 | float m13 = cY * sZ * sX + cZ * sY;
66 |
67 | float m21 = cY * sZ + cZ * sX * sY;
68 | float m22 = cZ * cX;
69 | float m23 = sZ * sY - cZ * cY * sX;
70 |
71 | float m31 = - cX * sY;
72 | float m32 = sX;
73 | float m33 = cX * cY;
74 |
75 | return mat3(
76 | m11, m12, m13,
77 | m21, m22, m23,
78 | m31, m32, m33
79 | );
80 | }
81 |
82 | mat3 getScreenTransformationMatrix( float screenOrientation ) {
83 | float orientationAngle = screenOrientation * DEGTORAD;
84 |
85 | float cA = cos( orientationAngle );
86 | float sA = sin( orientationAngle );
87 |
88 | // Construct our screen transformation matrix
89 | return mat3(
90 | cA, -sA, 0,
91 | sA, cA, 0,
92 | 0, 0, 1
93 | );
94 | }
95 |
96 | mat3 getWorldTransformationMatrix() {
97 | float x = 90. * DEGTORAD;
98 |
99 | float cA = cos( x );
100 | float sA = sin( x );
101 |
102 | return mat3 (
103 | 1, 0, 0,
104 | 0, cA, -sA,
105 | 0, sA, cA
106 | );
107 | }
108 |
109 | void mainImage( out vec4 fragColor, in vec2 fragCoord )
110 | {
111 | vec2 uv = fragCoord / iResolution.xy - .5;
112 | uv.x *= iResolution.x / iResolution.y;
113 |
114 | //code ported over to glsl from https://dev.opera.com/articles/w3c-device-orientation-usage/
115 | mat3 rotationMatrix = getBaseRotationMatrix(vec3(-iDeviceOrientation.x, -iDeviceOrientation.y, iDeviceOrientation.z)); // R
116 | //mat3 screenTransform = getScreenTransformationMatrix( iDeviceOrientation.w ); // r_s
117 | //mat3 screenAdjustedMatrix = rotationMatrix * screenTransform; // R_s
118 | mat3 screenAdjustedMatrix = rotationMatrix;
119 | mat3 worldTransform = getWorldTransformationMatrix(); // r_w
120 | mat3 finalMatrix = screenAdjustedMatrix * worldTransform; // R_w
121 |
122 |
123 | float dt = iTime * 6.;
124 | vec3 ro = vec3(0, 0, -5. + dt);
125 | vec3 ta = vec3(0, 0, dt);
126 |
127 | //ro.xy += path(ro.z);
128 | //ta.xy += path(ta.z);
129 |
130 | vec3 fwd = normalize(ta - ro);
131 | vec3 right = cross(fwd, vec3(0, 1, 0));
132 | vec3 up = cross(right, fwd);
133 |
134 | vec3 rd = normalize(fwd + uv.x*right + uv.y*up) * finalMatrix;
135 |
136 | // rd.xy *= r2d(sin(-ro.x / 3.14)*.3);
137 |
138 | // Raycast in 3d to get voxels.
139 | // Algorithm fully explained here in 2D (just look at dde algo):
140 | // http://lodev.org/cgtutor/raycasting.html
141 | // Basically, tracing a ray in a 3d grid space, and looking for
142 | // each voxel (think pixel with a third dimension) traversed by the ray.
143 | vec3 p = floor(ro) + .5;
144 | vec3 mask;
145 | vec3 drd = 1. / abs(rd);
146 | rd = sign(rd);
147 | vec3 side = drd * (rd * (p - ro) + .5);
148 |
149 | float t = 0., ri = 0.;
150 | for (float i = 0.; i < 1.; i += .01) {
151 | ri = i;
152 |
153 | /*
154 | // sphere tracing algorithm (for comparison)
155 | p = ro + rd * t;
156 | float d = de(p);
157 | if(d<.001) break;
158 | t += d;
159 | */
160 |
161 | if (de(p) < 0.) break;// distance field
162 | // we test if we are inside the surface
163 |
164 | mask = step(side, side.yzx) * step(side, side.zxy);
165 | // minimum value between x,y,z, output 0 or 1
166 |
167 | side += drd * mask;
168 | p += rd * mask;
169 | }
170 | t = length(p - ro);
171 |
172 | vec3 c = vec3(1) * length(mask * vec3(1., .5, .75));
173 | c = mix(vec3(.2, .2, .7), vec3(.2, .1, .2), c);
174 | c += g * .4;
175 | c.r += sin(iTime)*.2 + sin(p.z*.5 - iTime * 6.);// red rings
176 | c = mix(c, vec3(.2, .1, .2), 1. - exp(-.001*t*t));// fog
177 |
178 | fragColor = vec4(c, 1.0);
179 | }`;
--------------------------------------------------------------------------------
/examples/src/shaders/fs.js:
--------------------------------------------------------------------------------
1 | export default `
2 | // @lsdlive
3 |
4 | // This was my shader for the shader showdown at Outline demoparty 2018 in Nederland.
5 | // Shader showdown is a live-coding competition where two participants are
6 | // facing each other during 25 minutes.
7 | // (Round 1)
8 |
9 | // I don't have access to the code I typed at the event, so it might be
10 | // slightly different.
11 |
12 | // Original algorithm on shadertoy from fb39ca4: https://www.shadertoy.com/view/4dX3zl
13 | // I used the implementation from shane: https://www.shadertoy.com/view/MdVSDh
14 |
15 | // Thanks to shadertoy community & shader showdown paris.
16 |
17 | // This is under CC-BY-NC-SA (shadertoy default licence)
18 |
19 | mat2 r2d(float a) {
20 | float c = cos(a), s = sin(a);
21 | return mat2(c, s, -s, c);
22 | }
23 |
24 | vec2 path(float t) {
25 | float a = sin(t*.2 + 1.5), b = sin(t*.2);
26 | return vec2(2.*a, a*b);
27 | }
28 |
29 | float g = 0.;
30 | float de(vec3 p) {
31 | p.xy -= path(p.z);
32 |
33 | float d = -length(p.xy) + 4.;// tunnel (inverted cylinder)
34 |
35 | p.xy += vec2(cos(p.z + iTime)*sin(iTime), cos(p.z + iTime));
36 | p.z -= 6. + iTime * 6.;
37 | d = min(d, dot(p, normalize(sign(p))) - 1.); // octahedron (LJ's formula)
38 | // I added this in the last 1-2 minutes, but I'm not sure if I like it actually!
39 |
40 | // Trick inspired by balkhan's shadertoys.
41 | // Usually, in raymarch shaders it gives a glow effect,
42 | // here, it gives a colors patchwork & transparent voxels effects.
43 | g += .015 / (.01 + d * d);
44 | return d;
45 | }
46 |
47 | void mainImage( out vec4 fragColor, in vec2 fragCoord )
48 | {
49 | vec2 uv = fragCoord / iResolution.xy - .5;
50 | uv.x *= iResolution.x / iResolution.y;
51 |
52 | float dt = iTime * 6.;
53 | vec3 ro = vec3(0, 0, -5. + dt);
54 | vec3 ta = vec3(0, 0, dt);
55 |
56 | ro.xy += path(ro.z);
57 | ta.xy += path(ta.z);
58 |
59 | vec3 fwd = normalize(ta - ro);
60 | vec3 right = cross(fwd, vec3(0, 1, 0));
61 | vec3 up = cross(right, fwd);
62 | vec3 rd = normalize(fwd + uv.x*right + uv.y*up);
63 |
64 | rd.xy *= r2d(sin(-ro.x / 3.14)*.3);
65 |
66 | // Raycast in 3d to get voxels.
67 | // Algorithm fully explained here in 2D (just look at dde algo):
68 | // http://lodev.org/cgtutor/raycasting.html
69 | // Basically, tracing a ray in a 3d grid space, and looking for
70 | // each voxel (think pixel with a third dimension) traversed by the ray.
71 | vec3 p = floor(ro) + .5;
72 | vec3 mask;
73 | vec3 drd = 1. / abs(rd);
74 | rd = sign(rd);
75 | vec3 side = drd * (rd * (p - ro) + .5);
76 |
77 | float t = 0., ri = 0.;
78 | for (float i = 0.; i < 1.; i += .01) {
79 | ri = i;
80 |
81 | /*
82 | // sphere tracing algorithm (for comparison)
83 | p = ro + rd * t;
84 | float d = de(p);
85 | if(d<.001) break;
86 | t += d;
87 | */
88 |
89 | if (de(p) < 0.) break;// distance field
90 | // we test if we are inside the surface
91 |
92 | mask = step(side, side.yzx) * step(side, side.zxy);
93 | // minimum value between x,y,z, output 0 or 1
94 |
95 | side += drd * mask;
96 | p += rd * mask;
97 | }
98 | t = length(p - ro);
99 |
100 | vec3 c = vec3(1) * length(mask * vec3(1., .5, .75));
101 | c = mix(vec3(.2, .2, .7), vec3(.2, .1, .2), c);
102 | c += g * .4;
103 | c.r += sin(iTime)*.2 + sin(p.z*.5 - iTime * 6.);// red rings
104 | c = mix(c, vec3(.2, .1, .2), 1. - exp(-.001*t*t));// fog
105 |
106 | fragColor = vec4(c, 1.0);
107 | }`;
--------------------------------------------------------------------------------
/examples/src/shaders/fsImages.js:
--------------------------------------------------------------------------------
1 | export default `
2 | void mainImage( out vec4 fragColor, in vec2 fragCoord )
3 | {
4 | vec2 uv = fragCoord / iResolution.xy;
5 | vec4 text = texture(iChannel0, fragCoord/iChannelResolution[0].xy + vec2(0., iTime * 0.1), sin(iTime) * 5.);
6 | fragColor = text;
7 | }`;
--------------------------------------------------------------------------------
/examples/src/shaders/keyboard.js:
--------------------------------------------------------------------------------
1 | export default `// Created by inigo quilez - iq/2013
2 |
3 | // An example showing how to use the keyboard input.
4 | //
5 | // Row 0: contain the current state of the 256 keys.
6 | // Row 1: contains Keypress.
7 | // Row 2: contains a toggle for every key.
8 | //
9 | // Texel positions correspond to ASCII codes. Press arrow keys to test.
10 |
11 | // See also:
12 | //
13 | // Input - Keyboard : https://www.shadertoy.com/view/lsXGzf
14 | // Input - Microphone : https://www.shadertoy.com/view/llSGDh
15 | // Input - Mouse : https://www.shadertoy.com/view/Mss3zH
16 | // Input - Sound : https://www.shadertoy.com/view/Xds3Rr
17 | // Input - SoundCloud : https://www.shadertoy.com/view/MsdGzn
18 | // Input - Time : https://www.shadertoy.com/view/lsXGz8
19 | // Input - TimeDelta : https://www.shadertoy.com/view/lsKGWV
20 | // Inout - 3D Texture : https://www.shadertoy.com/view/4llcR4
21 |
22 | const int KEY_LEFT = 37;
23 | const int KEY_UP = 38;
24 | const int KEY_RIGHT = 39;
25 | const int KEY_DOWN = 40;
26 |
27 | void mainImage( out vec4 fragColor, in vec2 fragCoord )
28 | {
29 | vec2 uv = (-iResolution.xy + 2.0*fragCoord) / iResolution.y;
30 |
31 | vec3 col = vec3(0.0);
32 |
33 | // state
34 | col = mix( col, vec3(1.0,0.0,0.0),
35 | (1.0-smoothstep(0.3,0.31,length(uv-vec2(-0.75,0.0))))*
36 | (0.3+0.7*texelFetch( iChannel0, ivec2(KEY_LEFT,0), 0 ).x) );
37 |
38 | col = mix( col, vec3(1.0,1.0,0.0),
39 | (1.0-smoothstep(0.3,0.31,length(uv-vec2(0.0,0.5))))*
40 | (0.3+0.7*texelFetch( iChannel0, ivec2(KEY_UP,0), 0 ).x));
41 |
42 | col = mix( col, vec3(0.0,1.0,0.0),
43 | (1.0-smoothstep(0.3,0.31,length(uv-vec2(0.75,0.0))))*
44 | (0.3+0.7*texelFetch( iChannel0, ivec2(KEY_RIGHT,0), 0 ).x));
45 |
46 | col = mix( col, vec3(0.0,0.0,1.0),
47 | (1.0-smoothstep(0.3,0.31,length(uv-vec2(0.0,-0.5))))*
48 | (0.3+0.7*texelFetch( iChannel0, ivec2(KEY_DOWN,0), 0 ).x));
49 |
50 |
51 | // keypress
52 | col = mix( col, vec3(1.0,0.0,0.0),
53 | (1.0-smoothstep(0.0,0.01,abs(length(uv-vec2(-0.75,0.0))-0.35)))*
54 | texelFetch( iChannel0, ivec2(KEY_LEFT,1),0 ).x);
55 |
56 | col = mix( col, vec3(1.0,1.0,0.0),
57 | (1.0-smoothstep(0.0,0.01,abs(length(uv-vec2(0.0,0.5))-0.35)))*
58 | texelFetch( iChannel0, ivec2(KEY_UP,1),0 ).x);
59 |
60 | col = mix( col, vec3(0.0,1.0,0.0),
61 | (1.0-smoothstep(0.0,0.01,abs(length(uv-vec2(0.75,0.0))-0.35)))*
62 | texelFetch( iChannel0, ivec2(KEY_RIGHT,1),0 ).x);
63 |
64 | col = mix( col, vec3(0.0,0.0,1.0),
65 | (1.0-smoothstep(0.0,0.01,abs(length(uv-vec2(0.0,-0.5))-0.35)))*
66 | texelFetch( iChannel0, ivec2(KEY_DOWN,1),0 ).x);
67 |
68 |
69 | // toggle
70 | col = mix( col, vec3(1.0,0.0,0.0),
71 | (1.0-smoothstep(0.0,0.01,abs(length(uv-vec2(-0.75,0.0))-0.3)))*
72 | texelFetch( iChannel0, ivec2(KEY_LEFT,2),0 ).x);
73 |
74 | col = mix( col, vec3(1.0,1.0,0.0),
75 | (1.0-smoothstep(0.0,0.01,abs(length(uv-vec2(0.0,0.5))-0.3)))*
76 | texelFetch( iChannel0, ivec2(KEY_UP,2),0 ).x);
77 |
78 | col = mix( col, vec3(0.0,1.0,0.0),
79 | (1.0-smoothstep(0.0,0.01,abs(length(uv-vec2(0.75,0.0))-0.3)))*
80 | texelFetch( iChannel0, ivec2(KEY_RIGHT,2),0 ).x);
81 |
82 | col = mix( col, vec3(0.0,0.0,1.0),
83 | (1.0-smoothstep(0.0,0.01,abs(length(uv-vec2(0.0,-0.5))-0.3)))*
84 | texelFetch( iChannel0, ivec2(KEY_DOWN,2),0 ).x);
85 |
86 | fragColor = vec4(col,1.0);
87 | }`;
--------------------------------------------------------------------------------
/examples/src/shaders/mouse.js:
--------------------------------------------------------------------------------
1 | export default `
2 | // Created by inigo quilez - iq/2013
3 | // License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
4 |
5 |
6 | // See also:
7 | //
8 | // Input - Keyboard : https://www.shadertoy.com/view/lsXGzf
9 | // Input - Microphone : https://www.shadertoy.com/view/llSGDh
10 | // Input - Mouse : https://www.shadertoy.com/view/Mss3zH
11 | // Input - Sound : https://www.shadertoy.com/view/Xds3Rr
12 | // Input - SoundCloud : https://www.shadertoy.com/view/MsdGzn
13 | // Input - Time : https://www.shadertoy.com/view/lsXGz8
14 | // Input - TimeDelta : https://www.shadertoy.com/view/lsKGWV
15 | // Inout - 3D Texture : https://www.shadertoy.com/view/4llcR4
16 |
17 |
18 | float distanceToSegment( vec2 a, vec2 b, vec2 p )
19 | {
20 | vec2 pa = p - a, ba = b - a;
21 | float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
22 | return length( pa - ba*h );
23 | }
24 |
25 | void mainImage( out vec4 fragColor, in vec2 fragCoord )
26 | {
27 | vec2 p = fragCoord.xy / iResolution.xx;
28 | vec4 m = iMouse / iResolution.xxxx;
29 |
30 | vec3 col = vec3(0.0);
31 |
32 | if( m.z>0.0 )
33 | {
34 | float d = distanceToSegment( m.xy, m.zw, p );
35 | col = mix( col, vec3(1.0,1.0,0.0), 1.0-smoothstep(.004,0.008, d) );
36 | }
37 |
38 | col = mix( col, vec3(1.0,0.0,0.0), 1.0-smoothstep(0.03,0.035, length(p-m.xy)) );
39 | col = mix( col, vec3(0.0,0.0,1.0), 1.0-smoothstep(0.03,0.035, length(p-abs(m.zw))) );
40 |
41 | fragColor = vec4( col, 1.0 );
42 | }
43 | `;
--------------------------------------------------------------------------------
/lib/shadertoy-react.js:
--------------------------------------------------------------------------------
1 | /******/ (() => { // webpackBootstrap
2 | /******/ "use strict";
3 | /******/ // The require scope
4 | /******/ var __webpack_require__ = {};
5 | /******/
6 | /************************************************************************/
7 | /******/ /* webpack/runtime/compat get default export */
8 | /******/ (() => {
9 | /******/ // getDefaultExport function for compatibility with non-harmony modules
10 | /******/ __webpack_require__.n = (module) => {
11 | /******/ var getter = module && module.__esModule ?
12 | /******/ () => (module['default']) :
13 | /******/ () => (module);
14 | /******/ __webpack_require__.d(getter, { a: getter });
15 | /******/ return getter;
16 | /******/ };
17 | /******/ })();
18 | /******/
19 | /******/ /* webpack/runtime/define property getters */
20 | /******/ (() => {
21 | /******/ // define getter functions for harmony exports
22 | /******/ __webpack_require__.d = (exports, definition) => {
23 | /******/ for(var key in definition) {
24 | /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
25 | /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
26 | /******/ }
27 | /******/ }
28 | /******/ };
29 | /******/ })();
30 | /******/
31 | /******/ /* webpack/runtime/hasOwnProperty shorthand */
32 | /******/ (() => {
33 | /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
34 | /******/ })();
35 | /******/
36 | /******/ /* webpack/runtime/make namespace object */
37 | /******/ (() => {
38 | /******/ // define __esModule on exports
39 | /******/ __webpack_require__.r = (exports) => {
40 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
41 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
42 | /******/ }
43 | /******/ Object.defineProperty(exports, '__esModule', { value: true });
44 | /******/ };
45 | /******/ })();
46 | /******/
47 | /************************************************************************/
48 | var __webpack_exports__ = {};
49 | // ESM COMPAT FLAG
50 | __webpack_require__.r(__webpack_exports__);
51 |
52 | // EXPORTS
53 | __webpack_require__.d(__webpack_exports__, {
54 | "ClampToEdgeWrapping": () => (/* reexport */ ClampToEdgeWrapping),
55 | "LinearFilter": () => (/* reexport */ LinearFilter),
56 | "LinearMipMapLinearFilter": () => (/* reexport */ LinearMipMapLinearFilter),
57 | "LinearMipMapNearestFilter": () => (/* reexport */ LinearMipMapNearestFilter),
58 | "MirroredRepeatWrapping": () => (/* reexport */ MirroredRepeatWrapping),
59 | "NearestFilter": () => (/* reexport */ NearestFilter),
60 | "NearestMipMapLinearFilter": () => (/* reexport */ NearestMipMapLinearFilter),
61 | "NearestMipMapNearestFilter": () => (/* reexport */ NearestMipMapNearestFilter),
62 | "RepeatWrapping": () => (/* reexport */ RepeatWrapping),
63 | "default": () => (/* binding */ ShadertoyReact)
64 | });
65 |
66 | ;// CONCATENATED MODULE: external "react"
67 | const external_react_namespaceObject = require("react");
68 | var external_react_default = /*#__PURE__*/__webpack_require__.n(external_react_namespaceObject);
69 | ;// CONCATENATED MODULE: ./src/prefixLogs.js
70 | // $flow
71 | var SRLOG = function SRLOG(text) {
72 | return "shadertoy-react: ".concat(text);
73 | };
74 | ;// CONCATENATED MODULE: ./src/Texture.js
75 | var _templateObject, _templateObject2;
76 |
77 | function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
78 |
79 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
80 |
81 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
82 |
83 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
84 |
85 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
86 |
87 |
88 |
89 | var NearestFilter = 9728;
90 | var LinearFilter = 9729;
91 | var NearestMipMapNearestFilter = 9984;
92 | var LinearMipMapNearestFilter = 9985;
93 | var NearestMipMapLinearFilter = 9986;
94 | var LinearMipMapLinearFilter = 9987;
95 | var ClampToEdgeWrapping = 33071;
96 | var MirroredRepeatWrapping = 33648;
97 | var RepeatWrapping = 10497; // eslint-disable-next-line
98 |
99 | var isPowerOf2 = function isPowerOf2(value) {
100 | return (value & value - 1) == 0;
101 | };
102 |
103 | var floorPowerOfTwo = function floorPowerOfTwo(value) {
104 | return Math.pow(2, Math.floor(Math.log(value) / Math.LN2));
105 | };
106 |
107 | var textureNeedsGenerateMipmaps = function textureNeedsGenerateMipmaps(texture, isPowerOfTwo) {
108 | return isPowerOfTwo && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter;
109 | };
110 |
111 | var textureNeedsPowerOfTwo = function textureNeedsPowerOfTwo(texture) {
112 | if (texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping) return true;
113 | if (texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) return true;
114 | return false;
115 | };
116 |
117 | var Texture = /*#__PURE__*/_createClass(function Texture(_gl) {
118 | var _this = this;
119 |
120 | _classCallCheck(this, Texture);
121 |
122 | _defineProperty(this, "isLoaded", false);
123 |
124 | _defineProperty(this, "url", void 0);
125 |
126 | _defineProperty(this, "wrapS", void 0);
127 |
128 | _defineProperty(this, "wrapT", void 0);
129 |
130 | _defineProperty(this, "minFilter", void 0);
131 |
132 | _defineProperty(this, "magFilter", void 0);
133 |
134 | _defineProperty(this, "source", void 0);
135 |
136 | _defineProperty(this, "flipY", -1);
137 |
138 | _defineProperty(this, "width", 0);
139 |
140 | _defineProperty(this, "height", 0);
141 |
142 | _defineProperty(this, "_webglTexture", null);
143 |
144 | _defineProperty(this, "updateTexture", function (texture, video, flipY) {
145 | var gl = _this.gl;
146 | var level = 0;
147 | var internalFormat = gl.RGBA;
148 | var srcFormat = gl.RGBA;
149 | var srcType = gl.UNSIGNED_BYTE;
150 | gl.bindTexture(gl.TEXTURE_2D, texture);
151 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
152 | gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, srcFormat, srcType, video);
153 | });
154 |
155 | _defineProperty(this, "setupVideo", function (url) {
156 | var video = document.createElement('video');
157 | var playing = false;
158 | var timeupdate = false;
159 | video.autoplay = true;
160 | video.muted = true;
161 | video.loop = true;
162 | video.crossOrigin = 'anonymous';
163 |
164 | var checkReady = function checkReady() {
165 | if (playing && timeupdate) {
166 | _this.isLoaded = true;
167 | }
168 | };
169 |
170 | video.addEventListener('playing', function () {
171 | playing = true;
172 | _this.width = video.videoWidth || 0;
173 | _this.height = video.videoHeight || 0;
174 | checkReady();
175 | }, true);
176 | video.addEventListener('timeupdate', function () {
177 | timeupdate = true;
178 | checkReady();
179 | }, true);
180 | video.src = url; // video.play();
181 |
182 | return video;
183 | });
184 |
185 | _defineProperty(this, "makePowerOfTwo", function (image) {
186 | if (image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof ImageBitmap) {
187 | if (_this.pow2canvas === undefined) _this.pow2canvas = document.createElement('canvas');
188 | _this.pow2canvas.width = floorPowerOfTwo(image.width);
189 | _this.pow2canvas.height = floorPowerOfTwo(image.height);
190 |
191 | var context = _this.pow2canvas.getContext('2d');
192 |
193 | context.drawImage(image, 0, 0, _this.pow2canvas.width, _this.pow2canvas.height); // eslint-disable-next-line
194 |
195 | console.warn(SRLOG("Image is not power of two ".concat(image.width, " x ").concat(image.height, ". Resized to ").concat(_this.pow2canvas.width, " x ").concat(_this.pow2canvas.height, ";")));
196 | return _this.pow2canvas;
197 | }
198 |
199 | return image;
200 | });
201 |
202 | _defineProperty(this, "load", function (textureArgs, channelId) {
203 | var gl = _this.gl;
204 | var url = textureArgs.url,
205 | wrapS = textureArgs.wrapS,
206 | wrapT = textureArgs.wrapT,
207 | minFilter = textureArgs.minFilter,
208 | magFilter = textureArgs.magFilter,
209 | _textureArgs$flipY = textureArgs.flipY,
210 | flipY = _textureArgs$flipY === void 0 ? -1 : _textureArgs$flipY;
211 |
212 | if (!url) {
213 | return Promise.reject(new Error(SRLOG(_templateObject || (_templateObject = _taggedTemplateLiteral(["Missing url, please make sure to pass the url of your texture { url: ... }"])))));
214 | }
215 |
216 | var isImage = /(\.jpg|\.jpeg|\.png|\.gif|\.bmp)$/i.exec(url);
217 | var isVideo = /(\.mp4|\.3gp|\.webm|\.ogv)$/i.exec(url);
218 |
219 | if (isImage === null && isVideo === null) {
220 | return Promise.reject(new Error(SRLOG(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["Please upload a video or an image with a valid format"]))), url));
221 | }
222 |
223 | Object.assign(_this, {
224 | url: url,
225 | wrapS: wrapS,
226 | wrapT: wrapT,
227 | minFilter: minFilter,
228 | magFilter: magFilter,
229 | flipY: flipY
230 | });
231 | var level = 0;
232 | var internalFormat = gl.RGBA;
233 | var width = 1;
234 | var height = 1;
235 | var border = 0;
236 | var srcFormat = gl.RGBA;
237 | var srcType = gl.UNSIGNED_BYTE;
238 | var pixel = new Uint8Array([255, 255, 255, 0]);
239 | var texture = gl.createTexture();
240 | gl.bindTexture(gl.TEXTURE_2D, texture);
241 | gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, border, srcFormat, srcType, pixel);
242 |
243 | if (isVideo) {
244 | var video = _this.setupVideo(url);
245 |
246 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
247 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
248 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
249 | _this._webglTexture = texture;
250 | _this.source = video;
251 | _this.isVideo = true;
252 | return video.play().then(function () {
253 | return _this;
254 | });
255 | }
256 |
257 | return new Promise(function (resolve, reject) {
258 | var image = new Image();
259 | image.crossOrigin = 'anonymous';
260 |
261 | image.onload = function () {
262 | return resolve(image);
263 | };
264 |
265 | image.onerror = function () {
266 | return reject(new Error(SRLOG("failed loading url: ".concat(url))));
267 | };
268 |
269 | image.src = url;
270 | }).then(function (image) {
271 | var isPowerOfTwoImage = isPowerOf2(image.width) && isPowerOf2(image.height);
272 |
273 | if (textureNeedsPowerOfTwo(textureArgs) && isPowerOfTwoImage === false) {
274 | image = _this.makePowerOfTwo(image);
275 | isPowerOfTwoImage = true;
276 | }
277 |
278 | gl.bindTexture(gl.TEXTURE_2D, texture);
279 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
280 | gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, srcFormat, srcType, image);
281 |
282 | if (textureNeedsGenerateMipmaps(textureArgs, isPowerOfTwoImage)) {
283 | gl.generateMipmap(gl.TEXTURE_2D);
284 | }
285 |
286 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, _this.wrapS || RepeatWrapping);
287 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, _this.wrapT || RepeatWrapping);
288 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, _this.minFilter || LinearMipMapLinearFilter);
289 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, _this.magFilter || LinearFilter);
290 | _this._webglTexture = texture;
291 | _this.source = image;
292 | _this.isVideo = false;
293 | _this.isLoaded = true;
294 | _this.width = image.width || 0;
295 | _this.height = image.height || 0;
296 | return _this;
297 | });
298 | });
299 |
300 | this.gl = _gl;
301 | });
302 |
303 |
304 | ;// CONCATENATED MODULE: ./src/uniformsType.js
305 | var uniformsType_templateObject;
306 |
307 | function uniformsType_taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
308 |
309 |
310 | var INT = 'int';
311 | var FLOAT = 'float';
312 | var processUniform = function processUniform(gl, location, type, value) {
313 | switch (type) {
314 | case '1f':
315 | gl.uniform1f(location, value);
316 | break;
317 |
318 | case '2f':
319 | gl.uniform2f(location, value[0], value[1]);
320 | break;
321 |
322 | case '3f':
323 | gl.uniform3f(location, value[0], value[1], value[2]);
324 | break;
325 |
326 | case '4f':
327 | gl.uniform4f(location, value[0], value[1], value[2], value[3]);
328 | break;
329 |
330 | case '1i':
331 | gl.uniform1i(location, value);
332 | break;
333 |
334 | case '2i':
335 | gl.uniform2i(location, value[0], value[1]);
336 | break;
337 |
338 | case '3i':
339 | gl.uniform3i(location, value[0], value[1], value[2]);
340 | break;
341 |
342 | case '4i':
343 | gl.uniform3i(location, value[0], value[1], value[2], value[3]);
344 | break;
345 |
346 | case '1iv':
347 | gl.uniform1iv(location, value);
348 | break;
349 |
350 | case '2iv':
351 | gl.uniform2iv(location, value);
352 | break;
353 |
354 | case '3iv':
355 | gl.uniform3iv(location, value);
356 | break;
357 |
358 | case '4iv':
359 | gl.uniform4iv(location, value);
360 | break;
361 |
362 | case '1fv':
363 | gl.uniform1fv(location, value);
364 | break;
365 |
366 | case '2fv':
367 | gl.uniform2fv(location, value);
368 | break;
369 |
370 | case '3fv':
371 | gl.uniform3fv(location, value);
372 | break;
373 |
374 | case '4fv':
375 | gl.uniform4fv(location, value);
376 | break;
377 |
378 | case 'Matrix2fv':
379 | gl.uniformMatrix2fv(location, false, value);
380 | break;
381 |
382 | case 'Matrix3fv':
383 | gl.uniformMatrix3fv(location, false, value);
384 | break;
385 |
386 | case 'Matrix4fv':
387 | gl.uniformMatrix4fv(location, false, value);
388 | break;
389 |
390 | default:
391 | break;
392 | }
393 | };
394 | var uniformTypeToGLSLType = function uniformTypeToGLSLType(type) {
395 | switch (type) {
396 | case '1f':
397 | return FLOAT;
398 |
399 | case '2f':
400 | return 'vec2';
401 |
402 | case '3f':
403 | return 'vec3';
404 |
405 | case '4f':
406 | return 'vec4';
407 |
408 | case '1i':
409 | return INT;
410 |
411 | case '2i':
412 | return 'ivec2';
413 |
414 | case '3i':
415 | return 'ivec3';
416 |
417 | case '4i':
418 | return 'ivec4';
419 |
420 | case '1iv':
421 | return INT;
422 |
423 | case '2iv':
424 | return 'ivec2';
425 |
426 | case '3iv':
427 | return 'ivec3';
428 |
429 | case '4iv':
430 | return 'ivec4';
431 |
432 | case '1fv':
433 | return 'float';
434 |
435 | case '2fv':
436 | return 'vec2';
437 |
438 | case '3fv':
439 | return 'vec3';
440 |
441 | case '4fv':
442 | return 'vec4';
443 |
444 | case 'Matrix2fv':
445 | return 'mat2';
446 | break;
447 |
448 | case 'Matrix3fv':
449 | return 'mat3';
450 |
451 | case 'Matrix4fv':
452 | return 'mat4';
453 |
454 | default:
455 | console.error(SRLOG(uniformsType_templateObject || (uniformsType_templateObject = uniformsType_taggedTemplateLiteral(["The uniform type \"", "\" is not valid, please make sure your uniform type is valid"])), type));
456 | }
457 | };
458 | ;// CONCATENATED MODULE: ./src/index.jsx
459 | function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
460 |
461 | var src_templateObject, src_templateObject2;
462 |
463 | function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
464 |
465 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { src_defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
466 |
467 | function src_taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
468 |
469 | function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
470 |
471 | function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
472 |
473 | function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
474 |
475 | function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
476 |
477 | function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
478 |
479 | function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
480 |
481 | function src_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
482 |
483 | function src_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
484 |
485 | function src_createClass(Constructor, protoProps, staticProps) { if (protoProps) src_defineProperties(Constructor.prototype, protoProps); if (staticProps) src_defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
486 |
487 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
488 |
489 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
490 |
491 | function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
492 |
493 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
494 |
495 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
496 |
497 | function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
498 |
499 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
500 |
501 | function src_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
502 |
503 |
504 |
505 |
506 |
507 |
508 | var PRECISIONS = ["lowp", "mediump", "highp"];
509 | var FS_MAIN_SHADER = "\nvoid main(void){\n vec4 color = vec4(0.0,0.0,0.0,1.0);\n mainImage( color, gl_FragCoord.xy );\n gl_FragColor = color;\n}";
510 | var BASIC_FS = // Basic shadertoy shader
511 | "void mainImage( out vec4 fragColor, in vec2 fragCoord ) {\n vec2 uv = fragCoord/iResolution.xy;\n vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));\n fragColor = vec4(col,1.0);\n}";
512 | var BASIC_VS = "attribute vec3 aVertexPosition;\nvoid main(void) {\n gl_Position = vec4(aVertexPosition, 1.0);\n}"; // Shadertoy built-in uniforms
513 |
514 | var UNIFORM_TIME = "iTime";
515 | var UNIFORM_TIMEDELTA = "iTimeDelta";
516 | var UNIFORM_DATE = "iDate";
517 | var UNIFORM_FRAME = "iFrame";
518 | var UNIFORM_MOUSE = "iMouse";
519 | var UNIFORM_RESOLUTION = "iResolution";
520 | var UNIFORM_CHANNEL = "iChannel";
521 | var UNIFORM_CHANNELRESOLUTION = "iChannelResolution"; // Uniforms not built-int in shadertoy
522 |
523 | var UNIFORM_DEVICEORIENTATION = "iDeviceOrientation";
524 | /* eslint-disable */
525 |
526 | var lerpVal = function lerpVal(v0, v1, t) {
527 | return v0 * (1 - t) + v1 * t;
528 | };
529 |
530 | var insertStringAtIndex = function insertStringAtIndex(currentString, string, index) {
531 | return index > 0 ? currentString.substring(0, index) + string + currentString.substring(index, currentString.length) : string + currentString;
532 | };
533 |
534 | var ShadertoyReact = /*#__PURE__*/function (_Component) {
535 | _inherits(ShadertoyReact, _Component);
536 |
537 | var _super = _createSuper(ShadertoyReact);
538 |
539 | function ShadertoyReact(props) {
540 | var _this$uniforms;
541 |
542 | var _this;
543 |
544 | src_classCallCheck(this, ShadertoyReact);
545 |
546 | _this = _super.call(this, props);
547 |
548 | src_defineProperty(_assertThisInitialized(_this), "componentDidMount", function () {
549 | _this.initWebGL();
550 |
551 | var _this$props = _this.props,
552 | fs = _this$props.fs,
553 | vs = _this$props.vs,
554 | _this$props$clearColo = _this$props.clearColor,
555 | clearColor = _this$props$clearColo === void 0 ? [0, 0, 0, 1] : _this$props$clearColo;
556 |
557 | var _assertThisInitialize = _assertThisInitialized(_this),
558 | gl = _assertThisInitialize.gl;
559 |
560 | if (gl) {
561 | gl.clearColor.apply(gl, _toConsumableArray(clearColor));
562 | gl.clearDepth(1.0);
563 | gl.enable(gl.DEPTH_TEST);
564 | gl.depthFunc(gl.LEQUAL);
565 | gl.viewport(0, 0, _this.canvas.width, _this.canvas.height);
566 | _this.canvas.height = _this.canvas.clientHeight;
567 | _this.canvas.width = _this.canvas.clientWidth;
568 |
569 | _this.processCustomUniforms();
570 |
571 | _this.processTextures();
572 |
573 | var shaders = _this.preProcessShaders(fs || BASIC_FS, vs || BASIC_VS);
574 |
575 | _this.initShaders(shaders);
576 |
577 | _this.initBuffers();
578 |
579 | _this.drawScene();
580 |
581 | _this.addEventListeners();
582 |
583 | _this.onResize();
584 | }
585 | });
586 |
587 | src_defineProperty(_assertThisInitialized(_this), "shouldComponentUpdate", function () {
588 | return false;
589 | });
590 |
591 | src_defineProperty(_assertThisInitialized(_this), "setupChannelRes", function (_ref, id) {
592 | var width = _ref.width,
593 | height = _ref.height;
594 | var _this$props$devicePix = _this.props.devicePixelRatio,
595 | devicePixelRatio = _this$props$devicePix === void 0 ? 1 : _this$props$devicePix;
596 | _this.uniforms.iChannelResolution.value[id * 3] = width * devicePixelRatio;
597 | _this.uniforms.iChannelResolution.value[id * 3 + 1] = height * devicePixelRatio;
598 | _this.uniforms.iChannelResolution.value[id * 3 + 2] = 0; // console.log(this.uniforms);
599 | });
600 |
601 | src_defineProperty(_assertThisInitialized(_this), "initWebGL", function () {
602 | var contextAttributes = _this.props.contextAttributes; // $FlowFixMe
603 |
604 | _this.gl = _this.canvas.getContext("webgl", contextAttributes) || _this.canvas.getContext("experimental-webgl", contextAttributes); // $FlowFixMe
605 |
606 | _this.gl.getExtension("OES_standard_derivatives"); // $FlowFixMe
607 |
608 |
609 | _this.gl.getExtension("EXT_shader_texture_lod");
610 | });
611 |
612 | src_defineProperty(_assertThisInitialized(_this), "initBuffers", function () {
613 | var _assertThisInitialize2 = _assertThisInitialized(_this),
614 | gl = _assertThisInitialize2.gl;
615 |
616 | _this.squareVerticesBuffer = gl.createBuffer();
617 | gl.bindBuffer(gl.ARRAY_BUFFER, _this.squareVerticesBuffer);
618 | var vertices = [1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, -1.0, -1.0, 0.0];
619 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
620 | });
621 |
622 | src_defineProperty(_assertThisInitialized(_this), "addEventListeners", function () {
623 | var options = {
624 | passive: true
625 | };
626 |
627 | if (_this.uniforms.iMouse.isNeeded) {
628 | _this.canvas.addEventListener("mousemove", _this.mouseMove, options);
629 |
630 | _this.canvas.addEventListener("mouseout", _this.mouseUp, options);
631 |
632 | _this.canvas.addEventListener("mouseup", _this.mouseUp, options);
633 |
634 | _this.canvas.addEventListener("mousedown", _this.mouseDown, options);
635 |
636 | _this.canvas.addEventListener("touchmove", _this.mouseMove, options);
637 |
638 | _this.canvas.addEventListener("touchend", _this.mouseUp, options);
639 |
640 | _this.canvas.addEventListener("touchstart", _this.mouseDown, options);
641 | }
642 |
643 | if (_this.uniforms.iDeviceOrientation.isNeeded) {
644 | window.addEventListener("deviceorientation", _this.onDeviceOrientationChange, options);
645 | }
646 |
647 | window.addEventListener("resize", _this.onResize, options);
648 | });
649 |
650 | src_defineProperty(_assertThisInitialized(_this), "removeEventListeners", function () {
651 | var options = {
652 | passive: true
653 | };
654 |
655 | if (_this.uniforms.iMouse.isNeeded) {
656 | _this.canvas.removeEventListener("mousemove", _this.mouseMove, options);
657 |
658 | _this.canvas.removeEventListener("mouseout", _this.mouseUp, options);
659 |
660 | _this.canvas.removeEventListener("mouseup", _this.mouseUp, options);
661 |
662 | _this.canvas.removeEventListener("mousedown", _this.mouseDown, options);
663 |
664 | _this.canvas.removeEventListener("touchmove", _this.mouseMove, options);
665 |
666 | _this.canvas.removeEventListener("touchend", _this.mouseUp, options);
667 |
668 | _this.canvas.removeEventListener("touchstart", _this.mouseDown, options);
669 | }
670 |
671 | if (_this.uniforms.iDeviceOrientation.isNeeded) {
672 | window.removeEventListener("deviceorientation", _this.onDeviceOrientationChange, options);
673 | }
674 |
675 | window.removeEventListener("resize", _this.onResize, options);
676 | });
677 |
678 | src_defineProperty(_assertThisInitialized(_this), "onDeviceOrientationChange", function (_ref2) {
679 | var alpha = _ref2.alpha,
680 | beta = _ref2.beta,
681 | gamma = _ref2.gamma;
682 | _this.uniforms.iDeviceOrientation.value = [alpha, beta, gamma, window.orientation || 0];
683 | });
684 |
685 | src_defineProperty(_assertThisInitialized(_this), "mouseDown", function (e) {
686 | var clientX = e.clientX || e.changedTouches[0].clientX;
687 | var clientY = e.clientY || e.changedTouches[0].clientY;
688 | var mouseX = clientX - _this.canvasPosition.left - window.pageXOffset;
689 | var mouseY = _this.canvasPosition.height - clientY - _this.canvasPosition.top - window.pageYOffset;
690 | _this.mousedown = true;
691 | _this.uniforms.iMouse.value[2] = mouseX;
692 | _this.uniforms.iMouse.value[3] = mouseY;
693 | _this.lastMouseArr[0] = mouseX;
694 | _this.lastMouseArr[1] = mouseY;
695 | });
696 |
697 | src_defineProperty(_assertThisInitialized(_this), "mouseMove", function (e) {
698 | _this.canvasPosition = _this.canvas.getBoundingClientRect();
699 | var _this$props$lerp = _this.props.lerp,
700 | lerp = _this$props$lerp === void 0 ? 1 : _this$props$lerp;
701 | var clientX = e.clientX || e.changedTouches[0].clientX;
702 | var clientY = e.clientY || e.changedTouches[0].clientY;
703 | var mouseX = clientX - _this.canvasPosition.left;
704 | var mouseY = _this.canvasPosition.height - clientY - _this.canvasPosition.top;
705 |
706 | if (lerp !== 1) {
707 | _this.lastMouseArr[0] = mouseX;
708 | _this.lastMouseArr[1] = mouseY;
709 | } else {
710 | _this.uniforms.iMouse.value[0] = mouseX;
711 | _this.uniforms.iMouse.value[1] = mouseY;
712 | }
713 | });
714 |
715 | src_defineProperty(_assertThisInitialized(_this), "mouseUp", function (e) {
716 | _this.uniforms.iMouse.value[2] = 0;
717 | _this.uniforms.iMouse.value[3] = 0;
718 | });
719 |
720 | src_defineProperty(_assertThisInitialized(_this), "onResize", function () {
721 | var _assertThisInitialize3 = _assertThisInitialized(_this),
722 | gl = _assertThisInitialize3.gl;
723 |
724 | var _this$props$devicePix2 = _this.props.devicePixelRatio,
725 | devicePixelRatio = _this$props$devicePix2 === void 0 ? 1 : _this$props$devicePix2;
726 | _this.canvasPosition = _this.canvas.getBoundingClientRect();
727 | var realToCSSPixels = devicePixelRatio; // Force pixel ratio to be one to avoid expensive calculus on retina display
728 |
729 | var displayWidth = Math.floor(_this.canvasPosition.width * realToCSSPixels);
730 | var displayHeight = Math.floor(_this.canvasPosition.height * realToCSSPixels);
731 | gl.canvas.width = displayWidth;
732 | gl.canvas.height = displayHeight;
733 |
734 | if (_this.uniforms.iResolution.isNeeded) {
735 | var rUniform = gl.getUniformLocation(_this.shaderProgram, UNIFORM_RESOLUTION); // $FlowFixMe
736 |
737 | gl.uniform2fv(rUniform, [gl.canvas.width, gl.canvas.height]);
738 | }
739 | });
740 |
741 | src_defineProperty(_assertThisInitialized(_this), "drawScene", function (timestamp) {
742 | var _assertThisInitialize4 = _assertThisInitialized(_this),
743 | gl = _assertThisInitialize4.gl;
744 |
745 | var _this$props$lerp2 = _this.props.lerp,
746 | lerp = _this$props$lerp2 === void 0 ? 1 : _this$props$lerp2;
747 | gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
748 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // eslint-disable-line no-bitwise
749 |
750 | gl.bindBuffer(gl.ARRAY_BUFFER, _this.squareVerticesBuffer);
751 | gl.vertexAttribPointer(_this.vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
752 |
753 | _this.setUniforms(timestamp);
754 |
755 | gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
756 |
757 | if (_this.uniforms.iMouse.isNeeded && lerp !== 1) {
758 | _this.uniforms.iMouse.value[0] = lerpVal(_this.uniforms.iMouse.value[0], _this.lastMouseArr[0], lerp);
759 | _this.uniforms.iMouse.value[1] = lerpVal(_this.uniforms.iMouse.value[1], _this.lastMouseArr[1], lerp);
760 | }
761 |
762 | _this.animFrameId = requestAnimationFrame(_this.drawScene);
763 | });
764 |
765 | src_defineProperty(_assertThisInitialized(_this), "createShader", function (type, shaderCodeAsText) {
766 | var _assertThisInitialize5 = _assertThisInitialized(_this),
767 | gl = _assertThisInitialize5.gl;
768 |
769 | var shader = gl.createShader(type);
770 | gl.shaderSource(shader, shaderCodeAsText);
771 | gl.compileShader(shader);
772 | /* eslint-disable no-console */
773 |
774 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
775 | console.warn(SRLOG(src_templateObject || (src_templateObject = src_taggedTemplateLiteral(["Error compiling the shader:"]))), shaderCodeAsText);
776 | var compilationLog = gl.getShaderInfoLog(shader);
777 | gl.deleteShader(shader);
778 | console.error(SRLOG("Shader compiler log: ".concat(compilationLog)));
779 | }
780 | /* eslint-enable no-console */
781 |
782 |
783 | return shader;
784 | });
785 |
786 | src_defineProperty(_assertThisInitialized(_this), "initShaders", function (_ref3) {
787 | var fs = _ref3.fs,
788 | vs = _ref3.vs;
789 |
790 | var _assertThisInitialize6 = _assertThisInitialized(_this),
791 | gl = _assertThisInitialize6.gl; // console.log(fs, vs);
792 |
793 |
794 | var fragmentShader = _this.createShader(gl.FRAGMENT_SHADER, fs);
795 |
796 | var vertexShader = _this.createShader(gl.VERTEX_SHADER, vs);
797 |
798 | _this.shaderProgram = gl.createProgram();
799 | gl.attachShader(_this.shaderProgram, vertexShader);
800 | gl.attachShader(_this.shaderProgram, fragmentShader);
801 | gl.linkProgram(_this.shaderProgram);
802 | /* eslint-disable no-console */
803 |
804 | if (!gl.getProgramParameter(_this.shaderProgram, gl.LINK_STATUS)) {
805 | // $FlowFixMe
806 | console.error(SRLOG("Unable to initialize the shader program: ".concat(gl.getProgramInfoLog(_this.shaderProgram))));
807 | return;
808 | }
809 | /* eslint-enable no-console */
810 |
811 |
812 | gl.useProgram(_this.shaderProgram);
813 | _this.vertexPositionAttribute = gl.getAttribLocation(_this.shaderProgram, "aVertexPosition");
814 | gl.enableVertexAttribArray(_this.vertexPositionAttribute);
815 | });
816 |
817 | src_defineProperty(_assertThisInitialized(_this), "processCustomUniforms", function () {
818 | var uniforms = _this.props.uniforms;
819 |
820 | if (uniforms) {
821 | Object.keys(uniforms).forEach(function (name, id) {
822 | var _this$props$uniforms$ = _this.props.uniforms[name],
823 | value = _this$props$uniforms$.value,
824 | type = _this$props$uniforms$.type;
825 | var glslType = uniformTypeToGLSLType(type);
826 | if (!glslType) return; // If the type specified doesn't exist
827 |
828 | var tempObject = {};
829 |
830 | if (type.includes("Matrix")) {
831 | var arrayLength = type.length;
832 | var val = type.charAt(arrayLength - 3);
833 | var numberOfMatrices = Math.floor(value.length / (val * val));
834 |
835 | if (value.length > val * val) {
836 | tempObject.arraySize = "[".concat(numberOfMatrices, "]");
837 | }
838 | } else if (type.includes("v") && value.length > type.charAt(0)) {
839 | tempObject.arraySize = "[".concat(Math.floor(value.length / type.charAt(0)), "]");
840 | }
841 |
842 | _this.uniforms[name] = _objectSpread({
843 | type: glslType,
844 | isNeeded: false,
845 | value: value
846 | }, tempObject);
847 | });
848 | }
849 | });
850 |
851 | src_defineProperty(_assertThisInitialized(_this), "processTextures", function () {
852 | var _assertThisInitialize7 = _assertThisInitialized(_this),
853 | gl = _assertThisInitialize7.gl;
854 |
855 | var _this$props2 = _this.props,
856 | textures = _this$props2.textures,
857 | onDoneLoadingTextures = _this$props2.onDoneLoadingTextures;
858 |
859 | if (textures && textures.length > 0) {
860 | _this.uniforms["".concat(UNIFORM_CHANNELRESOLUTION)] = {
861 | type: "vec3",
862 | isNeeded: false,
863 | arraySize: "[".concat(textures.length, "]"),
864 | value: []
865 | };
866 | var texturePromisesArr = textures.map(function (texture, id) {
867 | _this.uniforms["".concat(UNIFORM_CHANNEL).concat(id)] = {
868 | type: "sampler2D",
869 | isNeeded: false
870 | }; // Dynamically add textures uniforms
871 |
872 | _this.setupChannelRes(texture, id); // initialize array with 0s
873 |
874 |
875 | _this.texturesArr[id] = new Texture(gl);
876 | return _this.texturesArr[id].load(texture, id).then(function (texture) {
877 | return _this.setupChannelRes(texture, id);
878 | });
879 | });
880 | Promise.all(texturePromisesArr).then(function () {
881 | return onDoneLoadingTextures && onDoneLoadingTextures();
882 | })["catch"](function (e) {
883 | console.error(e);
884 | if (onDoneLoadingTextures) onDoneLoadingTextures();
885 | });
886 | } else {
887 | if (onDoneLoadingTextures) onDoneLoadingTextures();
888 | }
889 | });
890 |
891 | src_defineProperty(_assertThisInitialized(_this), "preProcessShaders", function (fs, vs) {
892 | var _this$props3 = _this.props,
893 | precision = _this$props3.precision,
894 | _this$props3$devicePi = _this$props3.devicePixelRatio,
895 | devicePixelRatio = _this$props3$devicePi === void 0 ? 1 : _this$props3$devicePi;
896 | var dprString = "#define DPR ".concat(devicePixelRatio.toFixed(1), "\n");
897 | var isValidPrecision = PRECISIONS.includes(precision);
898 | var precisionString = "precision ".concat(isValidPrecision ? precision : PRECISIONS[1], " float;\n");
899 | if (!isValidPrecision) console.warn(SRLOG(src_templateObject2 || (src_templateObject2 = src_taggedTemplateLiteral(["wrong precision type ", ", please make sure to pass one of a valid precision lowp, mediump, highp, by default you shader precision will be set to highp."])), precision));
900 | var fsString = precisionString.concat(dprString).concat(fs).replace(/texture\(/g, "texture2D(");
901 | var indexOfPrecisionString = fsString.lastIndexOf(precisionString);
902 | Object.keys(_this.uniforms).forEach(function (uniform) {
903 | if (fs.includes(uniform)) {
904 | fsString = insertStringAtIndex(fsString, "uniform ".concat(_this.uniforms[uniform].type, " ").concat(uniform).concat(_this.uniforms[uniform].arraySize || "", "; \n"), indexOfPrecisionString + precisionString.length);
905 | _this.uniforms[uniform].isNeeded = true;
906 | }
907 | });
908 | var isShadertoy = /mainImage/.test(fs);
909 | if (isShadertoy) fsString = fsString.concat(FS_MAIN_SHADER); // console.log(fsString);
910 |
911 | return {
912 | fs: fsString,
913 | vs: vs
914 | };
915 | });
916 |
917 | src_defineProperty(_assertThisInitialized(_this), "setUniforms", function (timestamp) {
918 | var _assertThisInitialize8 = _assertThisInitialized(_this),
919 | gl = _assertThisInitialize8.gl;
920 |
921 | var delta = _this.lastTime ? (timestamp - _this.lastTime) / 1000 : 0;
922 | _this.lastTime = timestamp;
923 |
924 | if (_this.props.uniforms) {
925 | Object.keys(_this.props.uniforms).forEach(function (name) {
926 | var currentUniform = _this.props.uniforms[name];
927 |
928 | if (_this.uniforms[name].isNeeded) {
929 | var customUniformLocation = gl.getUniformLocation(_this.shaderProgram, name);
930 | processUniform(gl, customUniformLocation, currentUniform.type, currentUniform.value);
931 | }
932 | });
933 | }
934 |
935 | if (_this.uniforms.iMouse.isNeeded) {
936 | var mouseUniform = gl.getUniformLocation(_this.shaderProgram, UNIFORM_MOUSE); // $FlowFixMe
937 |
938 | gl.uniform4fv(mouseUniform, _this.uniforms.iMouse.value);
939 | }
940 |
941 | if (_this.uniforms.iChannelResolution && _this.uniforms.iChannelResolution.isNeeded) {
942 | var channelResUniform = gl.getUniformLocation(_this.shaderProgram, UNIFORM_CHANNELRESOLUTION);
943 | gl.uniform3fv(channelResUniform, _this.uniforms.iChannelResolution.value);
944 | }
945 |
946 | if (_this.uniforms.iDeviceOrientation.isNeeded) {
947 | var deviceOrientationUniform = gl.getUniformLocation(_this.shaderProgram, UNIFORM_DEVICEORIENTATION);
948 | gl.uniform4fv(deviceOrientationUniform, _this.uniforms.iDeviceOrientation.value);
949 | }
950 |
951 | if (_this.uniforms.iTime.isNeeded) {
952 | var timeUniform = gl.getUniformLocation(_this.shaderProgram, UNIFORM_TIME);
953 | gl.uniform1f(timeUniform, _this.timer += delta);
954 | }
955 |
956 | if (_this.uniforms.iTimeDelta.isNeeded) {
957 | var timeDeltaUniform = gl.getUniformLocation(_this.shaderProgram, UNIFORM_TIMEDELTA);
958 | gl.uniform1f(timeDeltaUniform, delta);
959 | }
960 |
961 | if (_this.uniforms.iDate.isNeeded) {
962 | var d = new Date();
963 | var month = d.getMonth() + 1;
964 | var day = d.getDate();
965 | var year = d.getFullYear();
966 | var time = d.getHours() * 60 * 60 + d.getMinutes() * 60 + d.getSeconds() + d.getMilliseconds() * 0.001;
967 | var dateUniform = gl.getUniformLocation(_this.shaderProgram, UNIFORM_DATE);
968 | gl.uniform4fv(dateUniform, [year, month, day, time]);
969 | }
970 |
971 | if (_this.uniforms.iFrame.isNeeded) {
972 | var _timeDeltaUniform = gl.getUniformLocation(_this.shaderProgram, UNIFORM_FRAME);
973 |
974 | gl.uniform1i(_timeDeltaUniform, _this.uniforms.iFrame.value++);
975 | }
976 |
977 | if (_this.texturesArr.length > 0) {
978 | _this.texturesArr.forEach(function (texture, id) {
979 | var isVideo = texture.isVideo,
980 | _webglTexture = texture._webglTexture,
981 | source = texture.source,
982 | flipY = texture.flipY,
983 | isLoaded = texture.isLoaded;
984 | if (!isLoaded) return;
985 |
986 | if (_this.uniforms["iChannel".concat(id)].isNeeded) {
987 | var iChannel = gl.getUniformLocation(_this.shaderProgram, "iChannel".concat(id));
988 | gl.activeTexture(gl["TEXTURE".concat(id)]);
989 | gl.bindTexture(gl.TEXTURE_2D, _webglTexture);
990 | gl.uniform1i(iChannel, id);
991 | if (isVideo) texture.updateTexture(_webglTexture, source, flipY);
992 | }
993 | });
994 | }
995 | });
996 |
997 | src_defineProperty(_assertThisInitialized(_this), "registerCanvas", function (r) {
998 | _this.canvas = r;
999 | });
1000 |
1001 | src_defineProperty(_assertThisInitialized(_this), "gl", void 0);
1002 |
1003 | src_defineProperty(_assertThisInitialized(_this), "squareVerticesBuffer", void 0);
1004 |
1005 | src_defineProperty(_assertThisInitialized(_this), "shaderProgram", void 0);
1006 |
1007 | src_defineProperty(_assertThisInitialized(_this), "vertexPositionAttribute", void 0);
1008 |
1009 | src_defineProperty(_assertThisInitialized(_this), "animFrameId", void 0);
1010 |
1011 | src_defineProperty(_assertThisInitialized(_this), "timeoutId", void 0);
1012 |
1013 | src_defineProperty(_assertThisInitialized(_this), "canvas", void 0);
1014 |
1015 | src_defineProperty(_assertThisInitialized(_this), "mousedown", false);
1016 |
1017 | src_defineProperty(_assertThisInitialized(_this), "canvasPosition", void 0);
1018 |
1019 | src_defineProperty(_assertThisInitialized(_this), "timer", 0);
1020 |
1021 | src_defineProperty(_assertThisInitialized(_this), "lastMouseArr", [0, 0]);
1022 |
1023 | src_defineProperty(_assertThisInitialized(_this), "texturesArr", []);
1024 |
1025 | src_defineProperty(_assertThisInitialized(_this), "lastTime", 0);
1026 |
1027 | src_defineProperty(_assertThisInitialized(_this), "render", function () {
1028 | var style = _this.props.style;
1029 | var currentStyle = {
1030 | glCanvas: _objectSpread({
1031 | height: "100%",
1032 | width: "100%"
1033 | }, style)
1034 | };
1035 | return /*#__PURE__*/external_react_default().createElement("canvas", {
1036 | style: currentStyle.glCanvas,
1037 | ref: _this.registerCanvas
1038 | });
1039 | });
1040 |
1041 | _this.uniforms = (_this$uniforms = {}, src_defineProperty(_this$uniforms, UNIFORM_TIME, {
1042 | type: "float",
1043 | isNeeded: false,
1044 | value: 0
1045 | }), src_defineProperty(_this$uniforms, UNIFORM_TIMEDELTA, {
1046 | type: "float",
1047 | isNeeded: false,
1048 | value: 0
1049 | }), src_defineProperty(_this$uniforms, UNIFORM_DATE, {
1050 | type: "vec4",
1051 | isNeeded: false,
1052 | value: [0, 0, 0, 0]
1053 | }), src_defineProperty(_this$uniforms, UNIFORM_MOUSE, {
1054 | type: "vec4",
1055 | isNeeded: false,
1056 | value: [0, 0, 0, 0]
1057 | }), src_defineProperty(_this$uniforms, UNIFORM_RESOLUTION, {
1058 | type: "vec2",
1059 | isNeeded: false,
1060 | value: [0, 0]
1061 | }), src_defineProperty(_this$uniforms, UNIFORM_FRAME, {
1062 | type: "int",
1063 | isNeeded: false,
1064 | value: 0
1065 | }), src_defineProperty(_this$uniforms, UNIFORM_DEVICEORIENTATION, {
1066 | type: "vec4",
1067 | isNeeded: false,
1068 | value: [0, 0, 0, 0]
1069 | }), _this$uniforms);
1070 | return _this;
1071 | }
1072 |
1073 | src_createClass(ShadertoyReact, [{
1074 | key: "componentWillUnmount",
1075 | value: function componentWillUnmount() {
1076 | var gl = this.gl;
1077 |
1078 | if (gl) {
1079 | gl.getExtension("WEBGL_lose_context").loseContext();
1080 | gl.useProgram(null);
1081 | gl.deleteProgram(this.shaderProgram);
1082 |
1083 | if (this.texturesArr.length > 0) {
1084 | this.texturesArr.forEach(function (texture) {
1085 | gl.deleteTexture(texture._webglTexture);
1086 | });
1087 | }
1088 |
1089 | this.shaderProgram = null;
1090 | }
1091 |
1092 | this.removeEventListeners();
1093 | cancelAnimationFrame(this.animFrameId);
1094 | }
1095 | }]);
1096 |
1097 | return ShadertoyReact;
1098 | }(external_react_namespaceObject.Component);
1099 |
1100 | src_defineProperty(ShadertoyReact, "defaultProps", {
1101 | textures: [],
1102 | contextAttributes: {},
1103 | devicePixelRatio: 1,
1104 | vs: BASIC_VS,
1105 | precision: "highp"
1106 | });
1107 |
1108 |
1109 | module.exports = __webpack_exports__;
1110 | /******/ })()
1111 | ;
--------------------------------------------------------------------------------
/lib/shadertoy-react.min.js:
--------------------------------------------------------------------------------
1 | (()=>{"use strict";var n={n:e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return n.d(r,{a:r}),r},d:(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},o:(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},e={};n.r(e),n.d(e,{ClampToEdgeWrapping:()=>x,LinearFilter:()=>T,LinearMipMapLinearFilter:()=>P,LinearMipMapNearestFilter:()=>f,MirroredRepeatWrapping:()=>v,NearestFilter:()=>y,NearestMipMapLinearFilter:()=>l,NearestMipMapNearestFilter:()=>c,RepeatWrapping:()=>R,default:()=>q});var h,g,o=require("react"),a=n.n(o),b=function(e){return"shadertoy-react: ".concat(e)};function w(e,r){return r=r||e.slice(0),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(r)}}))}function i(e,r){for(var t=0;te.length)&&(r=e.length);for(var t=0,n=new Array(r);ti*i&&(t.arraySize="[".concat(n,"]"))):a.includes("v")&&o.length>a.charAt(0)&&(t.arraySize="[".concat(Math.floor(o.length/a.charAt(0)),"]")),u.uniforms[e]=S({type:s,isNeeded:!1,value:o},t))})}),k(I(u),"processTextures",function(){var t=I(u).gl,e=u.props,r=e.textures,n=e.onDoneLoadingTextures;r&&0",
25 | "license": "MIT",
26 | "devDependencies": {
27 | "@babel/cli": "^7.16.7",
28 | "@babel/core": "^7.16.7",
29 | "@babel/plugin-proposal-class-properties": "^7.16.7",
30 | "@babel/plugin-proposal-object-rest-spread": "^7.16.7",
31 | "@babel/preset-env": "^7.16.7",
32 | "@babel/preset-flow": "^7.16.7",
33 | "@babel/preset-react": "^7.16.7",
34 | "babel-loader": "^8.2.3",
35 | "file-loader": "^6.2.0",
36 | "gh-pages": "^3.2.3",
37 | "html-webpack-plugin": "^5.5.0",
38 | "react": "^17.0.2",
39 | "react-dom": "^17.0.2",
40 | "styled-components": "^5.3.3",
41 | "uglifyjs-webpack-plugin": "^2.2.0",
42 | "webpack": "^5.65.0",
43 | "webpack-cli": "^4.9.1",
44 | "webpack-dev-server": "^4.7.2"
45 | },
46 | "peerDependencies": {
47 | "react": "^17.0.2",
48 | "react-dom": "^17.0.2"
49 | },
50 | "keywords": [
51 | "shadertoy",
52 | "shader",
53 | "glsl",
54 | "webgl",
55 | "react"
56 | ]
57 | }
58 |
--------------------------------------------------------------------------------
/src/Texture.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import {
4 | APP_NAME
5 | } from './index';
6 | import {
7 | SRLOG
8 | } from './prefixLogs';
9 |
10 | export const NearestFilter = 9728;
11 | export const LinearFilter = 9729;
12 | export const NearestMipMapNearestFilter = 9984;
13 | export const LinearMipMapNearestFilter = 9985;
14 | export const NearestMipMapLinearFilter = 9986;
15 | export const LinearMipMapLinearFilter = 9987;
16 | export const ClampToEdgeWrapping = 33071;
17 | export const MirroredRepeatWrapping = 33648;
18 | export const RepeatWrapping = 10497;
19 |
20 | // eslint-disable-next-line
21 | const isPowerOf2 = (value: number) => (value & (value - 1)) == 0;
22 | const floorPowerOfTwo = (value: number) =>
23 | 2 ** Math.floor(Math.log(value) / Math.LN2);
24 | const textureNeedsGenerateMipmaps = (
25 | texture: TextureType,
26 | isPowerOfTwo: boolean
27 | ) =>
28 | isPowerOfTwo &&
29 | texture.minFilter !== NearestFilter &&
30 | texture.minFilter !== LinearFilter;
31 | const textureNeedsPowerOfTwo = (texture: TextureType) => {
32 | if (
33 | texture.wrapS !== ClampToEdgeWrapping ||
34 | texture.wrapT !== ClampToEdgeWrapping
35 | )
36 | return true;
37 | if (texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter)
38 | return true;
39 | return false;
40 | };
41 |
42 | export default class Texture {
43 | constructor(gl) {
44 | this.gl = gl;
45 | }
46 |
47 | isLoaded: boolean = false;
48 | url: string;
49 | wrapS: number;
50 | wrapT: number;
51 | minFilter: number;
52 | magFilter: number;
53 | source: HTMLImageElement | HTMLVideoElement;
54 | flipY: number = -1;
55 | width: number = 0;
56 | height: number = 0;
57 | _webglTexture: WebGLTexture = null;
58 |
59 | updateTexture = (texture: WebGLTexture, video: HTMLVideoElement, flipY: boolean) => {
60 | const {
61 | gl
62 | } = this;
63 | const level = 0;
64 | const internalFormat = gl.RGBA;
65 | const srcFormat = gl.RGBA;
66 | const srcType = gl.UNSIGNED_BYTE;
67 | gl.bindTexture(gl.TEXTURE_2D, texture);
68 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
69 | gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, srcFormat, srcType, video);
70 | }
71 |
72 | setupVideo = (url: string) => {
73 | const video = document.createElement('video');
74 |
75 | let playing = false;
76 | let timeupdate = false;
77 |
78 | video.autoplay = true;
79 | video.muted = true;
80 | video.loop = true;
81 | video.crossOrigin = 'anonymous';
82 |
83 | const checkReady = () => {
84 | if (playing && timeupdate) {
85 | this.isLoaded = true;
86 | }
87 | }
88 |
89 | video.addEventListener('playing', () => {
90 | playing = true;
91 | this.width = video.videoWidth || 0;
92 | this.height = video.videoHeight || 0;
93 | checkReady();
94 | }, true);
95 |
96 | video.addEventListener('timeupdate', () => {
97 | timeupdate = true;
98 | checkReady();
99 | }, true);
100 |
101 | video.src = url;
102 | // video.play();
103 |
104 | return video;
105 | }
106 |
107 | makePowerOfTwo = (
108 | image: HTMLImageElement | HTMLCanvasElement | ImageBitmap
109 | ) => {
110 | if (
111 | image instanceof HTMLImageElement ||
112 | image instanceof HTMLCanvasElement ||
113 | image instanceof ImageBitmap
114 | ) {
115 | if (this.pow2canvas === undefined)
116 | this.pow2canvas = document.createElement('canvas');
117 |
118 | this.pow2canvas.width = floorPowerOfTwo(image.width);
119 | this.pow2canvas.height = floorPowerOfTwo(image.height);
120 |
121 | const context = this.pow2canvas.getContext('2d');
122 | context.drawImage(
123 | image,
124 | 0,
125 | 0,
126 | this.pow2canvas.width,
127 | this.pow2canvas.height
128 | );
129 |
130 | // eslint-disable-next-line
131 | console.warn(SRLOG(`Image is not power of two ${image.width} x ${
132 | image.height
133 | }. Resized to ${this.pow2canvas.width} x ${this.pow2canvas.height};`));
134 |
135 | return this.pow2canvas;
136 | }
137 | return image;
138 | };
139 |
140 | load = (textureArgs: TextureType, channelId: number) => {
141 | const {gl} = this;
142 |
143 | const {
144 | url,
145 | wrapS,
146 | wrapT,
147 | minFilter,
148 | magFilter,
149 | flipY = -1,
150 | }: TextureType = textureArgs;
151 |
152 | if (!url) {
153 | return Promise.reject(new Error(SRLOG `Missing url, please make sure to pass the url of your texture { url: ... }`));
154 | }
155 |
156 | const isImage = /(\.jpg|\.jpeg|\.png|\.gif|\.bmp)$/i.exec(url);
157 | const isVideo = /(\.mp4|\.3gp|\.webm|\.ogv)$/i.exec(url);
158 |
159 | if (isImage === null && isVideo === null) {
160 | return Promise.reject(new Error(SRLOG `Please upload a video or an image with a valid format`, url));
161 | }
162 |
163 | Object.assign(this, {
164 | url,
165 | wrapS,
166 | wrapT,
167 | minFilter,
168 | magFilter,
169 | flipY,
170 | });
171 |
172 | const level = 0;
173 | const internalFormat = gl.RGBA;
174 | const width = 1;
175 | const height = 1;
176 | const border = 0;
177 | const srcFormat = gl.RGBA;
178 | const srcType = gl.UNSIGNED_BYTE;
179 | const pixel = new Uint8Array([255, 255, 255, 0]);
180 |
181 | const texture = gl.createTexture();
182 | gl.bindTexture(gl.TEXTURE_2D, texture);
183 |
184 | gl.texImage2D(
185 | gl.TEXTURE_2D,
186 | level,
187 | internalFormat,
188 | width,
189 | height,
190 | border,
191 | srcFormat,
192 | srcType,
193 | pixel
194 | );
195 |
196 | if (isVideo) {
197 | const video = this.setupVideo(url);
198 |
199 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
200 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
201 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
202 |
203 | this._webglTexture = texture;
204 | this.source = video;
205 | this.isVideo = true;
206 |
207 | return video.play().then(() => this);
208 | }
209 |
210 | return new Promise((resolve, reject) => {
211 | let image = new Image();
212 | image.crossOrigin = 'anonymous';
213 | image.onload = () => resolve(image);
214 | image.onerror = () => reject(new Error(SRLOG(`failed loading url: ${url}`)));
215 | image.src = url;
216 | }).then(image => {
217 | let isPowerOfTwoImage =
218 | isPowerOf2(image.width) && isPowerOf2(image.height);
219 |
220 | if (textureNeedsPowerOfTwo(textureArgs) && isPowerOfTwoImage === false) {
221 | image = this.makePowerOfTwo(image);
222 | isPowerOfTwoImage = true;
223 | }
224 |
225 | gl.bindTexture(gl.TEXTURE_2D, texture);
226 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
227 | gl.texImage2D(
228 | gl.TEXTURE_2D,
229 | level,
230 | internalFormat,
231 | srcFormat,
232 | srcType,
233 | image
234 | );
235 |
236 | if (textureNeedsGenerateMipmaps(textureArgs, isPowerOfTwoImage)) {
237 | gl.generateMipmap(gl.TEXTURE_2D);
238 | }
239 |
240 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, this.wrapS || RepeatWrapping);
241 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, this.wrapT || RepeatWrapping);
242 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.minFilter || LinearMipMapLinearFilter);
243 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.magFilter || LinearFilter);
244 |
245 | this._webglTexture = texture;
246 | this.source = image;
247 | this.isVideo = false;
248 | this.isLoaded = true;
249 | this.width = image.width || 0;
250 | this.height = image.height || 0;
251 |
252 | return this;
253 | });
254 | }
255 | }
--------------------------------------------------------------------------------
/src/index.jsx:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { Component } from "react";
3 | import Texture, {
4 | NearestFilter,
5 | LinearFilter,
6 | NearestMipMapNearestFilter,
7 | LinearMipMapNearestFilter,
8 | NearestMipMapLinearFilter,
9 | LinearMipMapLinearFilter,
10 | ClampToEdgeWrapping,
11 | MirroredRepeatWrapping,
12 | RepeatWrapping,
13 | } from "./Texture";
14 |
15 | import { SRLOG } from "./prefixLogs";
16 |
17 | import { uniformTypeToGLSLType, processUniform } from "./uniformsType";
18 |
19 | export {
20 | NearestFilter,
21 | LinearFilter,
22 | NearestMipMapNearestFilter,
23 | LinearMipMapNearestFilter,
24 | NearestMipMapLinearFilter,
25 | LinearMipMapLinearFilter,
26 | ClampToEdgeWrapping,
27 | MirroredRepeatWrapping,
28 | RepeatWrapping,
29 | };
30 |
31 | const PRECISIONS = ["lowp", "mediump", "highp"];
32 |
33 | const FS_MAIN_SHADER = `\nvoid main(void){
34 | vec4 color = vec4(0.0,0.0,0.0,1.0);
35 | mainImage( color, gl_FragCoord.xy );
36 | gl_FragColor = color;
37 | }`;
38 |
39 | const BASIC_FS =
40 | // Basic shadertoy shader
41 | `void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
42 | vec2 uv = fragCoord/iResolution.xy;
43 | vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));
44 | fragColor = vec4(col,1.0);
45 | }`;
46 |
47 | const BASIC_VS = `attribute vec3 aVertexPosition;
48 | void main(void) {
49 | gl_Position = vec4(aVertexPosition, 1.0);
50 | }`;
51 |
52 | // Shadertoy built-in uniforms
53 | const UNIFORM_TIME = "iTime";
54 | const UNIFORM_TIMEDELTA = "iTimeDelta";
55 | const UNIFORM_DATE = "iDate";
56 | const UNIFORM_FRAME = "iFrame";
57 | const UNIFORM_MOUSE = "iMouse";
58 | const UNIFORM_RESOLUTION = "iResolution";
59 | const UNIFORM_CHANNEL = "iChannel";
60 | const UNIFORM_CHANNELRESOLUTION = "iChannelResolution";
61 |
62 | // Uniforms not built-int in shadertoy
63 | const UNIFORM_DEVICEORIENTATION = "iDeviceOrientation";
64 |
65 | /* eslint-disable */
66 | type TexturePropsType = {
67 | url: string,
68 | wrapS?: number,
69 | wrapT?: number,
70 | minFilter?: number,
71 | magFilter?: number,
72 | flipY?: number,
73 | };
74 | /* eslint-emable */
75 |
76 | type Uniform = {
77 | type: string,
78 | value: number | Array,
79 | };
80 |
81 | type Props = {
82 | fs: string,
83 | vs?: string,
84 | textures?: Array,
85 | uniforms?: Array,
86 | clearColor?: Array,
87 | precision?: string,
88 | style?: string,
89 | contextAttributes?: Object,
90 | onDoneLoadingTextures?: Function,
91 | lerp?: number,
92 | devicePixelRatio?: number,
93 | };
94 |
95 | type Shaders = {
96 | fs: string,
97 | vs: string,
98 | };
99 |
100 | const lerpVal = (v0: number, v1: number, t: number) => v0 * (1 - t) + v1 * t;
101 | const insertStringAtIndex = (
102 | currentString: string,
103 | string: string,
104 | index: number
105 | ) =>
106 | index > 0
107 | ? currentString.substring(0, index) +
108 | string +
109 | currentString.substring(index, currentString.length)
110 | : string + currentString;
111 |
112 | export default class ShadertoyReact extends Component {
113 | constructor(props) {
114 | super(props);
115 |
116 | this.uniforms = {
117 | [UNIFORM_TIME]: {
118 | type: "float",
119 | isNeeded: false,
120 | value: 0,
121 | },
122 | [UNIFORM_TIMEDELTA]: {
123 | type: "float",
124 | isNeeded: false,
125 | value: 0,
126 | },
127 | [UNIFORM_DATE]: {
128 | type: "vec4",
129 | isNeeded: false,
130 | value: [0, 0, 0, 0],
131 | },
132 | [UNIFORM_MOUSE]: {
133 | type: "vec4",
134 | isNeeded: false,
135 | value: [0, 0, 0, 0],
136 | },
137 | [UNIFORM_RESOLUTION]: {
138 | type: "vec2",
139 | isNeeded: false,
140 | value: [0, 0],
141 | },
142 | [UNIFORM_FRAME]: {
143 | type: "int",
144 | isNeeded: false,
145 | value: 0,
146 | },
147 | [UNIFORM_DEVICEORIENTATION]: {
148 | type: "vec4",
149 | isNeeded: false,
150 | value: [0, 0, 0, 0],
151 | },
152 | };
153 | }
154 |
155 | static defaultProps = {
156 | textures: [],
157 | contextAttributes: {},
158 | devicePixelRatio: 1,
159 | vs: BASIC_VS,
160 | precision: "highp",
161 | };
162 |
163 | componentDidMount = () => {
164 | this.initWebGL();
165 |
166 | const { fs, vs, clearColor = [0, 0, 0, 1] } = this.props;
167 | const { gl } = this;
168 |
169 | if (gl) {
170 | gl.clearColor(...clearColor);
171 | gl.clearDepth(1.0);
172 | gl.enable(gl.DEPTH_TEST);
173 | gl.depthFunc(gl.LEQUAL);
174 | gl.viewport(0, 0, this.canvas.width, this.canvas.height);
175 |
176 | this.canvas.height = this.canvas.clientHeight;
177 | this.canvas.width = this.canvas.clientWidth;
178 |
179 | this.processCustomUniforms();
180 | this.processTextures();
181 | const shaders = this.preProcessShaders(fs || BASIC_FS, vs || BASIC_VS);
182 | this.initShaders(shaders);
183 | this.initBuffers();
184 | this.drawScene();
185 | this.addEventListeners();
186 | this.onResize();
187 | }
188 | };
189 |
190 | shouldComponentUpdate = () => false;
191 |
192 | componentWillUnmount() {
193 | const { gl } = this;
194 |
195 | if (gl) {
196 | gl.getExtension("WEBGL_lose_context").loseContext();
197 |
198 | gl.useProgram(null);
199 | gl.deleteProgram(this.shaderProgram);
200 |
201 | if (this.texturesArr.length > 0) {
202 | this.texturesArr.forEach((texture: Texture) => {
203 | gl.deleteTexture(texture._webglTexture);
204 | });
205 | }
206 |
207 | this.shaderProgram = null;
208 | }
209 |
210 | this.removeEventListeners();
211 | cancelAnimationFrame(this.animFrameId);
212 | }
213 |
214 | setupChannelRes = ({ width, height }: Texture, id: number) => {
215 | const { devicePixelRatio = 1 } = this.props;
216 | this.uniforms.iChannelResolution.value[id * 3] = width * devicePixelRatio;
217 | this.uniforms.iChannelResolution.value[id * 3 + 1] =
218 | height * devicePixelRatio;
219 | this.uniforms.iChannelResolution.value[id * 3 + 2] = 0;
220 | // console.log(this.uniforms);
221 | };
222 |
223 | initWebGL = () => {
224 | const { contextAttributes } = this.props;
225 | // $FlowFixMe
226 | this.gl =
227 | this.canvas.getContext("webgl", contextAttributes) ||
228 | this.canvas.getContext("experimental-webgl", contextAttributes);
229 | // $FlowFixMe
230 | this.gl.getExtension("OES_standard_derivatives");
231 | // $FlowFixMe
232 | this.gl.getExtension("EXT_shader_texture_lod");
233 | };
234 |
235 | initBuffers = () => {
236 | const { gl } = this;
237 |
238 | this.squareVerticesBuffer = gl.createBuffer();
239 |
240 | gl.bindBuffer(gl.ARRAY_BUFFER, this.squareVerticesBuffer);
241 |
242 | const vertices = [
243 | 1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, -1.0, -1.0, 0.0,
244 | ];
245 |
246 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
247 | };
248 |
249 | addEventListeners = () => {
250 | const options = {
251 | passive: true,
252 | };
253 |
254 | if (this.uniforms.iMouse.isNeeded) {
255 | this.canvas.addEventListener("mousemove", this.mouseMove, options);
256 | this.canvas.addEventListener("mouseout", this.mouseUp, options);
257 | this.canvas.addEventListener("mouseup", this.mouseUp, options);
258 | this.canvas.addEventListener("mousedown", this.mouseDown, options);
259 |
260 | this.canvas.addEventListener("touchmove", this.mouseMove, options);
261 | this.canvas.addEventListener("touchend", this.mouseUp, options);
262 | this.canvas.addEventListener("touchstart", this.mouseDown, options);
263 | }
264 |
265 | if (this.uniforms.iDeviceOrientation.isNeeded) {
266 | window.addEventListener(
267 | "deviceorientation",
268 | this.onDeviceOrientationChange,
269 | options
270 | );
271 | }
272 |
273 | window.addEventListener("resize", this.onResize, options);
274 | };
275 |
276 | removeEventListeners = () => {
277 | const options = {
278 | passive: true,
279 | };
280 |
281 | if (this.uniforms.iMouse.isNeeded) {
282 | this.canvas.removeEventListener("mousemove", this.mouseMove, options);
283 | this.canvas.removeEventListener("mouseout", this.mouseUp, options);
284 | this.canvas.removeEventListener("mouseup", this.mouseUp, options);
285 | this.canvas.removeEventListener("mousedown", this.mouseDown, options);
286 |
287 | this.canvas.removeEventListener("touchmove", this.mouseMove, options);
288 | this.canvas.removeEventListener("touchend", this.mouseUp, options);
289 | this.canvas.removeEventListener("touchstart", this.mouseDown, options);
290 | }
291 |
292 | if (this.uniforms.iDeviceOrientation.isNeeded) {
293 | window.removeEventListener(
294 | "deviceorientation",
295 | this.onDeviceOrientationChange,
296 | options
297 | );
298 | }
299 |
300 | window.removeEventListener("resize", this.onResize, options);
301 | };
302 |
303 | onDeviceOrientationChange = ({ alpha, beta, gamma }) => {
304 | this.uniforms.iDeviceOrientation.value = [
305 | alpha,
306 | beta,
307 | gamma,
308 | window.orientation || 0,
309 | ];
310 | };
311 |
312 | mouseDown = (e) => {
313 | const clientX = e.clientX || e.changedTouches[0].clientX;
314 | const clientY = e.clientY || e.changedTouches[0].clientY;
315 |
316 | let mouseX = clientX - this.canvasPosition.left - window.pageXOffset;
317 | let mouseY =
318 | this.canvasPosition.height -
319 | clientY -
320 | this.canvasPosition.top -
321 | window.pageYOffset;
322 |
323 | this.mousedown = true;
324 | this.uniforms.iMouse.value[2] = mouseX;
325 | this.uniforms.iMouse.value[3] = mouseY;
326 |
327 | this.lastMouseArr[0] = mouseX;
328 | this.lastMouseArr[1] = mouseY;
329 | };
330 |
331 | mouseMove = (e) => {
332 | this.canvasPosition = this.canvas.getBoundingClientRect();
333 | const { lerp = 1 } = this.props;
334 |
335 | const clientX = e.clientX || e.changedTouches[0].clientX;
336 | const clientY = e.clientY || e.changedTouches[0].clientY;
337 |
338 | let mouseX = clientX - this.canvasPosition.left;
339 | let mouseY = this.canvasPosition.height - clientY - this.canvasPosition.top;
340 |
341 | if (lerp !== 1) {
342 | this.lastMouseArr[0] = mouseX;
343 | this.lastMouseArr[1] = mouseY;
344 | } else {
345 | this.uniforms.iMouse.value[0] = mouseX;
346 | this.uniforms.iMouse.value[1] = mouseY;
347 | }
348 | };
349 |
350 | mouseUp = (e) => {
351 | this.uniforms.iMouse.value[2] = 0;
352 | this.uniforms.iMouse.value[3] = 0;
353 | };
354 |
355 | onResize = () => {
356 | const { gl } = this;
357 | const { devicePixelRatio = 1 } = this.props;
358 |
359 | this.canvasPosition = this.canvas.getBoundingClientRect();
360 |
361 | const realToCSSPixels = devicePixelRatio; // Force pixel ratio to be one to avoid expensive calculus on retina display
362 |
363 | const displayWidth = Math.floor(
364 | this.canvasPosition.width * realToCSSPixels
365 | );
366 |
367 | const displayHeight = Math.floor(
368 | this.canvasPosition.height * realToCSSPixels
369 | );
370 |
371 | gl.canvas.width = displayWidth;
372 | gl.canvas.height = displayHeight;
373 |
374 | if (this.uniforms.iResolution.isNeeded) {
375 | const rUniform = gl.getUniformLocation(
376 | this.shaderProgram,
377 | UNIFORM_RESOLUTION
378 | );
379 | // $FlowFixMe
380 | gl.uniform2fv(rUniform, [gl.canvas.width, gl.canvas.height]);
381 | }
382 | };
383 |
384 | drawScene = (timestamp: number) => {
385 | const { gl } = this;
386 | const { lerp = 1 } = this.props;
387 |
388 | gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
389 |
390 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // eslint-disable-line no-bitwise
391 |
392 | gl.bindBuffer(gl.ARRAY_BUFFER, this.squareVerticesBuffer);
393 | gl.vertexAttribPointer(
394 | this.vertexPositionAttribute,
395 | 3,
396 | gl.FLOAT,
397 | false,
398 | 0,
399 | 0
400 | );
401 |
402 | this.setUniforms(timestamp);
403 |
404 | gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
405 |
406 | if (this.uniforms.iMouse.isNeeded && lerp !== 1) {
407 | this.uniforms.iMouse.value[0] = lerpVal(
408 | this.uniforms.iMouse.value[0],
409 | this.lastMouseArr[0],
410 | lerp
411 | );
412 | this.uniforms.iMouse.value[1] = lerpVal(
413 | this.uniforms.iMouse.value[1],
414 | this.lastMouseArr[1],
415 | lerp
416 | );
417 | }
418 |
419 | this.animFrameId = requestAnimationFrame(this.drawScene);
420 | };
421 |
422 | createShader = (type: number, shaderCodeAsText: string) => {
423 | const { gl } = this;
424 |
425 | const shader = gl.createShader(type);
426 |
427 | gl.shaderSource(shader, shaderCodeAsText);
428 | gl.compileShader(shader);
429 |
430 | /* eslint-disable no-console */
431 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
432 | console.warn(SRLOG`Error compiling the shader:`, shaderCodeAsText);
433 | const compilationLog = gl.getShaderInfoLog(shader);
434 | gl.deleteShader(shader);
435 | console.error(SRLOG(`Shader compiler log: ${compilationLog}`));
436 | }
437 | /* eslint-enable no-console */
438 |
439 | return shader;
440 | };
441 |
442 | initShaders = ({ fs, vs }: Shaders) => {
443 | const { gl } = this;
444 | // console.log(fs, vs);
445 | const fragmentShader = this.createShader(gl.FRAGMENT_SHADER, fs);
446 | const vertexShader = this.createShader(gl.VERTEX_SHADER, vs);
447 |
448 | this.shaderProgram = gl.createProgram();
449 | gl.attachShader(this.shaderProgram, vertexShader);
450 | gl.attachShader(this.shaderProgram, fragmentShader);
451 | gl.linkProgram(this.shaderProgram);
452 |
453 | /* eslint-disable no-console */
454 | if (!gl.getProgramParameter(this.shaderProgram, gl.LINK_STATUS)) {
455 | // $FlowFixMe
456 | console.error(
457 | SRLOG(
458 | `Unable to initialize the shader program: ${gl.getProgramInfoLog(
459 | this.shaderProgram
460 | )}`
461 | )
462 | );
463 | return;
464 | }
465 | /* eslint-enable no-console */
466 |
467 | gl.useProgram(this.shaderProgram);
468 |
469 | this.vertexPositionAttribute = gl.getAttribLocation(
470 | this.shaderProgram,
471 | "aVertexPosition"
472 | );
473 | gl.enableVertexAttribArray(this.vertexPositionAttribute);
474 | };
475 |
476 | processCustomUniforms = () => {
477 | const { uniforms } = this.props;
478 | if (uniforms) {
479 | Object.keys(uniforms).forEach((name: string, id: number) => {
480 | const { value, type } = this.props.uniforms[name];
481 |
482 | const glslType = uniformTypeToGLSLType(type);
483 | if (!glslType) return; // If the type specified doesn't exist
484 |
485 | let tempObject = {};
486 | if (type.includes("Matrix")) {
487 | const arrayLength = type.length;
488 | const val = type.charAt(arrayLength - 3);
489 | const numberOfMatrices = Math.floor(value.length / (val * val));
490 |
491 | if (value.length > val * val) {
492 | tempObject.arraySize = `[${numberOfMatrices}]`;
493 | }
494 | } else if (type.includes("v") && value.length > type.charAt(0)) {
495 | tempObject.arraySize = `[${Math.floor(
496 | value.length / type.charAt(0)
497 | )}]`;
498 | }
499 |
500 | this.uniforms[name] = {
501 | type: glslType,
502 | isNeeded: false,
503 | value,
504 | ...tempObject,
505 | };
506 | });
507 | }
508 | };
509 |
510 | processTextures = () => {
511 | const { gl } = this;
512 | const { textures, onDoneLoadingTextures } = this.props;
513 |
514 | if (textures && textures.length > 0) {
515 | this.uniforms[`${UNIFORM_CHANNELRESOLUTION}`] = {
516 | type: "vec3",
517 | isNeeded: false,
518 | arraySize: `[${textures.length}]`,
519 | value: [],
520 | };
521 |
522 | const texturePromisesArr = textures.map(
523 | (texture: TexturePropsType, id: number) => {
524 | this.uniforms[`${UNIFORM_CHANNEL}${id}`] = {
525 | type: "sampler2D",
526 | isNeeded: false,
527 | }; // Dynamically add textures uniforms
528 |
529 | this.setupChannelRes(texture, id); // initialize array with 0s
530 | this.texturesArr[id] = new Texture(gl);
531 | return this.texturesArr[id]
532 | .load(texture, id)
533 | .then((texture) => this.setupChannelRes(texture, id));
534 | }
535 | );
536 |
537 | Promise.all(texturePromisesArr)
538 | .then(() => onDoneLoadingTextures && onDoneLoadingTextures())
539 | .catch((e) => {
540 | console.error(e);
541 | if (onDoneLoadingTextures) onDoneLoadingTextures();
542 | });
543 | } else {
544 | if (onDoneLoadingTextures) onDoneLoadingTextures();
545 | }
546 | };
547 |
548 | preProcessShaders = (fs: string, vs: string) => {
549 | const { precision, devicePixelRatio = 1 } = this.props;
550 |
551 | const dprString = `#define DPR ${devicePixelRatio.toFixed(1)}\n`;
552 | const isValidPrecision = PRECISIONS.includes(precision);
553 | const precisionString = `precision ${
554 | isValidPrecision ? precision : PRECISIONS[1]
555 | } float;\n`;
556 | if (!isValidPrecision)
557 | console.warn(
558 | SRLOG`wrong precision type ${precision}, please make sure to pass one of a valid precision lowp, mediump, highp, by default you shader precision will be set to highp.`
559 | );
560 |
561 | let fsString = precisionString
562 | .concat(dprString)
563 | .concat(fs)
564 | .replace(/texture\(/g, "texture2D(");
565 |
566 | const indexOfPrecisionString = fsString.lastIndexOf(precisionString);
567 |
568 | Object.keys(this.uniforms).forEach((uniform: string) => {
569 | if (fs.includes(uniform)) {
570 | fsString = insertStringAtIndex(
571 | fsString,
572 | `uniform ${this.uniforms[uniform].type} ${uniform}${
573 | this.uniforms[uniform].arraySize || ""
574 | }; \n`,
575 | indexOfPrecisionString + precisionString.length
576 | );
577 | this.uniforms[uniform].isNeeded = true;
578 | }
579 | });
580 |
581 | const isShadertoy = /mainImage/.test(fs);
582 | if (isShadertoy) fsString = fsString.concat(FS_MAIN_SHADER);
583 |
584 | // console.log(fsString);
585 | return {
586 | fs: fsString,
587 | vs,
588 | };
589 | };
590 |
591 | setUniforms = (timestamp: number) => {
592 | const { gl } = this;
593 |
594 | let delta = this.lastTime ? (timestamp - this.lastTime) / 1000 : 0;
595 | this.lastTime = timestamp;
596 |
597 | if (this.props.uniforms) {
598 | Object.keys(this.props.uniforms).forEach((name) => {
599 | const currentUniform = this.props.uniforms[name];
600 | if (this.uniforms[name].isNeeded) {
601 | const customUniformLocation = gl.getUniformLocation(
602 | this.shaderProgram,
603 | name
604 | );
605 | processUniform(
606 | gl,
607 | customUniformLocation,
608 | currentUniform.type,
609 | currentUniform.value
610 | );
611 | }
612 | });
613 | }
614 |
615 | if (this.uniforms.iMouse.isNeeded) {
616 | const mouseUniform = gl.getUniformLocation(
617 | this.shaderProgram,
618 | UNIFORM_MOUSE
619 | );
620 | // $FlowFixMe
621 | gl.uniform4fv(mouseUniform, this.uniforms.iMouse.value);
622 | }
623 |
624 | if (
625 | this.uniforms.iChannelResolution &&
626 | this.uniforms.iChannelResolution.isNeeded
627 | ) {
628 | const channelResUniform = gl.getUniformLocation(
629 | this.shaderProgram,
630 | UNIFORM_CHANNELRESOLUTION
631 | );
632 | gl.uniform3fv(channelResUniform, this.uniforms.iChannelResolution.value);
633 | }
634 |
635 | if (this.uniforms.iDeviceOrientation.isNeeded) {
636 | const deviceOrientationUniform = gl.getUniformLocation(
637 | this.shaderProgram,
638 | UNIFORM_DEVICEORIENTATION
639 | );
640 | gl.uniform4fv(
641 | deviceOrientationUniform,
642 | this.uniforms.iDeviceOrientation.value
643 | );
644 | }
645 |
646 | if (this.uniforms.iTime.isNeeded) {
647 | const timeUniform = gl.getUniformLocation(
648 | this.shaderProgram,
649 | UNIFORM_TIME
650 | );
651 | gl.uniform1f(timeUniform, (this.timer += delta));
652 | }
653 |
654 | if (this.uniforms.iTimeDelta.isNeeded) {
655 | const timeDeltaUniform = gl.getUniformLocation(
656 | this.shaderProgram,
657 | UNIFORM_TIMEDELTA
658 | );
659 | gl.uniform1f(timeDeltaUniform, delta);
660 | }
661 |
662 | if (this.uniforms.iDate.isNeeded) {
663 | const d = new Date();
664 | const month = d.getMonth() + 1;
665 | const day = d.getDate();
666 | const year = d.getFullYear();
667 | const time =
668 | d.getHours() * 60 * 60 +
669 | d.getMinutes() * 60 +
670 | d.getSeconds() +
671 | d.getMilliseconds() * 0.001;
672 |
673 | const dateUniform = gl.getUniformLocation(
674 | this.shaderProgram,
675 | UNIFORM_DATE
676 | );
677 |
678 | gl.uniform4fv(dateUniform, [year, month, day, time]);
679 | }
680 |
681 | if (this.uniforms.iFrame.isNeeded) {
682 | const timeDeltaUniform = gl.getUniformLocation(
683 | this.shaderProgram,
684 | UNIFORM_FRAME
685 | );
686 | gl.uniform1i(timeDeltaUniform, this.uniforms.iFrame.value++);
687 | }
688 |
689 | if (this.texturesArr.length > 0) {
690 | this.texturesArr.forEach((texture: Texture, id: number) => {
691 | const { isVideo, _webglTexture, source, flipY, isLoaded } = texture;
692 | if (!isLoaded) return;
693 | if (this.uniforms[`iChannel${id}`].isNeeded) {
694 | const iChannel = gl.getUniformLocation(
695 | this.shaderProgram,
696 | `iChannel${id}`
697 | );
698 | gl.activeTexture(gl[`TEXTURE${id}`]);
699 | gl.bindTexture(gl.TEXTURE_2D, _webglTexture);
700 | gl.uniform1i(iChannel, id);
701 | if (isVideo) texture.updateTexture(_webglTexture, source, flipY);
702 | }
703 | });
704 | }
705 | };
706 |
707 | registerCanvas = (r: HTMLCanvasElement) => {
708 | this.canvas = r;
709 | };
710 |
711 | gl: WebGLRenderingContext;
712 | squareVerticesBuffer: WebGLBuffer;
713 | shaderProgram: WebGLProgram;
714 | vertexPositionAttribute: number;
715 | animFrameId: AnimationFrameID;
716 | timeoutId: TimeoutID;
717 | canvas: HTMLCanvasElement;
718 | mousedown: boolean = false;
719 | canvasPosition: ClientRect;
720 | timer: number = 0;
721 | lastMouseArr: Array = [0, 0];
722 | texturesArr: Array = [];
723 | lastTime: number = 0;
724 |
725 | render = () => {
726 | const { style } = this.props;
727 |
728 | const currentStyle = {
729 | glCanvas: {
730 | height: "100%",
731 | width: "100%",
732 | ...style,
733 | },
734 | };
735 |
736 | return ;
737 | };
738 | }
739 |
--------------------------------------------------------------------------------
/src/prefixLogs.js:
--------------------------------------------------------------------------------
1 | // $flow
2 | export const SRLOG = (text: string) => `shadertoy-react: ${text}`;
--------------------------------------------------------------------------------
/src/uniformsType.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import {
3 | SRLOG
4 | } from './prefixLogs';
5 |
6 | const INT = 'int';
7 | const FLOAT = 'float';
8 |
9 | type Uniforms = {
10 | '1i': number,
11 | '2i': Array,
12 | '3i': Array,
13 | '4i': Array,
14 | '1f': number,
15 | '2f': Array,
16 | '3f': Array,
17 | '4f': Array,
18 | '1iv': Array,
19 | '2iv': Array,
20 | '3iv': Array,
21 | '4iv': Array,
22 | '1fv': Array,
23 | '2fv': Array,
24 | '3fv': Array,
25 | '4fv': Array,
26 | 'Matrix3fv': Array,
27 | 'Matrix4fv': Array,
28 | };
29 |
30 | export const processUniform = (gl: WebGLContext, location, type: Uniforms, value: number | string | Array ) => {
31 | switch (type) {
32 | case '1f':
33 | gl.uniform1f(location, value);
34 | break;
35 | case '2f':
36 | gl.uniform2f(location, value[0], value[1]);
37 | break;
38 | case '3f':
39 | gl.uniform3f(location, value[0], value[1], value[2]);
40 | break;
41 | case '4f':
42 | gl.uniform4f(location, value[0], value[1], value[2], value[3]);
43 | break;
44 | case '1i':
45 | gl.uniform1i(location, value);
46 | break;
47 | case '2i':
48 | gl.uniform2i(location, value[0], value[1]);
49 | break;
50 | case '3i':
51 | gl.uniform3i(location, value[0], value[1], value[2]);
52 | break;
53 | case '4i':
54 | gl.uniform3i(location, value[0], value[1], value[2], value[3]);
55 | break;
56 | case '1iv':
57 | gl.uniform1iv(location, value);
58 | break;
59 | case '2iv':
60 | gl.uniform2iv(location, value);
61 | break;
62 | case '3iv':
63 | gl.uniform3iv(location, value);
64 | break;
65 | case '4iv':
66 | gl.uniform4iv(location, value);
67 | break;
68 | case '1fv':
69 | gl.uniform1fv(location, value);
70 | break;
71 | case '2fv':
72 | gl.uniform2fv(location, value);
73 | break;
74 | case '3fv':
75 | gl.uniform3fv(location, value);
76 | break;
77 | case '4fv':
78 | gl.uniform4fv(location, value);
79 | break;
80 | case 'Matrix2fv':
81 | gl.uniformMatrix2fv(location, false, value);
82 | break;
83 | case 'Matrix3fv':
84 | gl.uniformMatrix3fv(location, false, value);
85 | break;
86 | case 'Matrix4fv':
87 | gl.uniformMatrix4fv(location, false, value);
88 | break;
89 | default:
90 | break;
91 | }
92 | }
93 |
94 | export const uniformTypeToGLSLType = (type: string) => {
95 | switch (type) {
96 | case '1f':
97 | return FLOAT;
98 | case '2f':
99 | return 'vec2';
100 | case '3f':
101 | return 'vec3';
102 | case '4f':
103 | return 'vec4';
104 | case '1i':
105 | return INT;
106 | case '2i':
107 | return 'ivec2';
108 | case '3i':
109 | return 'ivec3';
110 | case '4i':
111 | return 'ivec4';
112 | case '1iv':
113 | return INT;
114 | case '2iv':
115 | return 'ivec2';
116 | case '3iv':
117 | return 'ivec3';
118 | case '4iv':
119 | return 'ivec4';
120 | case '1fv':
121 | return 'float';
122 | case '2fv':
123 | return 'vec2';
124 | case '3fv':
125 | return 'vec3';
126 | case '4fv':
127 | return 'vec4';
128 | case 'Matrix2fv':
129 | return 'mat2';
130 | break;
131 | case 'Matrix3fv':
132 | return 'mat3';
133 | case 'Matrix4fv':
134 | return 'mat4';
135 | default:
136 | console.error(SRLOG `The uniform type "${type}" is not valid, please make sure your uniform type is valid`);
137 | }
138 | }
--------------------------------------------------------------------------------
/webpack.common.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const HtmlWebpackPlugin = require("html-webpack-plugin");
3 | const htmlWebpackPlugin = new HtmlWebpackPlugin({
4 | template: path.join(__dirname, "examples/src/index.html"),
5 | filename: "./index.html"
6 | });
7 |
8 | module.exports = {
9 | entry: path.join(__dirname, "examples/src/index.jsx"),
10 | output: {
11 | path: path.join(__dirname, "examples/dist"),
12 | filename: "bundle.js"
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.(js|jsx)$/,
18 | use: "babel-loader",
19 | exclude: /node_modules/
20 | },
21 | {
22 | test: /\.(png|jpg|gif|mp4|avi|m4v)$/,
23 | use: [
24 | {
25 | loader: 'file-loader',
26 | options: {}
27 | }
28 | ]
29 | },
30 | ]
31 | },
32 | plugins: [htmlWebpackPlugin],
33 | resolve: {
34 | extensions: [".js", ".jsx"]
35 | },
36 | devServer: {
37 | host: '0.0.0.0',
38 | port: 3001
39 | }
40 | };
--------------------------------------------------------------------------------
/webpack.lib.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
3 |
4 | module.exports = {
5 | entry: {
6 | "shadertoy-react" : path.join(__dirname, "src/index.jsx"),
7 | "shadertoy-react.min" : path.join(__dirname, "src/index.jsx"),
8 | },
9 | output: {
10 | path: path.join(__dirname, "lib/"),
11 | filename: "[name].js",
12 | libraryTarget: 'commonjs2',
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.(js|jsx)$/,
18 | use: "babel-loader",
19 | exclude: /node_modules/
20 | },
21 | ]
22 | },
23 | resolve: {
24 | extensions: [".js", ".jsx"]
25 | },
26 | externals: {
27 | 'react': 'react',
28 | 'react-dom': 'react-dom'
29 | },
30 | optimization: {
31 | minimizer: [new UglifyJsPlugin({
32 | include: /\.min\.js$/
33 | })]
34 | },
35 | devServer: {
36 | port: 3001
37 | }
38 | };
--------------------------------------------------------------------------------