Vai-gl RenderEngine is a WebGL2 TypeScript rendering engine component that leverages vertex attribute instancing. This engine is designed for efficiently rendering instances of vertex primitives with custom attributes while minimizing boilerplate and GL state management. See an example usage here: audreyadora/harmony
pnmp install vai-gl
-
Shader Support: The engine supports custom vertex and fragment shaders. The default shaders,
PianoRollShaderVertex
andPianoRollShaderFragment
, can be easily replaced with your own. -
Instanced Rendering: Efficiently renders multiple instances of vertex primitives with customizable attributes. The engine provides functions to bind instanced vertex primitives and attributes.
-
Dynamic Attribute Handling: The engine allows dynamic handling of attributes, supporting different attribute names, sizes, and lengths.
-
Program Compilation: The
compileProgram
function simplifies the process of compiling and linking vertex and fragment shaders into a WebGL program. -
Canvas Resize Handling: The engine includes a
resizeHandler
method to handle canvas resizing, considering device pixel ratio for optimal display.
import { RenderEngine } from './RenderEngine';
const canvas = document.getElementById('yourCanvas') as HTMLCanvasElement;
const renderEngine = new RenderEngine({ canvas });
const numInstances = 10;
const attributeArrayF32List = [...]; // List of Float32Arrays for each instance
renderEngine.render({ attributeArrayF32List, numInstances });
The RenderEngine class contains a contextLost flag that can be validated in the render loop to reinitialize the Render component when needed.
Bundled Shader Params:
//default floats per layer
const layer_float_length = 15
//number of layers to render
const layer_count = 1
const attributeArrayF32List = new Float32Array( layer_count * layer_float_length )
for (let layer_index=0; layer_index < layer_count; layer_index += 1) {
attributeArrayF32List.set([
xa,ya,xb,yb, //rectangle vertices scaled to viewport dimensions
r,g,b,a, // rgba color params scaled between 0 and 1
cxa,cya,cxb,cyb, // corner radius for vertices in px
width, height, // canvas dimensions in px
sigma // default is 0.25, adjust to blur rectangle edges - can be used to create a shadow effect underneath a second layer
], layer_index * layer_float_length
)
}
Replace default shaders by providing your own vertex and fragment shader sources during initialization:
const customVertexSource = `
// Your custom vertex shader code here
`;
const customFragmentSource = `
// Your custom fragment shader code here
`;
const customRenderEngine = new RenderEngine({
canvas,
vertexSource: customVertexSource,
fragmentSource: customFragmentSource,
});
This is a general example component class implementation which handles contextLost and canvas resize events. You can also import and extend this wrapper class from 'Vai'.
import { RenderEngine } from '../RenderEngine/RenderEngine';
export class MyComponent {
canvas = null as null | HTMLCanvasElement;
Render = null as null | RenderEngine;
width = 1000;
height = 1000;
id = '';
recalc_render_flag = true;
constructor(data: {canvas: HTMLCanvasElement, canvas_id: string}) {
this.canvas = data.canvas;
this.id = data.canvas_id;
this.Render = new RenderEngine({canvas: this.canvas});
this._resizeObserver.observe(this.canvas, {box: "device-pixel-content-box"})
this._onResize()
}
_resizeObserver = new ResizeObserver((entries:ResizeObserverEntry[]) => {
for (const entry of entries) {
if (entry?.target?.id === this.id) {
this.height = entry.devicePixelContentBoxSize[0].blockSize;
this.width = entry.devicePixelContentBoxSize[0].inlineSize;
this._onResize();
break;
};
};
});
_renderCtxInit() {
if (this.canvas) {
this.Render = new RenderEngine({canvas: this.canvas});
}
}
_onResize() {
if (!this.Render) { this._renderCtxInit() } else {
this.Render.resizeHandler(this.width, this.height)
}
this.recalc_render_flag = true;
}
render() {
if (!this?.Render) { this._renderCtxInit() } else if (this?.Render?.contextLost) { this._renderCtxInit() } else {
this.Render.render({
attributeArrayF32List: [],
numInstances: 0
})
};
}
}
Feel free to fork and modify for your own usecases/implementations.
This code is licensed under the MIT License - see the LICENSE file for details.