├── .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 |

screenshot

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 | --------------------------------------------------------------------------------