Skip to content

Minimal footprint WebGL library

License

Notifications You must be signed in to change notification settings

learosema/glea

GLea - GL experience artistry

GLea is a low-level WebGL library with a minimal footprint. It provides helper functions for creating a WebGL program, compiling shaders and passing data from JavaScript to the shader language.

Introduction

There are several options to embed GLea into your project. You can load GLea directly via script tag:

<scriptsrc="https://unpkg.com/glea@latest/dist/glea.umd.min.js"></script>

Inside a JavaScript ES module:

importGLeafrom'https://cdn.skypack.dev/glea';

Or via NPM, you can install GLea via npm i glea and import it like this:

importGLeafrom'glea';

Initialization

By default, GLea looks for a canvas element in your HTML and uses that. If there is no canvas element existing, GLea creates one for you.

If your HTML document doesn't include any CSS (neither a style nor a link tag, a minimal stylesheet is provided that sizes the canvas to the browser's viewport size.

The GLea instance expects a shaders property, containing your fragment and vertex shader. Also, a buffers property, which contains the data that is passed as attributes to the vertex shader.

If no buffers are provided, GLea provides a default position attribute with a buffer containing 4 vec2 values for a triangle strip, defining a plane filling the screen.

Setting uniforms

GLea provides several helper functions to set uniforms to pass data from JavaScript to GLSL. These are:

// set uniform floatglea.uni('pi',Math.PI);// set uniform intglea.uniI('width',innerWidth);// set uniform float vector (supported types are vec2, vec3, vec4)glea.uniV('vector',[Math.sqrt(2),Math.sqrt(3)]);// set uniform int vectorglea.uniIV('resolution',[innerWidth,innerHeight]);// set uniform matrix// HEADS UP: it is the other way round as you would write it down on paper// prettier-ignoreglea.uniM('translateMatrix',[1,0,0,0,// column 10,1,0,0,// column 20,0,1,0,// column 3x,y,z,1,// column 4]);

Draw

GLea provides a wrapper to drawArrays from the underlying WebGLRenderingContext. It works exactly like the original drawArrays function, but if you don't provide any vertex count, it is determined automatically from the buffers.

const{ gl }=glea;glea.drawArrays(gl.TRIANGLE_STRIP);// The same as:constnumVertices=4;glea.gl.drawArrays(gl.TRIANGLE_STRIP,0,numVertices);

Multiple programs and switching

GLea supports multiple programs.

constprg1=newGLea({shaders: [GLea.vertexShader(vert),GLea.fragmentShader(frag)],}).create();constprg2=prg1.add({shaders: [GLea.vertexShader(vert2),GLea.fragmentShader(frag2)],buffers: {position: GLea.buffer(3,Ella.Geometry.sphere(0.25,32,16).toTriangles()),},});

The the main instance prg1 and its child prg2 use the same underlying WebGLRenderingContext. In the example prg1 renders a plane geometry (GLea provides a position attribute with a plane geometry by default), and prg2 provides a sphere geometry. The sphere geometry is provided by ella-math.

In the draw loop, the switching between programs is done via enableAttribs and disableAttribs:

// Shader 1 does the background animationprg1.gl.disable(gl.DEPTH_TEST);prg1.enableAttribs();prg1.uniV('resolution',[width,height]);prg1.uni('time',time*1e-3);prg1.drawArrays(gl.TRIANGLE_STRIP);prg1.disableAttribs();// Shader 2 renders a spheregl.enable(gl.DEPTH_TEST);prg2.enableAttribs();prg2.uniV('resolution',[width,height]);prg2.uni('time',time*1e-3);prg2.uniM('uPM',this.projectionMat.toArray());prg2.uniM('uVM',this.viewMat.toArray());prg2.drawArrays(gl.TRIANGLES);prg2.disableAttribs();

Full example

Loading textures

I'm using a loadImage helper function that wraps img.onload into a Promise:

functionloadImage(url){returnnewPromise((resolve,reject)=>{constimg=newImage();img.crossOrigin='Anonymous';img.src=url;img.onload=()=>{resolve(img);};img.onerror=()=>{reject(img);};});}asyncfunctionsetup(){constimg=awaitloadImage('https://placekitten.com/256/256/');consttextureIndex=0;glea.createTexture(textureIndex);glea.gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,img);glea.uniI('texture0',textureIndex);}setup();

In GLSL, you can access the texture like this:

uniformsampler2D texture0; void main(){vec2 coord =1.0-gl_FragCoord.xy /vec2(width, height); gl_FragColor=texture2D(texture1, coord)}

Example

importGLeafrom'https://cdn.skypack.dev/glea';constvert=`precision highp float;attribute vec2 position;void main(){ gl_Position = vec4(position, 0, 1.0);}`;constfrag=`precision highp float;uniform float time;uniform vec2 resolution;void main(){ float vmin = min(resolution.y, resolution.x); vec2 p = (gl_FragCoord.xy - .5 * resolution) / vmin; float r = .5 + .5 * sin(5. * log(length(p)) - time * 1.2); float g = .5 + .5 * sin(5. * log(length(p)) + sin(time + 2. * p.x));  float b = .5 + .5 * sin(.2 + 5. * log(length(p)) + sin(time * .4 + 4. * p.y)); gl_FragColor = vec4(r, g, b, 1.);}`;constglea=newGLea({shaders: [GLea.fragmentShader(frag),GLea.vertexShader(vert)],buffers: {// create a position attribute bound// to a buffer with 4 2D coordinates// this is what GLea provides by default if you omit buffers in the constructorposition: GLea.buffer(2,[1,1,-1,1,1,-1,-1,-1]),},}).create();functionloop(time){const{ gl, width, height }=glea;glea.clear();glea.uniV('resolution',[width,height]);glea.uni('time',time*1e-3);gl.drawArrays(gl.TRIANGLE_STRIP,0,4);requestAnimationFrame(loop);}functionsetup(){const{ gl }=glea;window.addEventListener('resize',()=>{glea.resize();});loop(0);}setup();

Exampes

More examples

Additional WebGL resources

About

Minimal footprint WebGL library

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

No packages published