UNPKG

gl-compute

Version:
336 lines (245 loc) 13 kB
<!DOCTYPE html> <html lang="en"> <head> <title>gl-compute demo</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- GitHub ribbon --> <style>#forkongithub a{background:#000;color:#fff;text-decoration:none;font-family:arial,sans-serif;text-align:center;font-weight:bold;padding:5px 40px;font-size:1rem;line-height:2rem;position:relative;transition:0.5s;}#forkongithub a:hover{background:#c11;color:#fff;}#forkongithub a::before,#forkongithub a::after{content:"";width:100%;display:block;position:absolute;top:1px;left:0;height:1px;background:#fff;}#forkongithub a::after{bottom:1px;top:auto;}@media screen and (min-width:800px){#forkongithub{position:fixed;display:block;top:0;right:0;width:200px;overflow:hidden;height:200px;z-index:9999;}#forkongithub a{width:200px;position:absolute;top:25px;right:-60px;transform:rotate(45deg);-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);-moz-transform:rotate(45deg);-o-transform:rotate(45deg);box-shadow:4px 4px 10px rgba(0,0,0,0.8);}}</style><span id="forkongithub"><a target="_blank" href="https://github.com/gnonio/gl-compute">Fork me on GitHub</a></span> <!-- jQuery --> <script src="ext/jquery-2.1.3.min.js"></script> <!-- marked --> <script src="ext/marked.min.js"></script> <!-- Bootstrap --> <!-- <link href="ext/bootstrap.min.css" rel="stylesheet"> --> <link href="ext/bootstrap-slate.min.css" rel="stylesheet"> <script src="ext/bootstrap.min.js"></script> <link href="ext/custom.css" rel="stylesheet"> <!-- Demo --> <script type="text/javascript" src="./js/bundle.js"></script> </head> <body> <div class="container top-margin" class="smalltext"> <div class="row"> <h3 class="col-md-12 text-center"><strong>gl-compute demo</strong></h3> </div> <!-- Canvas --> <div class="row"> <div class="col-md-3 smalltext bg-primary"> <h4><strong>Canvas</strong></h4> <p><br/>Exhibiting the results of StageRender. This is totally optional. It helps debugging your results, keep in mind that only normalized values (ie [0,1]) will be shown properly.<br/><br/> In your projects using gl-compute, once your kernels are final you should disable the render stage given its processing cost.<br/><br/> Current example is iterative, processing the stages will yield incremental results.<br/><br/> Play around with the shaders bellow, update them and reprocess the stages.<br/><br/> This canvas is showing upscaled pixels, the colored squares are actually one pixel wide. Depending on your inputs, or more exacly, the size of the stage's output you should readjust the scale factor when using this visualization helper.<br/><br/> Check console to see the raw results being processed, along with some benchmark statistics. </p> </div> <div id="container" class="col-md-9"></div> <!-- Canvas will be place here via ID tag --> <div class="col-md-2 smalltext"></div> </div> <div class="row"> <input id="animate" class="btn btn-primary btn-sm pull-right" type="button" value="animated = true"> <input id="processStages" class="btn btn-primary btn-sm pull-right" type="button" value="processStages()"> </div> <!-- Stage A --> <div class="row"> <div class="col-md-3" ><strong>StageA</strong></div> <div class="col-md-3" >Vertex Shader:</div> <div class="col-md-6" >Fragment Shader:</div> </div> <div class="row"> <span class="col-md-3 smalltext"> <p>Here you can configure the kernels for your computations. The vertex shader is a simple pass through sending a screen sized quad to the fragment shader. You can still modify it.<br/><br/> Results will be passed over to the next stage ("StageB"). Full cycle's last stage results is also available in this first stage via a uniform named after the last stage name ("StageB") </p> </span> <textarea id="vertStageA" class="col-md-3 mono xsmalltext" rows="12"> attribute vec2 position; varying vec2 uv; void main() { gl_Position = vec4( position, 0.0, 1.0 ); uv = 0.5 * ( position + 1.0 ); }</textarea> <textarea id="fragStageA" class="col-md-6 mono xsmalltext" rows="12"> precision highp float; // Base varying vec2 uv; void main() { vec4 result1 = texture2D( dataA, vec2(uv.x, uv.y) ); vec4 result2 = texture2D( StageB, vec2(uv.x, uv.y) ); vec4 last; if ( mod( float( computeLoop ), 3.0 ) == 0.0 ) { last = result1; } else { last = result2; } gl_FragColor = last; }</textarea> </div> <div class="row"> <input id="updateShaderA" class="btn btn-primary btn-sm pull-right" type="button" value="Update StageA Shader"> </div> <!-- Stage B --> <div class="row"> <div class="col-md-3" ><strong>StageB</strong></div> <div class="col-md-3" >Vertex Shader:</div> <div class="col-md-6" >Fragment Shader:</div> </div> <div class="row"> <span class="col-md-3 smalltext"> <p>This intermediate stage may access results from the previous stage following its name. The uniforms are made available automatically. Currently, declaring a gl-compute generated uniform in the shaders will result in error.<br/><br/> Use your browser's shader editor to check uniforms made available. </p> </span> <textarea id="vertStageB" class="col-md-3 mono xsmalltext" rows="12"> attribute vec2 position; varying vec2 uv; void main() { gl_Position = vec4( position, 0.0, 1.0 ); uv = 0.5 * ( position + 1.0 ); }</textarea> <textarea id="fragStageB" class="col-md-6 mono xsmalltext" rows="12"> precision highp float; // Base varying vec2 uv; void main() { vec4 test = texture2D( StageA, vec2(uv.x, uv.y) ) * 0.5; test.a = 1.0; gl_FragColor = test; }</textarea> </div> <div class="row"> <input id="updateShaderB" class="btn btn-primary btn-sm pull-right" type="button" value="Update StageB Shader"> </div> <!-- Stage Render --> <div class="row"> <div class="col-md-3" ><strong>StageRender</strong></div> <div class="col-md-3" >Vertex Shader:</div> <div class="col-md-6" >Fragment Shader:</div> </div> <div class="row"> <span class="col-md-3 smalltext"> <p>This stage is not part of the computations. It helps visualizing the results of any stage of the computation, and do some further processing if convenient (ie. flip the vertical axis).<br/><br/> Internally data fed to WebGL is disposed following OpenGL's convention (left > right / bottom > top), we usually expect top > bottom. Always keep this in mind when interpreting the data order you are feeding gl-compute. </p> </span> <textarea id="vertRender" class="col-md-3 mono xsmalltext" rows="12"> attribute vec2 position; varying vec2 uv; void main() { gl_Position = vec4( position, 0.0, 1.0 ); uv = 0.5 * ( position + 1.0 ); }</textarea> <textarea id="fragRender" class="col-md-6 mono xsmalltext" rows="12"> precision highp float; // Base varying vec2 uv; void main() { gl_FragColor = texture2D( StageA, vec2(uv.x, 1.0 - uv.y) ); // Flip Vertically }</textarea> </div> <div class="row"> <input id="updateShaderRender" class="btn btn-primary btn-sm pull-right" type="button" value="Update Render Shader"> </div> <!-- Readme --> <div class="row"> <div id="marked-source" class="col-md-8 col-md-offset-2 bg-primary hide"> # gl-compute ## WebGL Compute Framework A compute framework on top of WebGL. Based on some [#stack.gl](http://www.stack.gl) modules. This framework will allow to create custom kernels for each stage of a algorithm intended to run in parallel on the GPU. Stages are defined given the need in a phase of a algorithm of the results of a previous phase. Additionally the full algorithm may then be looped over, providing the last phase results to the first according to your needs. The objective is to have a featured, easy to understand and use framework, following parallel processing conventions is not necessarily the aim of this project (though they may appear here and there). You may also have interest in existing projects that served as inspiration: [webclgl](https://github.com/stormcolor/webclgl) / [webgl-matrix-demo](https://github.com/watmough/webgl-matrix-demo) / [three.js - gpgpu flocking](http://jabtunes.com/labs/3d/gpuflocking/webgl_gpgpu_flocking6.html). ### [DEMO](http://www.euclidiana.pt/gnonio/gl-compute) **** ### Install - `npm install gl-compute` - `var glCompute = require('gl-compute')` *as a module for your projects* - `browserify node_modules/gl-compute/demo/js/index.js -o node_modules/gl-compute/demo/js/bundle.js` *as a Demo to try out* **** ### Features - Plug-in to any project: just tell gl-compute which object contains your data and it's location (objects' attribute) - Multiple Data sources: provide a configurable amount of inputs to each phase of your algorithm - Input data update: ability to set input data as dirty - Automatic uniform glsl code generation: link inputs via gl-compute config and code the kernels not bothering with uniform declaration (reduces code management) - Configurable outputs: similarly to inputs plug-in interface - Update inplace: output data may be directly written to source input object - Output callback: configure your custom function once output data has been written **** ### Todo - Provide some canned shaders for common pre and post processing tasks (ie. input data 1 component > output data 4 components > output reduction to 1 component) - If maturing enough, link up to #stack.gl compute feature requests **** ### Usage #### Stage Setup Options Stages must be fed to gl-compute in the order of computations, a render stage (optional) provided lastly. The computation cycle will pass the results of one stage to the next according to given name (see bellow). The last computation stage will pass it's results back to the first stage as needed (looped algorithms). (Logically, first stage data will be empty in the first pass, no data available from last stage). ``` var data = { dataA: inputA, dataAOut: emptyFloats, dataB: inputB, dataBOut: emptyFloats, dataC: inputC, dataCOut: emptyFloats, dataD: inputD, dataDOut: emptyFloats, dataROut: emptyUInts } Example callback function var callback = function() { console.log( this.output.object[this.output.location] ) } var options = { type : Stage Type - 'COMPUTE' (for the actual computations) or 'RENDER' (optional for visualization purposes only) draw : Draw Flag - to activate/deactivate this stage on demand shape : Stage Shape - These are the dimensions of this stages' output shaderSources : Shader Sources - to use in this stage (create them and don't worry about uniform declaration check uniforms option bellow) uniforms : Stage Inputs - Data input to be fed to gl-compute { uniformName : { type: 'sampler2D', object: object, location: string, shape: array, flip: boolean } These will be made into texture uniforms and fragment shader source will be generated and added correspondingly Each named property here becomes the uniform name to be made available in the shader Both the sampler2D uniform and a ivec2 containing the shape of the input is generated GLSL naming conventions apply The code generation includes a header and comments for your reference (ie. Stage Name and generation/compilation loop) Use your browser's shader editor to check the complete GLSL code being compiled output : Stage Output - definitions of the target object where data will be saved { write: boolean, object: object, location: string, onUpdated: function } Write Output Flag - This is where the most performance impact occurs, use sparingly when intermediate results are required Object Reference Attribute name where the data buffer lies callback to a function when new data has been writen } Inititalise Stages - Provide options as an object, stages will be named here compute.preInit( { nameOfStage1: stageOptions1, nameOfStage2: stageOptions2, renderStage: stageRender } ) ``` </div> <script> document.getElementById('marked-source').innerHTML = marked( document.getElementById('marked-source').innerHTML ) document.getElementById('marked-source').classList.remove("hide"); </script> </div> </div> <!-- container END --> <!-- GA --> <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-66399808-1', 'auto'); ga('send', 'pageview'); </script> </body> </html>