├── .gitignore ├── README.md ├── index.html ├── js ├── compiler.js └── editor.js └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.swp 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | `Ctrl + Enter` or `Command + Enter` to run your code. 2 | 3 | # Qiita 4 | https://qiita.com/JPNYKW/items/7096fe59d1edf3ef3aa3 5 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebGL Shader Editor 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /js/compiler.js: -------------------------------------------------------------------------------- 1 | const vertexShader = ` 2 | attribute vec3 position; 3 | void main(void) { 4 | gl_Position = vec4(position, 1.0); 5 | } 6 | `; 7 | 8 | const render = input => { 9 | errorLog.value = ''; 10 | 11 | const fragmentShader = input; 12 | const createProgram = (vs, fs) => { 13 | let stack = ctx.createProgram(); 14 | 15 | ctx.attachShader(stack, vs); 16 | ctx.attachShader(stack, fs); 17 | ctx.linkProgram(stack); 18 | 19 | if (ctx.getProgramParameter(stack, ctx.LINK_STATUS)) { 20 | console.log('Success '); 21 | ctx.useProgram(stack); 22 | return stack; 23 | } else { 24 | return null; 25 | } 26 | }; 27 | 28 | const createShader = (script, type) => { 29 | let shader = null; 30 | if (type == 'v') shader = ctx.createShader(ctx.VERTEX_SHADER); 31 | if (type == 'f') shader = ctx.createShader(ctx.FRAGMENT_SHADER); 32 | 33 | ctx.shaderSource(shader, script); 34 | ctx.compileShader(shader); 35 | 36 | if (ctx.getShaderParameter(shader, ctx.COMPILE_STATUS)) { 37 | console.log('Success '); 38 | return shader; 39 | } else { 40 | const error = ctx.getShaderInfoLog(shader); 41 | // console.log('Error', error); 42 | errorLog.value = error.toString(); 43 | } 44 | }; 45 | 46 | const createVbo = data => { 47 | let vbo = ctx.createBuffer(); 48 | ctx.bindBuffer(ctx.ARRAY_BUFFER, vbo); 49 | ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(data), ctx.STATIC_DRAW); 50 | ctx.bindBuffer(ctx.ARRAY_BUFFER, null); 51 | 52 | return vbo; 53 | }; 54 | 55 | const createIbo = data => { 56 | let ibo = ctx.createBuffer(); 57 | ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, ibo); 58 | ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, new Int16Array(data), ctx.STATIC_DRAW); 59 | ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, null); 60 | 61 | return ibo; 62 | }; 63 | 64 | const program = createProgram(createShader(vertexShader, 'v'), createShader(fragmentShader, 'f')); 65 | 66 | const uniform = []; 67 | uniform[0] = ctx.getUniformLocation(program, 'time'); 68 | uniform[1] = ctx.getUniformLocation(program, 'resolution'); 69 | 70 | const position = [ 71 | -1.0, 1.0, 0.0, 72 | 1.0, 1.0, 0.0, 73 | -1.0, -1.0, 0.0, 74 | 1.0, -1.0, 0.0 75 | ]; 76 | 77 | const index = [ 78 | 0, 2, 1, 79 | 1, 2, 3 80 | ]; 81 | 82 | const vbo_Index = createIbo(index); 83 | const vbo_Position = createVbo(position); 84 | const vbo_AttLocation = ctx.getAttribLocation(program, 'position'); 85 | 86 | ctx.bindBuffer(ctx.ARRAY_BUFFER, vbo_Position); 87 | ctx.enableVertexAttribArray(vbo_AttLocation); 88 | ctx.vertexAttribPointer(vbo_AttLocation, 3, ctx.FLOAT, false, 0, 0); 89 | ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, vbo_Index); 90 | ctx.clearColor(0.0, 0.0, 0.0, 1.0); 91 | 92 | const tick = () => { 93 | const sec = (new Date().getTime() - time) / 1000; 94 | ctx.clear(ctx.COLOR_BUFFER_BIT); 95 | 96 | ctx.uniform1f(uniform[0], sec); 97 | ctx.uniform2fv(uniform[1], [width, height]); 98 | ctx.drawElements(ctx.TRIANGLES, 6, ctx.UNSIGNED_SHORT, 0); 99 | ctx.flush(); 100 | 101 | time++; 102 | requestAnimationFrame(tick); 103 | } 104 | 105 | let time = new Date().getTime(); 106 | tick(); 107 | } 108 | 109 | const errorLog = document.getElementById('error'); 110 | 111 | const canvas = document.getElementById('canvas'); 112 | const height = innerHeight * 0.8; 113 | const width = innerWidth * 0.5; 114 | canvas.height = height; 115 | canvas.width = width; 116 | 117 | const ctx = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); 118 | -------------------------------------------------------------------------------- /js/editor.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | const keyBuffer = []; 3 | 4 | window.onload = () => { 5 | const editor = ace.edit('editor'); 6 | editor.$blockScrolling = Infinity; 7 | 8 | editor.setFontSize(13); 9 | editor.getSession().setMode("ace/mode/glsl"); 10 | editor.setTheme('ace/theme/tomorrow_night_eighties'); 11 | 12 | editor.setOptions({ 13 | enableBasicAutocompletion: true, 14 | enableSnippets: true, 15 | enableLiveAutocompletion: true 16 | }); 17 | 18 | // editor.setValue('// write your cool shader in here.'); 19 | editor.setValue('precision mediump float;\nuniform vec2 resolution;\n\nvoid main() {\n\t// write your cool shader in here ...\n\tvec2 position = (gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);\n\tvec3 color = vec3(position.x, position.y, 0.0);\n\tgl_FragColor = vec4(color, 1.0);\n}'); 20 | 21 | const editorElement = document.getElementById('editor'); 22 | 23 | editorElement.addEventListener('keydown', (event) => { 24 | keyBuffer[event.keyCode] = true; 25 | 26 | if (keyBuffer[13] && (keyBuffer[17] || keyBuffer[91])) { 27 | keyBuffer[13] = false; 28 | keyBuffer[17] = false; 29 | keyBuffer[91] = false; 30 | render(editor.getValue()); 31 | } 32 | }); 33 | 34 | editorElement.addEventListener('keyup', (event) => { 35 | keyBuffer[event.keyCode] = false; 36 | }); 37 | }; 38 | })(); 39 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #171717; 3 | 4 | height: 100%; 5 | width: 100%; 6 | } 7 | 8 | #editor { 9 | position: absolute; 10 | left: 0; 11 | top: 0; 12 | 13 | padding: 0; 14 | margin: 0; 15 | 16 | height: 80% !important; 17 | width: 50%; 18 | } 19 | 20 | #canvas { 21 | position: absolute; 22 | right: 0; 23 | top: 0; 24 | } 25 | 26 | #error { 27 | position: absolute; 28 | bottom: 0; 29 | left: 0; 30 | 31 | height: 20%; 32 | width: 100%; 33 | 34 | background: none; 35 | border: none; 36 | resize: none; 37 | 38 | font-size: 18px; 39 | color: #d8d8d8; 40 | } 41 | --------------------------------------------------------------------------------