├── .gitignore
├── .npmignore
├── README.md
├── docs
└── screenshot.png
├── examples
└── gl-toy
│ ├── gradient.frag
│ ├── index.js
│ └── package.json
├── package.json
└── src
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | lib/
3 | node_modules/
4 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # glsl-auto-ui
2 |
3 | **This is experimental module, if something doesn't work don't get angry and post an issue. PR's are more than welcome!**
4 |
5 | Automatic DAT.GUI generation from GLSL uniforms.
6 |
7 |

8 |
9 | Just name your UI uniforms starting with "ui": "uiColorModifier", etc.
10 | Currently only supports `float`, `vec2`, `vec3`, `vec4`.
11 |
12 | Uniforms default to `0.01` (to fix bug where float sliders in DAT.GUI display only 0 or 1).
13 | Uniforms are normalized to `0.0` - `1.0`, you can always multiple, subtract, add, etc. in GLSL code.
14 |
15 | ## Installation
16 |
17 | ```bash
18 | $ npm install glsl-auto-ui
19 | ```
20 |
21 | ## Usage
22 |
23 | ```javascript
24 | const { generateUI, updateGLSLToyShaderUniforms } = require('glsl-auto-ui');
25 |
26 | // get shader from somewhere
27 | const glslify = require('glslify');
28 | const shader = glslify('./shader.frag');
29 |
30 | const { GUI } = require('dat-gui');
31 | const gui = new GUI();
32 |
33 | // generate UI using DAT GUI instance, and shader string
34 | // you don't need glslify, you just have to pass the shader string here
35 | let params = generateUI(gui, shader);
36 |
37 | // currently only "supports" gl-toy
38 | const toy = require('gl-toy');
39 |
40 | toy(shader, (gl, shader) => {
41 | updateGLSLToyShaderUniforms(shader.uniforms, params);
42 | });
43 | ```
44 |
45 | This code will not really work, because `DAT.GUI` needs to be created after gl-toy, look into `examples/gl-toy` for more info.
46 |
47 |
--------------------------------------------------------------------------------
/docs/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/szymonkaliski/glsl-auto-ui/2083728e4ad84bf265c4f8691b5c3b64956edb6d/docs/screenshot.png
--------------------------------------------------------------------------------
/examples/gl-toy/gradient.frag:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | uniform vec2 iResolution;
4 |
5 | // just start uniform name with 'ui' and it gets pulled into DAT.GUI automagically!
6 | uniform vec2 uiGradientPos;
7 | uniform vec3 uiBaseColor;
8 | uniform vec3 uiSecondaryColor;
9 |
10 | void main() {
11 | vec2 pos = gl_FragCoord.xy / iResolution.xy;
12 | float dist = distance(pos, uiGradientPos);
13 |
14 | gl_FragColor = vec4(mix(uiBaseColor * (1.0 - dist), uiSecondaryColor, dist), 1.0);
15 | }
16 |
--------------------------------------------------------------------------------
/examples/gl-toy/index.js:
--------------------------------------------------------------------------------
1 | // setup glsl-toy
2 | const glslify = require('glslify');
3 | const start = Date.now();
4 | const toy = require('gl-toy');
5 | const { GUI } = require('dat-gui');
6 |
7 | const shader = glslify('./gradient.frag');
8 |
9 | // glsl-auto-ui related
10 | const {
11 | generateUI,
12 | updateGLSLToyShaderUniforms
13 | } = require('../../src');
14 |
15 | let params; // here we will hold params generated from ui* shader uniforms
16 |
17 | toy(shader, (gl, shader) => {
18 | shader.uniforms.iResolution = [ gl.drawingBufferWidth, gl.drawingBufferHeight ];
19 | shader.uniforms.iGlobalTime = (Date.now() - start) / 1000;
20 |
21 | // update
22 | updateGLSLToyShaderUniforms(shader.uniforms, params);
23 | });
24 |
25 | // add UI - after glsl-toy so it displays on top
26 | const gui = new GUI();
27 |
28 | // generate params and setup UI
29 | params = generateUI(gui, shader);
30 |
--------------------------------------------------------------------------------
/examples/gl-toy/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "glsl-auto-ui-gl-toy-example",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "budo index.js --live --port 3000 --host=localhost -- -t [ babelify --presets [ es2015 ] ] -t glslify"
8 | },
9 | "dependencies": {
10 | "babel": "^6.5.2",
11 | "babel-preset-es2015": "^6.6.0",
12 | "babelify": "^7.2.0",
13 | "budo": "^8.2.1",
14 | "dat-gui": "^0.5.0",
15 | "gl-toy": "^2.0.3",
16 | "glslify": "^5.0.2"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "glsl-auto-ui",
3 | "version": "0.0.3",
4 | "description": "Automatic DAT.GUI generation from GLSL uniforms",
5 | "author": "Szymon Kaliski ",
6 | "license": "MIT",
7 | "main": "./lib/index.js",
8 | "scripts": {
9 | "build": "babel src --presets es2015 --out-dir lib",
10 | "prepublish": "npm run build"
11 | },
12 | "dependencies": {
13 | "glsl-parser": "^2.0.0",
14 | "glsl-tokenizer": "^2.1.1",
15 | "lodash.flatten": "^4.2.0",
16 | "lodash.get": "^4.2.1"
17 | },
18 | "devDependencies": {
19 | "babel-cli": "^6.7.5",
20 | "babel-preset-es2015": "^6.6.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const flatten = require('lodash.flatten');
2 | const get = require('lodash.get');
3 | const parseTokens = require('glsl-parser/direct');
4 | const tokenString = require('glsl-tokenizer/string');
5 |
6 | // finds uniforms with name starting with "ui",
7 | // currently matches only float, vec2, vec3, vec4
8 | const findUniforms = (ast) => {
9 | return Object.keys(ast.scope)
10 | .filter(key => key.startsWith('ui'))
11 | .map(key => ({ key, scope: ast.scope[key] }))
12 | .map(({ key, scope }) => {
13 | const type = get(scope, [ 'parent', 'parent', 'children' ])
14 | .filter(({ type }) => type === 'keyword')
15 | .map(({ token: { data } }) => data)
16 | .filter(data => [ 'float', 'vec2', 'vec3', 'vec4' ].indexOf(data) >= 0);
17 |
18 | return {
19 | key,
20 | type: get(type, [ 0 ], undefined)
21 | };
22 | });
23 | };
24 |
25 | // generates UI from shader string
26 | const generateUI = (gui, shader, { remember } = { remember: true }) => {
27 | const uniforms = findUniforms(parseTokens(tokenString(shader)));
28 |
29 | const paramsStruct = uniforms.map(({ key, type }) => {
30 | if (type === 'float') {
31 | return [ { [key]: 0.01 } ];
32 | }
33 | else if (type.startsWith('vec')) {
34 | return [
35 | { [`${key}.x`]: 0.01 },
36 | { [`${key}.y`]: 0.01 },
37 | { [`${key}.z`]: 0.01 },
38 | { [`${key}.w`]: 0.01 }
39 | ].slice(0, parseInt(type.replace('vec', '')));
40 | }
41 | });
42 |
43 | // output params
44 | let outParams = flatten(paramsStruct).reduce((memo, value) => Object.assign(memo, value), {});
45 |
46 | // remember state - especially helpful with localStorage set to true
47 | // NOT: this HAS to be called before any gui.add!
48 | if (remember) { gui.remember(outParams); }
49 |
50 | // add folders to DAT.GUI
51 | paramsStruct.forEach((params, i) => {
52 | const folder = gui.addFolder(uniforms[i].key);
53 |
54 | params.forEach(params => {
55 | folder.add(outParams, Object.keys(params)[0], 0.0, 1.0);
56 | });
57 |
58 | folder.open();
59 | });
60 |
61 | // return params
62 | return outParams;
63 | };
64 |
65 | // updates shader uniforms with params for glsl-toy
66 | const updateGLSLToyShaderUniforms = (uniforms, params) => {
67 | const realParams = Array.from(Object.keys(params)
68 | .reduce((memo, key) => {
69 | if (key.indexOf('.') > 0) {
70 | const realKey = key.split('.')[0];
71 | memo.add(realKey);
72 | }
73 | else {
74 | memo.add(key);
75 | }
76 |
77 | return memo;
78 | }, new Set()));
79 |
80 | realParams.forEach(param => {
81 | if (params[param]) {
82 | uniforms[param] = params[param];
83 | }
84 | else {
85 | const value = [ 'x', 'y', 'z', 'w' ]
86 | .map(key => params[`${param}.${key}`])
87 | .filter(value => value !== undefined);
88 |
89 | uniforms[param] = value;
90 | }
91 | });
92 | };
93 |
94 | module.exports = {
95 | generateUI,
96 | updateGLSLToyShaderUniforms
97 | };
98 |
--------------------------------------------------------------------------------