# REGL API ## Table of contents - [Initialization](#initialization) - [Quick start](#quick-start) - [As a fullscreen canvas](#as-a-fullscreen-canvas) - [From a selector string](#from-a-selector-string) - [From a container div](#from-a-container-div) - [From a canvas](#from-a-canvas) - [From a WebGL context](#from-a-webgl-context) - [From a headless context](#from-a-headless-context) - [All initialization options](#all-initialization-options) - [Error messages and debug mode](#error-messages-and-debug-mode) - [Commands](#commands) - [Executing commands](#executing-commands) - [One-shot rendering](#one-shot-rendering) - [Batch rendering](#batch-rendering) - [Scoped commands](#scoped-commands) - [Inputs](#inputs) - [Example](#example) - [Context](#context) - [Props](#props) - [this](#this) - [Parameters](#parameters) - [Shaders](#shaders) - [Uniforms](#uniforms) - [Attributes](#attributes) - [Drawing](#drawing) - [Render target](#render-target) - [Profiling](#profiling) - [Depth buffer](#depth-buffer) - [Blending](#blending) - [Stencil](#stencil) - [Polygon offset](#polygon-offset) - [Culling](#culling) - [Front face](#front-face) - [Dithering](#dithering) - [Line width](#line-width) - [Color mask](#color-mask) - [Sample coverage](#sample-coverage) - [Scissor](#scissor) - [Viewport](#viewport) - [Resources](#resources) - [Buffers](#buffers) - [Buffer constructor](#buffer-constructor) - [Buffer update](#buffer-update) - [Buffer subdata](#buffer-subdata) - [Buffer destructor](#buffer-destructor) - [Profiling info](#profiling-info) - [Vertex array objects](#vertex-array-objects) - [Vertex array object constructor](#vertex-array-object-constructor) - [Vertex array object update](#vertex-array-object-update) - [Vertex array object destructor](#vertex-array-object-destructor) - [Elements](#elements) - [Element constructor](#element-constructor) - [Element update](#element-update) - [Element subdata](#element-subdata) - [Element destructor](#element-destructor) - [Textures](#textures) - [Texture constructor](#texture-constructor) - [Texture update](#texture-update) - [Texture subimage](#texture-subimage) - [Texture resize](#texture-resize) - [Texture properties](#texture-properties) - [Texture destructor](#texture-destructor) - [Texture profiling](#texture-profiling) - [Cube maps](#cube-maps) - [Cube map constructor](#cube-map-constructor) - [Cube map update](#cube-map-update) - [Cube map subimage](#cube-map-subimage) - [Cube map resize](#cube-map-resize) - [Cube map properties](#cube-map-properties) - [Cube map profiling](#cube-map-profiling) - [Cube map destructor](#cube-map-destructor) - [Renderbuffers](#renderbuffers) - [Renderbuffer constructor](#renderbuffer-constructor) - [Renderbuffer update](#renderbuffer-update) - [Renderbuffer resize](#renderbuffer-resize) - [Renderbuffer properties](#renderbuffer-properties) - [Renderbuffers destructor](#renderbuffers-destructor) - [Renderbuffer profiling](#renderbuffer-profiling) - [Framebuffers](#framebuffers) - [Framebuffer constructor](#framebuffer-constructor) - [Framebuffer update](#framebuffer-update) - [Framebuffer binding](#framebuffer-binding) - [Framebuffer resize](#framebuffer-resize) - [Framebuffer destructor](#framebuffer-destructor) - [Cubic frame buffers](#cubic-frame-buffers) - [Cube framebuffer constructor](#cube-framebuffer-constructor) - [Cube framebuffer update](#cube-framebuffer-update) - [Cube framebuffer resize](#cube-framebuffer-resize) - [Cube framebuffer destructor](#cube-framebuffer-destructor) - [Other tasks](#other-tasks) - [Clear the draw buffer](#clear-the-draw-buffer) - [Reading pixels](#reading-pixels) - [Per-frame callbacks](#per-frame-callbacks) - [Extensions](#extensions) - [Device capabilities and limits](#device-capabilities-and-limits) - [Performance metrics](#performance-metrics) - [Clocks and timers](#clocks-and-timers) - [Clean up](#clean-up) - [Context loss](#context-loss) - [Unsafe escape hatch](#unsafe-escape-hatch) - [Tips](#tips) - [Reuse commands](#reuse-commands) - [Reuse resources (buffers, elements, textures, etc.)](#reuse-resources-buffers-elements-textures-etc) - [Preallocate memory](#preallocate-memory) - [Removing assertions](#removing-assertions) - [Profiling tips](#profiling-tips) - [Context loss mitigation](#context-loss-mitigation) - [Use batch mode](#use-batch-mode) ## Initialization ### Quick start #### As a fullscreen canvas By default calling `module.exports` on the `regl` package creates a full screen canvas element and WebGLRenderingContext. ```javascript var regl = require('regl')() ``` This canvas will dynamically resize whenever the window changes shape. For most quick demos this is an easy way to get started using `regl`. #### From a selector string If the first argument is a CSS selector string, `regl` will attempt to find and use the corresponding DOM element. This may be: 1) an existing HTMLCanvasElement 2) an element that contains a canvas 3) an element in which you'd like `regl` to create a canvas ```javascript var regl = require('regl')('#my-canvas'); ``` #### From a container div Alternatively passing a container element as the first argument appends the generated canvas to its children. ```javascript var regl = require('regl')(element) // or: var regl = require('regl')({ container: element }) ``` #### From a canvas If the first argument is an HTMLCanvasElement, then `regl` will use this canvas to create a new WebGLRenderingContext that it renders into. ```javascript var regl = require('regl')(canvas) // or: var regl = require('regl')({ canvas: canvas }) ``` #### From a WebGL context Finally, if the first argument is a WebGLRenderingContext, then `regl` will just use this context without touching the DOM at all. ```javascript var regl = require('regl')(gl) // or: var regl = require('regl')({ gl: gl }) ``` #### From a headless context The above form can also be used to run `regl` headlessly by combining it with the [`headless-gl`](https://github.com/stackgl/headless-gl) package. This works in node.js, electron and the browser. ```javascript //Creates a headless 256x256 regl instance var regl = require('regl')(require('gl')(256, 256)) ``` ### All initialization options | Options | Meaning | | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `gl` | A reference to a WebGL rendering context. (Default created from canvas) | | `canvas` | A reference to an HTML canvas element. (Default created and appended to container) | | `container` | A container element into which regl inserts a canvas. (Default `document.body`) | | `attributes` | The [context creation attributes](https://www.khronos.org/registry/webgl/specs/1.0/#WEBGLCONTEXTATTRIBUTES) passed to the WebGL context constructor. See below for defaults. | | `pixelRatio` | A multiplier which is used to scale the canvas size relative to the container. (Default `window.devicePixelRatio`) | | `extensions` | A list of extensions that must be supported by WebGL context. Default `[]` | | `optionalExtensions` | A list of extensions which are loaded opportunistically. Default `[]` | | `profile` | If set, turns on profiling for all commands by default. (Default `false`) | | `onDone` | An optional callback which accepts a pair of arguments, `(err, regl)` that is called after the application loads. If not specified, context creation errors throw. | **Notes** - `canvas` or `container` may be a CSS selector string or a DOM element - `extensions` and `optionalExtensions` can be either arrays or comma separated strings representing all extensions. For more information see the [WebGL extension registry](https://www.khronos.org/registry/webgl/extensions/) - `onDone` is called * * * ### Error messages and debug mode By default if you compile `regl` with browserify then all error messages and checks are removed. This is done in order to reduce the size of the final bundle and to improve run time performance. **If you want error messages and are using browserify make sure that you compile using `--debug`**. Not only will this insert debug messages but it will also give you source maps which make finding errors easier. Alternatively, consider using [`budo`](https://github.com/mattdesl/budo) for your live development server. `budo` automatically compiles your code in debug mode and also provides facilities for live reloading. ## Commands _Draw commands_ are the fundamental abstraction in `regl`. A draw command wraps up all of the WebGL state associated with a draw call (either `drawArrays` or `drawElements`) and packages it into a single reusable function. For example, here is a command that draws a triangle, ```javascript const drawTriangle = regl({ frag: ` void main() { gl_FragColor = vec4(1, 0, 0, 1); }`, vert: ` attribute vec2 position; void main() { gl_Position = vec4(position, 0, 1); }`, attributes: { position: [[0, -1], [-1, 0], [1, 1]] }, count: 3 }) ``` To run a command you call it just like you would any function, ```javascript drawTriangle() ``` * * * ### Executing commands There are 3 ways to run a command, #### One-shot rendering In one shot rendering the command runs once immediately, ```javascript // Runs command immediately with no arguments command() // Runs a command using the specified arguments command(props) ``` #### Batch rendering A command can also run multiple times by passing a non-negative integer or an array as the first argument. The `batchId` is initially `0` and incremented for each iteration, ```javascript // Runs the command `count`-times command(count) // Runs the command once for each props command([props0, props1, props2, ..., propsn]) ``` In batch mode the command can be a little smarter regarding binding shaders/performing some checks. The command can then figure out which props are constant, which are dynamic, and then only apply the changes on each dynamic prop. This offers a small performance boost. #### Scoped commands Commands can be nested using scoping. If the argument to the command is a function then the command is evaluated and the state variables are saved as the defaults for all commands within its scope, ```javascript command(function (context) { // ... run sub commands }) command(props, function (context) { // ... run sub commands }) ``` * * * ### Inputs Inputs to `regl` commands can come from one of three sources, - Context: Context variables are not used directly in commands, but can be passed into - Props: props are arguments which are passed into commands - `this`: `this` variables are indexed from the `this` variable that the command was called with If you are familiar with Facebook's [react](https://github.com/facebook/react), these are roughly analogous to a component's [context](https://facebook.github.io/react/docs/context.html), [props](https://facebook.github.io/react/docs/transferring-props.html) and [state](https://facebook.github.io/react/docs/component-api.html#setstate) variables respectively. #### Example ```javascript var drawSpinningStretchyTriangle = regl({ frag: ` void main() { gl_FragColor = vec4(1, 0, 0, 1); }`, vert: ` attribute vec2 position; uniform float angle, scale, width, height; void main() { float aspect = width / height; gl_Position = vec4( scale * (cos(angle) * position.x - sin(angle) * position.y), aspect * scale * (sin(angle) * position.x + cos(angle) * position.y), 0, 1.0); }`, attributes: { position: [[0, -1], [-1, 0], [1, 1]] }, uniforms: { // // Dynamic properties can be functions. Each function gets passed: // // * context: which contains data about the current regl environment // * props: which are user specified arguments // * batchId: which is the index of the draw command in the batch // angle: function (context, props, batchId) { return props.speed * context.tick + 0.01 * batchId }, // As a shortcut/optimization we can also just read out a property // from the props. For example, this // scale: regl.prop('scale'), // // is semantically equivalent to // // scale: function (context, props) { // return props.scale // } // // Similarly there are shortcuts for accessing context variables width: regl.context('viewportWidth'), height: regl.context('viewportHeight'), // // which is the same as writing: // // width: function (context) { // return context.viewportWidth // } // }, count: 3 }) ``` To run a draw command with dynamic arguments we pass it a configuration object as the first argument, ```javascript // Draws one spinning triangle drawSpinningStretchyTriangle({ scale: 0.5, speed: 2 }) // Draws multiple spinning triangles drawSpinningStretchyTriangle([ { // batchId 0 scale: 1, speed: 1, }, { // batchId 1 scale: 2, speed: 0.1, }, { // batchId 2 scale: 0.25, speed: 3 } ]) ``` #### Context Context variables in `regl` are computed before any other parameters and can also be passed from a scoped command to any sub-commands. `regl` defines the following default context variables: | Name | Description | | --------------------- | ------------------------------------------------------------ | | `tick` | The number of frames rendered | | `time` | Total time elapsed since the regl was initialized in seconds | | `viewportWidth` | Width of the current viewport in pixels | | `viewportHeight` | Height of the current viewport in pixels | | `framebufferWidth` | Width of the current framebuffer in pixels | | `framebufferHeight` | Height of the current framebuffer in pixels | | `drawingBufferWidth` | Width of the WebGL context drawing buffer | | `drawingBufferHeight` | Height of the WebGL context drawing buffer | | `pixelRatio` | The pixel ratio of the drawing buffer | You can define context variables in the `context` block of a command. For example, here is how you can use context variables to set up a camera: ```javascript // This scoped command sets up the camera parameters var setupCamera = regl({ context: { projection: function (context) { return mat4.perspective([], Math.PI / 4, context.viewportWidth / context.viewportHeight, 0.01, 1000.0) }, view: function (context, props) { return mat4.lookAt([], props.eye, props.target, [0, 1, 0]) }, eye: regl.props('eye') }, uniforms: { view: regl.context('view'), invView: function (context) { return mat4.inverse([], context.view) }, projection: regl.context('projection') } }) // ... do stuff // In the render function: setupCamera({ eye: [10, 0, 0], target: [0, 0, 0] }, function () { // draw stuff }) ``` #### Props The most common way to pass data into regl is via props. The props for a render command are passed as an argument when the command is called. If the props are an array, then the command is executed in batch mode, once for each prop in order. Otherwise, if the props are an object, then the command is executed once with the props as an input. Any dynamic variable in the command may access the props as the second argument. For example: ```javascript const setupUniform = regl({ // ... uniforms: { foo: function (context, props) { return props.foo }, bar: regl.prop('bar') // this is equivalent to: // // function (context, props) { return props.bar } // } }) ``` #### `this` While `regl` strives to provide a stateless API, there are a few cases where it can be useful to cache state locally to a specific command. One way to achieve this is to use objects. When a regl command is run as a member function of an object, the `this` parameter is set to the object on which it was called and is passed to all computed parameters. For example, this shows how to use regl to create a simple reusable mesh object, ```javascript // First we create a constructor function Mesh (center, {positions, cells}) { this.center = center this.positions = regl.buffer(positions) this.cells = regl.buffer(cells) } // Then we assign regl commands directly to the prototype of the class Mesh.prototype.draw = regl({ vert: ` uniform mat4 projection, view, model; attribute vec3 position; void main () { gl_Position = projection * view * model * vec4(position, 1); }`, frag: ` void main () { gl_FragColor = vec4(1, 0, 0, 1); }`, uniforms: { // dynamic properties are invoked with the same `this` as the command model: function () { var c = this.center return [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -c[0], -c[1], -c[2], 1 ] }, view: regl.prop('view'), projection: regl.prop('projection') }, attributes: { // here we are using 'positions' property of the mesh position: regl.this('positions') }, // and same for the cells elements: regl.this('cells') }) ``` Once defined, we could then use these mesh objects as follows: ```javascript // Initialize meshes var bunnyMesh = new Mesh([5, 2, 1], require('bunny')) var teapotMesh = new Mesh([0, -3, 0], require('teapot')) // ... set up rest of scene, compute matrices etc. var view, projection // Now draw meshes: bunnyMesh.draw({ view: view, projection: projection }) teapotMesh.draw({ view: view, projection: projection }) ``` * * * ### Parameters The input to a command declaration is a complete description of the WebGL state machine in the form of an object. The properties of this object are parameters which specify how values in the WebGL state machine are to be computed. * * * #### Shaders Each draw command can specify the source code for a vertex and/or fragment shader, ```javascript var command = regl({ // ... vert: ` void main() { gl_Position = vec4(0, 0, 0, 1); }`, frag: ` void main() { gl_FragColor = vec4(1, 0, 1, 1); }`, // ... }) ``` | Property | Description | | -------- | ------------------------------ | | `vert` | Source code of vertex shader | | `frag` | Source code of fragment shader | **Related WebGL APIs** - [`gl.createShader`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCreateShader.xml) - [`gl.shaderSource`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glShaderSource.xml) - [`gl.compileShader`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCompileShader.xml) - [`gl.createProgram`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCreateProgram.xml) - [`gl.attachShader`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glAttachShader.xml) - [`gl.linkProgram`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glLinkProgram.xml) - [`gl.useProgram`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glUseProgram.xml) * * * #### Uniforms Uniform variables are specified in the `uniforms` block of the command. For example, ```javascript var command = regl({ // ... vert: ` struct SomeStruct { float value; }; uniform vec4 someUniform; uniform int anotherUniform; uniform SomeStruct nested; uniform vec4 colors[2]; void main() { gl_Position = vec4(1, 0, 0, 1); }`, uniforms: { someUniform: [1, 0, 0, 1], anotherUniform: regl.prop('myProp'), 'nested.value': 5.3, 'colors[0]': [0, 1, 0, 1], 'colors[1]': [0, 0, 1, 1] }, // ... }) ``` **Notes** - To specify uniforms in GLSL structs use the fully qualified path with dot notation. - To specify uniforms in GLSL arrays use the fully qualified path with bracket notation. - Matrix uniforms are specified as flat length n^2 arrays without transposing **Related WebGL APIs** - [`gl.getUniformLocation`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGetUniformLocation.xml) - [`gl.uniform`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml) * * * #### Attributes ```javascript var command = regl({ // ... attributes: { // Attributes can be declared explicitly normal: { buffer: regl.buffer([ // ... ]), offset: 0, stride: 12, normalized: false, // divisor is only used if instancing is enabled divisor: 0 }, // A regl.buffer or the arguments to regl.buffer may also be specified position: [ 0, 1, 2, 2, 3, 4, ... ], // Finally, attributes may be initialized with a constant value color: { constant: [1, 0, 1, 1] } }, // ... }) ``` Each attribute can have any of the following optional properties, | Property | Description | Default | | ------------ | ------------------------------------------------ | -------------------- | | `buffer` | A `REGLBuffer` wrapping the buffer object | `null` | | `offset` | The offset of the `vertexAttribPointer` in bytes | `0` | | `stride` | The stride of the `vertexAttribPointer` in bytes | `0` | | `normalized` | Whether the pointer is normalized | `false` | | `size` | The size of the vertex attribute | Inferred from shader | | `divisor` | Sets `gl.vertexAttribDivisorANGLE` | `0` \* | | `type` | Data type (see [buffer constructor](#buffer-constructor) for options) | Inferred from buffer | **Notes** - Attribute size is inferred from the shader vertex attribute if not specified - If a buffer is passed for an attribute then all pointer info is inferred - If the arguments to `regl.buffer` are passed, then a buffer is constructed - If an array is passed to an attribute, then the vertex attribute is set to a constant - `divisor` is only supported if the `ANGLE_instanced_arrays` extension is available **Related WebGL APIs** - [`gl.vertexAttribPointer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glVertexAttribPointer.xml) - [`gl.vertexAttrib`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glVertexAttrib.xml) - [`gl.getAttribLocation`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGetAttribLocation.xml) - [`gl.vertexAttibDivisor`](https://www.opengl.org/sdk/docs/man4/html/glVertexAttribDivisor.xhtml) - [`gl.enableVertexAttribArray`, `gl.disableVertexAttribArray`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDisableVertexAttribArray.xml) * * * #### Drawing ```javascript var command = regl({ // ... primitive: 'triangles', offset: 0, count: 6 }) ``` | Property | Description | Default | | ----------- | ----------------------------- | ---------------- | | `primitive` | Sets the primitive type | `'triangles'` \* | | `count` | Number of vertices to draw | `0` \* | | `offset` | Offset of primitives to draw | `0` | | `instances` | Number of instances to render | `0` \*\* | | `elements` | Element array buffer | `null` | **Notes** - If `elements` is specified while `primitive`, `count` and `offset` are not, then these values may be inferred from the state of the element array buffer. - `elements` must be either an instance of `regl.elements` or else the arguments to `regl.elements` - `instances` is only applicable if the `ANGLE_instanced_arrays` extension is present. - `primitive` can take on the following values | Primitive type | Description | | ------------------ | ------------------- | | `'points'` | `gl.POINTS` | | `'lines'` | `gl.LINES` | | `'line strip'` | `gl.LINE_STRIP` | | `'line loop` | `gl.LINE_LOOP` | | `'triangles` | `gl.TRIANGLES` | | `'triangle strip'` | `gl.TRIANGLE_STRIP` | | `'triangle fan'` | `gl.TRIANGLE_FAN` | **Related WebGL APIs** - [`gl.drawArrays`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDrawArrays.xml) - [`gl.drawElements`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDrawElements.xml) - [`gl.drawArraysInstancedANGLE`](https://www.opengl.org/sdk/docs/man4/html/glDrawArraysInstanced.xhtml) - [`gl.drawElementsInstancedANGLE`](https://www.opengl.org/sdk/docs/man4/html/glDrawElementsInstanced.xhtml) * * * #### Render target A `regl.framebuffer` object may also be specified to allow for rendering to offscreen locations. ```javascript var command = regl({ framebuffer: fbo }) ``` **Notes** - `framebuffer` must be a `regl.framebuffer` object - Passing `null` sets the framebuffer to the drawing buffer - Updating the render target will modify the viewport **Related WebGL APIs** - [`gl.bindFramebuffer`](https://www.opengl.org/sdk/docs/man4/html/glBindFramebuffer.xhtml) * * * #### Profiling `regl` can optionally instrument commands to track profiling data. This is toggled by setting the `profile` flag on each command. ```javascript var myScope = regl({ profile: true }) var drawA = regl({ ... }) var drawB = regl({ ... }) regl.frame(function () { myScope(function () { drawA() drawB() }) console.log(drawA.stats.count) console.log(drawB.stats.count) }) ``` The following stats are tracked for each command in the `.stats` property: | Statistic | Meaning | | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `count` | The number of times the command has been called | | `cpuTime` | The cumulative CPU time spent executing the command in milliseconds | | `gpuTime` | The cumulative GPU time spent executing the command in milliseconds (requires the [EXT_disjoint_timer_query](https://www.khronos.org/registry/webgl/extensions/EXT_disjoint_timer_query/) extension) | **Notes** - GPU timer queries update asynchronously. If you are not using `regl.frame()` to tick your application, then you should periodically call `regl.poll()` each frame to update the timer statistics. - CPU time uses `performance.now` if available, otherwise it falls back to `Date.now` **Related WebGL APIs** - [EXT_disjoint_timer_query](https://www.khronos.org/registry/webgl/extensions/EXT_disjoint_timer_query/) * * * #### Depth buffer All state relating to the depth buffer is stored in the `depth` field of the command. For example, ```javascript var command = regl({ // ... depth: { enable: true, mask: true, func: 'less', range: [0, 1] }, // .. }) ``` | Property | Description | Default | | -------- | -------------------------------------------------------- | -------- | | `enable` | Toggles `gl.enable(gl.DEPTH_TEST)` | `true` | | `mask` | Sets `gl.depthMask` | `true` | | `range` | Sets `gl.depthRange` | `[0, 1]` | | `func` | Sets `gl.depthFunc`. See table below for possible values | `'less'` | **Notes** - `depth.func` can take on the possible values | Value | Description | | ------------------ | ------------- | | `'never'` | `gl.NEVER` | | `'always'` | `gl.ALWAYS` | | `'<', 'less'` | `gl.LESS` | | `'<=', 'lequal'` | `gl.LEQUAL` | | `'>', 'greater'` | `gl.GREATER` | | `'>=', 'gequal'` | `gl.GEQUAL` | | `'=', 'equal'` | `gl.EQUAL` | | `'!=', 'notequal'` | `gl.NOTEQUAL` | **Related WebGL APIs** - [`gl.depthFunc`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDepthFunc.xml) - [`gl.depthMask`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDepthMask.xml) - [`gl.depthRange`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDepthRangef.xml) * * * #### Blending Blending information is stored in the `blend` field, ```javascript var command = regl({ // ... blend: { enable: true, func: { srcRGB: 'src alpha', srcAlpha: 1, dstRGB: 'one minus src alpha', dstAlpha: 1 }, equation: { rgb: 'add', alpha: 'add' }, color: [0, 0, 0, 0] }, // ... }) ``` | Property | Description | Default | | ---------- | ----------------------------------- | ----------------------- | | `enable` | Toggles `gl.enable(gl.BLEND)` | `false` | | `equation` | Sets `gl.blendEquation` (see table) | `'add'` | | `func` | Sets `gl.blendFunc` (see table) | `{src:'one',dst:'one'}` | | `color` | Sets `gl.blendColor` | `[0, 0, 0, 0]` | **Notes** - `equation` can be either a string or an object with the fields `{rgb, alpha}`. The former corresponds to `gl.blendEquation` and the latter to `gl.blendEquationSeparate` - The fields of `equation` can take on the following values | Equation | Description | | -------------------- | -------------------------- | | `'add'` | `gl.FUNC_ADD` | | `'subtract'` | `gl.FUNC_SUBTRACT` | | `'reverse subtract'` | `gl.FUNC_REVERSE_SUBTRACT` | | `'min'` | `gl.MIN_EXT` | | `'max'` | `gl.MAX_EXT` | - `'min'` and `'max'` are only available if the `EXT_blend_minmax` extension is supported - `func` can be an object with the fields `{src, dst}` or `{srcRGB, srcAlpha, dstRGB, dstAlpha}`, with the former corresponding to `gl.blendFunc` and the latter to `gl.blendFuncSeparate` - The fields of `func` can take on the following values | Func | Description | | ---------------------------- | ----------------------------- | | `0, 'zero'` | `gl.ZERO` | | `1, 'one'` | `gl.ONE` | | `'src color'` | `gl.SRC_COLOR` | | `'one minus src color'` | `gl.ONE_MINUS_SRC_COLOR` | | `'src alpha'` | `gl.SRC_ALPHA` | | `'one minus src alpha'` | `gl.ONE_MINUS_SRC_ALPHA` | | `'dst color'` | `gl.DST_COLOR` | | `'one minus dst color'` | `gl.ONE_MINUS_DST_COLOR` | | `'dst alpha'` | `gl.DST_ALPHA` | | `'one minus dst alpha'` | `gl.ONE_MINUS_DST_ALPHA` | | `'constant color'` | `gl.CONSTANT_COLOR` | | `'one minus constant color'` | `gl.ONE_MINUS_CONSTANT_COLOR` | | `'constant alpha'` | `gl.CONSTANT_ALPHA` | | `'one minus constant alpha'` | `gl.ONE_MINUS_CONSTANT_ALPHA` | | `'src alpha saturate'` | `gl.SRC_ALPHA_SATURATE` | **Related WebGL APIs** - [`gl.blendEquationSeparate`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBlendEquationSeparate.xml) - [`gl.blendFuncSeparate`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBlendFuncSeparate.xml) - [`gl.blendColor`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBlendColor.xml) * * * #### Stencil Example: ```javascript var command = regl({ // ... stencil: { enable: true, mask: 0xff, func: { cmp: '<', ref: 0, mask: 0xff }, opFront: { fail: 'keep', zfail: 'keep', zpass: 'keep' }, opBack: { fail: 'keep', zfail: 'keep', zpass: 'keep' } }, // ... }) ``` | Property | Description | Default | | --------- | ------------------------------------------ | ----------------------------------------- | | `enable` | Toggles `gl.enable(gl.STENCIL_TEST)` | `false` | | `mask` | Sets `gl.stencilMask` | `-1` | | `func` | Sets `gl.stencilFunc` | `{cmp:'always',ref:0,mask:-1}` | | `opFront` | Sets `gl.stencilOpSeparate` for front face | `{fail:'keep',zfail:'keep',zpass:'keep'}` | | `opBack` | Sets `gl.stencilOpSeparate` for back face | `{fail:'keep',zfail:'keep',zpass:'keep'}` | | `op` | Sets `opFront` and `opBack` simultaneously | | **Notes** - `func` is an object which configures the stencil test function. It has 3 properties, - `cmp` which is the comparison function - `ref` which is the reference value - `mask` which is the comparison mask - `func.cmp` is a comparison operator which takes one of the following values, | Value | Description | | ------------------ | ------------- | | `'never'` | `gl.NEVER` | | `'always'` | `gl.ALWAYS` | | `'<', 'less'` | `gl.LESS` | | `'<=', 'lequal'` | `gl.LEQUAL` | | `'>', 'greater'` | `gl.GREATER` | | `'>=', 'gequal'` | `gl.GEQUAL` | | `'=', 'equal'` | `gl.EQUAL` | | `'!=', 'notequal'` | `gl.NOTEQUAL` | - `opFront` and `opBack` specify the stencil op. Each is an object which takes the following parameters: - `fail`, the stencil op which is applied when the stencil test fails - `zfail`, the stencil op which is applied when the stencil test passes and the depth test fails - `zpass`, the stencil op which is applied when both stencil and depth tests pass - Values for `op.fail`, `op.zfail`, `op.zpass` can come from the following table | Stencil Op | Description | | ------------------ | -------------- | | `'zero'` | `gl.ZERO` | | `'keep'` | `gl.KEEP` | | `'replace'` | `gl.REPLACE` | | `'invert'` | `gl.INVERT` | | `'increment'` | `gl.INCR` | | `'decrement'` | `gl.DECR` | | `'increment wrap'` | `gl.INCR_WRAP` | | `'decrement wrap'` | `gl.DECR_WRAP` | **Related WebGL APIs** - [`gl.stencilFunc`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glStencilFunc.xml) - [`gl.stencilMask`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glStencilMask.xml) - [`gl.stencilOpSeparate`](http://www.khronos.org/opengles/sdk/2.0/docs/man/xhtml/glStencilOpSeparate.xml) * * * #### Polygon offset Polygon offsetting behavior can be controlled using the `polygonOffset` field, ```javascript var command = regl({ // ... polygonOffset: { enable: true, offset: { factor: 1, units: 0 } } // ... }) ``` | Property | Description | Default | | -------- | ------------------------------------------- | --------------------- | | `enable` | Toggles `gl.enable(gl.POLYGON_OFFSET_FILL)` | `false` | | `offset` | Sets `gl.polygonOffset` | `{factor:0, units:0}` | **Related WebGL APIs** - [`gl.polygonOffset`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glPolygonOffset.xml) * * * #### Culling Example, ```javascript var command = regl({ // ... cull: { enable: true, face: 'back' }, // ... }) ``` | Property | Description | Default | | -------- | --------------------------------- | -------- | | `enable` | Toggles `gl.enable(gl.CULL_FACE)` | `false` | | `face` | Sets `gl.cullFace` | `'back'` | **Notes** - `face` must be one of the following values, | Face | Description | | --------- | ----------- | | `'front'` | `gl.FRONT` | | `'back'` | `gl.BACK` | **Relevant WebGL APIs** - [`gl.cullFace`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCullFace.xml) * * * #### Front face Example, ```javascript var command = regl({ // ... frontFace: 'cw', // ... }) ``` | Property | Description | Default | | ----------- | ------------------- | ------- | | `frontFace` | Sets `gl.frontFace` | `'ccw'` | **Notes** - The value for front face must be one of the following, | Orientation | Description | | ----------- | ----------- | | `'cw'` | `gl.CW` | | `'ccw'` | `gl.CCW` | **Relevant WebGL APIs** - [`gl.frontFace`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glFrontFace.xml) * * * #### Dithering Example, ```javascript var command = regl({ // ... dither: true, // ... }) ``` | Property | Description | Default | | -------- | ------------------- | ------- | | `dither` | Toggles `gl.DITHER` | `false` | * * * #### Line width Example, ```javascript var command = regl({ // ... lineWidth: 4, // ... }) ``` | Property | Description | Default | | ----------- | ------------------- | ------- | | `lineWidth` | Sets `gl.lineWidth` | `1` | **Relevant WebGL APIs** - [`gl.lineWidth`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glLineWidth.xml) * * * #### Color mask Example, ```javascript var command = regl({ // ... colorMask: [true, false, true, false], // ... }) ``` | Property | Description | Default | | ----------- | ------------------- | -------------------------- | | `colorMask` | Sets `gl.colorMask` | `[true, true, true, true]` | **Relevant WebGL APIs** - [`gl.colorMask`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glColorMask.xml) * * * #### Sample coverage Example, ```javascript var command = regl({ // ... sample: { enable: true, alpha: false, coverage: { value: 1, invert: false } }, // ... }) ``` | Property | Description | Default | | ---------- | ------------------------------------------------ | ------------------------ | | `enable` | Toggles `gl.enable(gl.SAMPLE_COVERAGE)` | `false` | | `alpha` | Toggles `gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE)` | `false` | | `coverage` | Sets `gl.sampleCoverage` | `{value:1,invert:false}` | **Relevant WebGL APIs** - [`gl.sampleCoverage`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glSampleCoverage.xml) * * * #### Scissor Example, ```javascript var command = regl({ // ... scissor: { enable: true, box: { x: 10, y: 20, width: 100, height: 100 } } // ... }) ``` | Property | Description | Default | | -------- | ------------------------------- | ------- | | `enable` | Toggles `gl.enable(gl.SCISSOR)` | `false` | | `box` | Sets `gl.scissor` | `{}` | **Notes** - `box` is the shape of the scissor region, it takes the following parameters - `x` is the left coordinate of the box, default `0` - `y` is the top coordiante of the box, default `0` - `width` is the width of the box, default fbo width - `x` - `height` is the height of the box, default fbo height - `y` **Relevant WebGL APIs** - [`gl.scissor`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glScissor.xml) * * * #### Viewport Example, ```javascript var command = regl({ // ... viewport: { x: 5, y: 10, width: 100, height: 50 } // ... }) ``` | Property | Description | Default | | ---------- | --------------------- | ------- | | `viewport` | The shape of viewport | `{}` | **Notes** - Like `scissor.box`, `viewport` is a bounding box with properties `x,y,w,h` - Updating `viewport` will modify the context variables `viewportWidth` and `viewportHeight` **Relevant WebGL APIs** - [`gl.viewport`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glViewport.xml) * * * ## Resources Besides commands, the other major component of regl are resources. Resources are GPU resident objects which are managed explicitly by the programmer. Each resource follows a the same life cycle of create/read/update/delete. * * * ### Buffers `regl.buffer` wraps WebGL array buffer objects. #### Buffer constructor ```javascript // Creates an empty length 100 buffer var zeroBuffer = regl.buffer(100) // A buffer with float data var floatBuffer = regl.buffer(new Float32Array([1, 2, 3, 4])) // A streaming buffer of bytes var streamBuffer = regl.buffer({ usage: 'stream', data: new Uint8Array([2, 4, 6, 8, 10]) }) // An unpacked buffer of position data var positionBuffer = regl.buffer([ [1, 2, 3], [4, 5, 6], [2, 1, -2] ]) ``` | Property | Description | Default | | -------- | ---------------------------------------------------------------- | ---------- | | `data` | The data for the vertex buffer (see below) | `null` | | `length` | If `data` is `null` or not present reserves space for the buffer | `0` | | `usage` | Sets array buffer usage hint | `'static'` | | `type` | Data type for vertex buffer | `'uint8'` | - `usage` can be one of the following values | Usage Hint | Description | | ----------- | ----------------- | | `'static'` | `gl.DRAW_STATIC` | | `'dynamic'` | `gl.DYNAMIC_DRAW` | | `'stream'` | `gl.STREAM_DRAW` | - `type` can be one of the following data types | Data type | Description | | ---------------------- | ------------------- | | `'uint8'` | `gl.UNSIGNED_BYTE` | | `'int8'` | `gl.BYTE` | | `'uint16'` | `gl.UNSIGNED_SHORT` | | `'int16'` | `gl.SHORT` | | `'uint32'` | `gl.UNSIGNED_INT` | | `'int32'` | `gl.INT` | | `'float32'`, `'float'` | `gl.FLOAT` | **Relevant WebGL APIs** - [`gl.createBuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCreateBuffer.xml) - [`gl.bufferData`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferData.xml) #### Buffer update To reinitialize a buffer in place, we can call the buffer as a function: ```javascript // First we create a buffer var myBuffer = regl.buffer(5) // ... do stuff ... // Now reinitialize myBuffer myBuffer({ data: [ 1, 2, 3, 4, 5 ] }) ``` The arguments to the update pathway are the same as the constructor and the returned value will be a reference to the buffer. **Relevant WebGL APIs** - [`gl.bufferData`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferData.xml) ##### Buffer subdata For performance reasons we may sometimes want to update just a portion of the buffer. You can update a portion of the buffer using the `subdata` method. This can be useful if you are dealing with frequently changing or streaming vertex data. Here is an example: ```javascript // First we preallocate a buffer with 100 bytes of data var myBuffer = regl.buffer({ usage: 'dynamic', // give the WebGL driver a hint that this buffer may change type: 'float', length: 100 }) // Now we initialize the head of the buffer with the following data myBuffer.subdata([ 0, 1, 2, 3, 4, 5 ]) // // untyped arrays and arrays-of-arrays are converted to the same data type as // the buffer. typedarrays are copied bit-for-bit into the buffer // with no type conversion. // // We can also update the buffer at some byte offset by passing this as // the second argument to subdata myBuffer.subdata([[7, 8], [9, 10]], 8) // // now the contents of myBuffer are: // // new Float32Array([0, 1, 7, 8, 9, 10, 0, 0, 0, .... ]) // ``` **Relevant WebGL APIs** - [`gl.bufferSubData`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferSubData.xml) #### Buffer destructor Calling `.destroy()` on a buffer releases all resources associated to the buffer: ```javascript // Create a buffer var myBuffer = regl.buffer(10) // destroys the buffer myBuffer.destroy() ``` - [`gl.deleteBuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteBuffer.xml) #### Profiling info The following stats are tracked for each buffer in the `.stats` property: | Statistic | Meaning | | --------- | ------------------------------- | | `size` | The size of the buffer in bytes | * * * ### Vertex array objects `regl.vao` wraps WebGL vertex array objects. A vertex array object is a complete binding state for the set of all attributes for a given shader. This feature requires some caution when it is used since it depends on the specific ordering of vertex attributes which is determined at program link time. This will only have a performance benefit when the `OES_vertex_array_object` extension is enabled. If `OES_vertex_array_object` is not enabled, then vertex array objects are emulated. If `OES_vertex_array_object` is enabled then regl will try to optimize static draw commands when possible to user vertex array objects. #### Vertex array object constructor A vertex array object constructor takes as input an array of vertex bindings: ```javascript // First we create the VAO object var vao = regl.vao([ // first attribute is a triangle [ [0, 1], [1, 0], [1, 1] ], // second attribute is a color { x: 1, y: 0, z: 1 } ]) // then we create the command var command = regl({ frag: ` precision highp float; varying vec3 fragColor; void main () { gl_FragColor = vec4(fragColor, 1.); }`, vert: ` precision highp float; attribute vec2 position; attribute vec3 color; varying vec3 fragColor; void main () { fragColor = color; gl_Position = vec4(position, 0, 1); }`, // specify the vertex array object for this command vao: vao, // when using a VAO object we give numerical ids for each attribute binding location attributes: { position: 0, color: 1 }, count: 3 }) ``` You can also bake the element state into a vao: ```javascript // First we create the VAO object var vao = regl.vao({ attributes: [ [ [0, 1], [1, 0], [1, 1] ], }, // if not specified, then default is no elements elements: [[ 0, 1, 2 ]], ]) ``` **Relevant WebGL APIs** - [`OES_vertex_array_object`](https://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/) #### Vertex array object update You can update a vertex array object just like a buffer, ```javascript vao([ [ [0, 1], [1, 0], [0.5, 1] ], { x: 1, y: 1, z: 0 } ]) ``` #### Vertex array object destructor ```javascript vao.destroy() ``` ### Elements `regl.elements` wraps WebGL element array buffer objects. Each `regl.elements` object stores a buffer object as well as the primitive type and vertex count. #### Element constructor ```javascript var triElements = regl.elements([ [1, 2, 3], [0, 2, 5] ]) var starElements = regl.elements({ primitive: 'line loop', count: 5, data: new Uint8Array([0, 2, 4, 1, 3]) }) ``` | Property | Description | Default | | ----------- | ----------------------------------------- | ---------------- | | `data` | The data of the element buffer | `null` | | `usage` | Usage hint (see `gl.bufferData`) | `'static'` | | `length` | Length of the element buffer in bytes | `0` \* | | `primitive` | Default primitive type for element buffer | `'triangles'` \* | | `type` | Data type for element buffer | `'uint8'` | | `count` | Vertex count for element buffer | `0` \* | - `usage` must take on one of the following values | Usage Hint | Description | | ----------- | ----------------- | | `'static'` | `gl.DRAW_STATIC` | | `'dynamic'` | `gl.DYNAMIC_DRAW` | | `'stream'` | `gl.STREAM_DRAW` | - `primitive` can be one of the following primitive types | Primitive type | Description | | ------------------ | ------------------- | | `'points'` | `gl.POINTS` | | `'lines'` | `gl.LINES` | | `'line strip'` | `gl.LINE_STRIP` | | `'line loop'` | `gl.LINE_LOOP` | | `'triangles'` | `gl.TRIANGLES` | | `'triangle strip'` | `gl.TRIANGLE_STRIP` | | `'triangle fan'` | `gl.TRIANGLE_FAN` | - `type` can be one of the following data types | Data type | Description | Extension? | | ---------- | ------------------- | --------------------------------------------------------------------------------------------------- | | `'uint8'` | `gl.UNSIGNED_BYTE` | | | `'uint16'` | `gl.UNSIGNED_SHORT` | | | `'uint32'` | `gl.UNSIGNED_INT` | [OES_element_index_uint](https://www.khronos.org/registry/webgl/extensions/OES_element_index_uint/) | **Notes** - `primitive`, `count` and `length` are inferred from from the vertex data **Relevant WebGL APIs** - [`gl.createBuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCreateBuffer.xml) - [`gl.bufferData`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferData.xml) - [`gl.drawElements`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDrawElements.xml) #### Element update As in the case of buffers, calling an element buffer as a function reinitializes an element buffer in place. The arguments are the same as for the constructor. For example: ```javascript // First we create an element buffer var myElements = regl.elements() // Then we update it by calling it directly myElements({ data: [ [1, 2, 3], [0, 2, 1] ] }) ``` **Relevant WebGL APIs** - [`gl.bufferData`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferData.xml) ##### Element subdata Again like buffers it is possible to preallocate an element buffer and update regions of the elements using the `subdata` command. ```javascript // First we preallocate the element buffer var myElements = regl.elements({ primitive: 'triangles', usage: 'dynamic', type: 'uint16', length: 4096, count: 0 }) // Then we can update into ranges of the element buffer using subdata myElements.subdata( [ [0, 1, 2], [2, 1, 3] ]) ``` **Relevant WebGL APIs** - [`gl.bufferSubData`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferSubData.xml) #### Element destructor ```javascript // First we create an element buffer var myElements = regl.elements({ ... }) // Calling .destroy() on an element buffer releases all resources associated to // it myElements.destroy() ``` **Relevant WebGL APIs** - [`gl.deleteBuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteBuffer.xml) * * * ### Textures #### Texture constructor There are many ways to upload data to a texture in WebGL. As with drawing commands, regl consolidates all of these configuration parameters into one function. Here are some examples of how to create a texture, ```javascript // From size parameters var emptyTexture = regl.texture({ shape: [16, 16] }) // From a flat array var typedArrayTexture = regl.texture({ width: 2, height: 2, data: [ 255, 255, 255, 255, 0, 0, 0, 0, 255, 0, 255, 255, 0, 0, 255, 255 ] }) // From a square array var nestedArrayTexture = regl.texture([ [ [0, 255, 0], [255, 0, 0] ], [ [0, 0, 255], [255, 255, 255] ] ]) // From an ndarray-like object var ndarrayTexture = regl.texture(require('baboon-image')) // Manual mipmap specification var mipmapTexture = regl.texture({ min: 'mipmap' }) // From an image element var image = new Image() image.src = 'http://mydomain.com/myimage.png' image.onload = function () { var imageTexture = regl.texture(image) } /* From an ImageBitmap * This is only useful when loading many images, as bitmaps can be created asynchronously * and will transfer faster than an image element. */ var image = new Image() image.src = 'http://mydomain.com/myimage.png' image.onload = function () { createImageBitmap(image).then(function (bitmap) { var imageTexture = regl.texture(bitmap) } } // From a canvas var canvas = document.createElement(canvas) var context2D = canvas.getContext('2d') var canvasTexture = regl.texture(canvas) var otherCanvasTexture = regl.texture(context2D) // From a video element var video = document.querySelector('video') var videoTexture = regl.texture(video) // From the pixels in the current frame buffer var copyPixels = regl.texture({ x: 5, y: 1, width: 10, height: 10, copy: true }) ``` A data source from an image can be one of the following types: | Data type | Description | | --------------------------- | ------------------------------------------------------------------------------------------------------- | | Rectangular array of arrays | Interpreted as 2D array of arrays | | Typed array | A binary array of pixel values | | Array | Interpreted as array of pixel values with type based on the input type | | `ndarray` | Any object with a `shape, stride, offset, data` (see [SciJS ndarray](https://github.com/scijs/ndarray)) | | Image | An HTML image element | | ImageBitmap | An ImageBitmap object | | Video | An HTML video element | | Canvas | A canvas element | | Context 2D | A canvas 2D context | | Property | Description | Default | | | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ------- | | `width` | Width of texture | `0` | | | `height` | Height of texture | `0` | | | `mag` | Sets magnification filter (see table) | `'nearest'` | | | `min` | Sets minification filter (see table) | `'nearest'` | | | `wrapS` | Sets wrap mode on S axis (see table) | `'clamp'` | | | `wrapT` | Sets wrap mode on T axis (see table) | `'clamp'` | | | `aniso` | Sets number of anisotropic samples, requires [EXT_texture_filter_anisotropic](https://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/) | `0` | | | `format` | Texture format (see table) | `'rgba'` | | | `type` | Texture type (see table) | `'uint8'` | | | `data` | Input data (see below) | | | | `mipmap` | See below for a description | `false` | | | `flipY` | Flips textures vertically when uploading | `false` | | | `alignment` | Sets unpack alignment per row | `1` | | | `premultiplyAlpha` | Premultiply alpha when unpacking | `false` | | | `colorSpace` | Sets colorspace conversion | `'none'` | | | `data` | Image data for the texture | `null` | | | `copy` | | Copy the pixels in the current frame buffer. Cannot be used with `data` prop. | `false` | | `channels` | Number of channels for the texture format | `null` | | - `mipmap`. If `boolean`, then it sets whether or not we should regenerate the mipmaps. If a `string`, it allows you to specify a hint to the mipmap generator. It can be one of the hints below | Mipmap Hint | Description | | ----------------------------- | -------------- | | `'don't care'`, `'dont care'` | `gl.DONT_CARE` | | `'nice'` | `gl.NICEST` | | `'fast'` | `gl.FASTEST` | and if a hint is specified, then also the mipmaps will be regenerated. Finally, `mipmap` can also be an array of arrays. In this case, every subarray will be one of the mipmaps, and you can thus use this option to manually specify the mipmaps of the image. Like this: ```javascript regl.texture({ shape: [4, 4], mipmap: [ [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ], [ 0, 1, 2, 3 ], [ 0 ] ] }) ``` - `shape` can be used as an array shortcut for `[width, height, channels]` of image - `channels` can be used to set the number of color channels of the texture. Examples: var t1 = regl.texture({width: 1, height: 1, channels: 3}) // 'format' will be 'rgb' var t2 = regl.texture({shape: [2, 2, 2]}) // 'format' will be 'luminance alpha' var t3 = regl.texture({shape: [2, 2, 4]}) // 'format' will be 'rgba' So it can be used as an alternative to `format`. - `radius` can be specified for square images and sets both `width` and `height` - `data` can take one of the following values, - If an image element is specified and not yet loaded, then regl will upload a temporary image and hook a callback on the image - `mag` sets `gl.MAG_FILTER` for the texture and can have one of the following values | Mag filter | Description | | ----------- | ------------ | | `'nearest'` | `gl.NEAREST` | | `'linear'` | `gl.LINEAR` | - `min` sets `gl.MIN_FILTER` for the texture, and can take on one of the following values, | Min filter | Description | | ---------------------------------- | --------------------------- | | `'nearest'` | `gl.NEAREST` | | `'linear'` | `gl.LINEAR` | | `'mipmap', 'linear mipmap linear'` | `gl.LINEAR_MIPMAP_LINEAR` | | `'nearest mipmap linear'` | `gl.NEAREST_MIPMAP_LINEAR` | | `'linear mipmap nearest'` | `gl.LINEAR_MIPMAP_NEAREST` | | `'nearest mipmap nearest'` | `gl.NEAREST_MIPMAP_NEAREST` | - `wrap` can be used as an array shortcut for `[wrapS, wrapT]` - `wrapS` and `wrapT` can have any of the following values, | Wrap mode | Description | | ---------- | -------------------- | | `'repeat'` | `gl.REPEAT` | | `'clamp'` | `gl.CLAMP_TO_EDGE` | | `'mirror'` | `gl.MIRRORED_REPEAT` | - `format` determines the format of the texture and possibly the type. Possible values for `format` include, | Format | Description | Channels | Types | Compressed? | Extension? | | ------------------------------- | -------------------------------------------------- | -------- | ------------------------------ | ----------- | ------------------------------------------------------------------------------------------------------------------- | | `'alpha'` | `gl.ALPHA` | 1 | `'uint8','half float','float'` | ✖ | | | `'luminance'` | `gl.LUMINANCE` | 1 | `'uint8','half float','float'` | ✖ | | | `'luminance alpha'` | `gl.LUMINANCE_ALPHA` | 2 | `'uint8','half float','float'` | ✖ | | | `'rgb'` | `gl.RGB` | 3 | `'uint8','half float','float'` | ✖ | | | `'rgba'` | `gl.RGBA` | 4 | `'uint8','half float','float'` | ✖ | | | `'rgba4'` | `gl.RGBA4` | 4 | `'rgba4'` | ✖ | | | `'rgb5 a1'` | `gl.RGB5_A1` | 4 | `'rgb5 a1'` | ✖ | | | `'rgb565'` | `gl.RGB565` | 3 | `'rgb565'` | ✖ | | | `'srgb'` | `ext.SRGB` | 3 | `'uint8','half float','float'` | ✖ | [EXT_sRGB](https://www.khronos.org/registry/webgl/extensions/EXT_sRGB/) | | `'srgba'` | `ext.RGBA` | 4 | `'uint8','half float','float'` | ✖ | [EXT_sRGB](https://www.khronos.org/registry/webgl/extensions/EXT_sRGB/) | | `'depth'` | `gl.DEPTH_COMPONENT` | 1 | `'uint16','uint32'` | ✖ | [WEBGL_depth_texture](https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) | | `'depth stencil'` | `gl.DEPTH_STENCIL` | 2 | `'depth stencil'` | ✖ | [WEBGL_depth_texture](https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) | | `'rgb s3tc dxt1'` | `ext.COMPRESSED_RGB_S3TC_DXT1_EXT` | 3 | `'uint8'` | ✓ | [WEBGL_compressed_texture_s3tc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/) | | `'rgba s3tc dxt1'` | `ext.COMPRESSED_RGBA_S3TC_DXT1_EXT` | 4 | `'uint8'` | ✓ | [WEBGL_compressed_texture_s3tc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/) | | `'rgba s3tc dxt3'` | `ext.COMPRESSED_RGBA_S3TC_DXT3_EXT` | 4 | `'uint8'` | ✓ | [WEBGL_compressed_texture_s3tc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/) | | `'rgba s3tc dxt5'` | `ext.COMPRESSED_RGBA_S3TC_DXT5_EXT` | 4 | `'uint8'` | ✓ | [WEBGL_compressed_texture_s3tc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/) | | `'rgb atc'` | `ext.COMPRESSED_RGB_ATC_WEBGL` | 3 | `'uint8'` | ✓ | [WEBGL_compressed_texture_atc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_atc/) | | `'rgba atc explicit alpha'` | `ext.COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL` | 4 | `'uint8'` | ✓ | [WEBGL_compressed_texture_atc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_atc/) | | `'rgba atc interpolated alpha'` | `ext.COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL` | 4 | `'uint8'` | ✓ | [WEBGL_compressed_texture_atc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_atc/) | | `'rgb pvrtc 4bppv1'` | `ext.COMPRESSED_RGB_PVRTC_4BPPV1_IMG` | 3 | `'uint8'` | ✓ | [WEBGL_compressed_texture_pvrtc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/) | | `'rgb pvrtc 2bppv1'` | `ext.COMPRESSED_RGB_PVRTC_2BPPV1_IMG` | 3 | `'uint8'` | ✓ | [WEBGL_compressed_texture_pvrtc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/) | | `'rgba pvrtc 4bppv1'` | `ext.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG` | 4 | `'uint8'` | ✓ | [WEBGL_compressed_texture_pvrtc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/) | | `'rgba pvrtc 2bppv1'` | `ext.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG` | 4 | `'uint8'` | ✓ | [WEBGL_compressed_texture_pvrtc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/) | | `'rgb etc1'` | `ext.COMPRESSED_RGB_ETC1_WEBGL` | 3 | `'uint8'` | ✓ | [WEBGL_compressed_texture_etc1](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc1/) | - In many cases `type` can be inferred from the format and other information in the texture. However, in some situations it may still be necessary to set it manually. In such an event, the following values are possible, | Type | Description | | ------------------------- | -------------------- | | `'uint8'` | `gl.UNSIGNED_BYTE` | | `'uint16'` | `gl.UNSIGNED_SHORT` | | `'uint32'` | `gl.UNSIGNED_INT` | | `'float', 'float32'` | `gl.FLOAT` | | `'half float', 'float16'` | `ext.HALF_FLOAT_OES` | - `colorSpace` sets the WebGL color space flag for pixel unpacking | Color space | Description | | ----------- | -------------------------- | | `'none'` | `gl.NONE` | | `'browser'` | `gl.BROWSER_DEFAULT_WEBGL` | - `alignment` sets the pixel unpack alignment and must be one of `[1, 2, 4, 8]` **Relevant WebGL APIs** - [`gl.createTexture`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCreateTexture.xml) - [`gl.texParameter`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexParameter.xml) - [`gl.pixelStorei`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) - [`gl.texImage2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml) - [`gl.texImage2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml) - [`gl.compressedTexImage2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCompressedTexImage2D.xml) - [`gl.copyTexImage2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCopyTexImage2D.xml) - [`gl.generateMipmap`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGenerateMipmap.xml) - [`gl.hint`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glHint.xml) #### Texture update Like buffers, textures can be reinitialized in place. Calling the texture as a function re-evaluates the constructor and initializes the texture to a new value: ```javascript // First we create a texture const myTexture = regl.texture() // Then we can reinitialize it myTexture({ width: 10, height: 10 }) ``` Doing this lets you defer texture construction or reuse texture objects. **Relevant WebGL APIs** - [`gl.createTexture`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCreateTexture.xml) - [`gl.texParameter`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexParameter.xml) - [`gl.pixelStorei`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) - [`gl.texImage2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml) - [`gl.texImage2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml) - [`gl.compressedTexImage2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCompressedTexImage2D.xml) - [`gl.copyTexImage2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCopyTexImage2D.xml) - [`gl.generateMipmap`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGenerateMipmap.xml) ##### Texture subimage It is also possible to update a subset of a texture contained in a rectangle. This can be done using the `subimage()` method of the texture: ```javascript const myTexture = regl.texture(4, 4) myTexture.subimage({ width: 1, height: 1, data: [255, 0, 0, 255] }, 1, 1) ``` For textures, `subimage` takes 4 arguments: ```javascript texture.subimage(data[, x, y, level]) ``` Where, - `data` is an image data object, similar to the arguments for the texture constructor - `x, y` is the offset of the subimage within the texture (default `0,0`) - `level` is the miplevel to run the subimage within (default `0`) **Relevant WebGL APIs** - [`gl.texSubImage2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexSubImage2D.xml) - [`gl.copyTexSubImage2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCopyTexSubImage2D.xml) - [`gl.compressedTexSubImage2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCompressedTexSubImage2D.xml) ##### Texture resize Finally, textures can be resized with the `.resize()` method. Note that this clears the contents of the texture and is not supported by compressed textures. ```javascript var texture = regl.texture(5) texture.resize(3, 7) ``` ##### Texture properties The following properties contains information about the texture. | Property | Description | | -------- | ---------------------------- | | `width` | Width of texture | | `height` | Height of texture | | `format` | Texture Format | | `type` | Texture Type | | `mag` | Texture magnification filter | | `min` | Texture minification filter | | `wrapS` | Texture wrap mode on S axis | | `wrapT` | Texture wrap mode on T axis | They can be accessed after texture creation like this: ```javascript var t = regl.texture({ shape: [16, 16], min: 'nearest mipmap linear', mag: 'linear', wrapS: 'mirror', wrapT: 'repeat', format: 'rgb', type: 'uint8' }) console.log('tex info: ', t.width, t.height, t.min, t.mag, t.wrapS, t.wrapT, t.format, t.type) ``` #### Texture destructor Finally, when a texture is no longer needed it can be released by calling the `destroy()` method: ```javascript var myTexture = regl.texture({ ... }) myTexture.destroy() ``` **Relevant WebGL APIs** - [`gl.deleteTexture`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteTexture.xml) #### Texture profiling The following stats are tracked for each texture in the `.stats` property: | Statistic | Meaning | | --------- | -------------------------------- | | `size` | The size of the texture in bytes | * * * ### Cube maps #### Cube map constructor Cube maps follow similar syntax to textures. They are created using `regl.cube()` ```javascript // We can allocate a cubemap by giving just the size of the an edge: const emptyCube = regl.cube(16) // We can also specify each face individually const posX = new Image() const negX = new Image() // ... etc const cubeMap = regl.cube( posX, negX, posY, negY, posZ, negZ) // Or we can initialize each face using an array const anotherCubeMap = regl.cube({ radius: 4, faces: [ [ 0, 0, 0, 255, 255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255 ], // ... ] }) ``` #### Cube map update Cube maps can be reinitialized like textures or buffers: ```javascript const cube = regl.cube(8) // Reset cube map cube(4) // Resize cube map cube.resize(16) ``` ##### Cube map subimage Sub-rectangles of faces of cube maps can be updated again using `.subimage`. ```javascript const cube = regl.cube(4) cube.subimage(0, [ 0, 0, 0, 255, 0, 255, 0, 255, 255, 0, 0, 255, 0, 0, 255, 255 ]) ``` `cube.subimage` takes the following arguments: ```javascript cube.subimage(face, data[, x, y, miplevel]) ``` **Relevant WebGL APIs** - [`gl.texSubImage2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexSubImage2D.xml) - [`gl.copyTexSubImage2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCopyTexSubImage2D.xml) - [`gl.compressedTexSubImage2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCompressedTexSubImage2D.xml) #### Cube map resize Cube maps can be resized in place using the `.resize()` method. This takes one argument which is the size of the cube map. ```javascript var cubemap = regl.cube({ ... }) cubemap.resize(16) ``` #### Cube map properties The following properties contains information about the cube map. | Property | Description | | -------- | -------------------------------- | | `width` | Width of a single cube map face | | `height` | Height of a single cube map face | | `format` | Texture Format | | `type` | Texture Type | | `mag` | Texture magnification filter | | `min` | Texture minification filter | | `wrapS` | Texture wrap mode on S axis | | `wrapT` | Texture wrap mode on T axis | They can be accessed after cube map creation like this: ```javascript var c = regl.cube({ width: 2, height: 2 }) console.log('cube: ', c.width, c.height, c.format, c.type, c.mag, c.min, c.wrapS, c.wrapT) ``` #### Cube map profiling The following stats are tracked for each cube map in the `.stats` property: | Statistic | Meaning | | --------- | --------------------------------- | | `size` | The size of the cube map in bytes | #### Cube map destructor ```javascript cubeMap.destroy() ``` **Relevant WebGL APIs** - [`gl.deleteTexture`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteTexture.xml) * * * ### Renderbuffers #### Renderbuffer constructor ```javascript // Allocate a new renderbuffer with the prescribed format var rb = regl.renderbuffer({ width: 16, height: 16, format: 'rgba4' }) // Allocate an 'rgba4' renderbuffer with a fixed size var rgba_16x24 = regl.renderbuffer(16, 24) ``` | Property | Interpretation | Default | | ---------- | --------------------------------------------------------- | --------- | | `'format'` | Sets the internal format of the render buffer (see below) | `'rgba4'` | | `'width'` | Sets the width of the render buffer in pixels | `1` | | `'height'` | Sets the height of the render buffer in pixels | `1` | | `'shape'` | Alias for width and height | `[1,1]` | | `'radius'` | Simultaneously sets width and height | `1` | | Format | Description | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `'rgba4'` | `gl.RGBA4` | | `'rgb565'` | `gl.RGB565` | | `'rgb5 a1'` | `gl.RGB5_A1` | | `'depth'` | `gl.DEPTH_COMPONENT16` | | `'stencil'` | `gl.STENCIL_INDEX8` | | `'depth stencil'` | `gl.DEPTH_STENCIL` | | `'srgba'` | `ext.SRGB8_ALPHA8_EXT`, only if [EXT_sRGB](https://www.khronos.org/registry/webgl/extensions/EXT_sRGB/) supported | | `'rgba16f'` | 16 bit floating point RGBA buffer, only if [EXT_color_buffer_half_float](https://www.khronos.org/registry/webgl/extensions/EXT_color_buffer_half_float/) | | `'rgb16f'` | 16 bit floating point RGB buffer, only if [EXT_color_buffer_half_float](https://www.khronos.org/registry/webgl/extensions/EXT_color_buffer_half_float/) | | `'rgba32f'` | 32 bit floating point RGBA buffer, only if [WEBGL_color_buffer_float](https://www.khronos.org/registry/webgl/extensions/WEBGL_color_buffer_float/) supported | **Relevant WebGL APIs** - [`gl.createRenderbuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCreateRenderbuffer.xml) - [`gl.deleteRenderbuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteRenderbuffer.xml) - [`gl.renderbufferStorage`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glRenderbufferStorage.xml) - [`gl.bindRenderbuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBindRenderbuffer.xml) #### Renderbuffer update Like all other resources, renderbuffers can be updated in place: ```javascript var renderbuffer = regl.renderbuffer() renderbuffer({ radius: 3, format: 'depth' }) ``` ##### Renderbuffer resize A renderbuffer can also be resized in place by calling `.resize()`: ```javascript var renderbuffer = regl.renderbuffer({ shape: [10, 10], format: 'depth stencil' }) renderbuffer.resize(32, 32) ``` #### Renderbuffer properties The following properties contains information about the renderbuffer. | Property | Description | | -------- | -------------------------- | | `width` | Width of the renderbuffer | | `height` | Height of the renderbuffer | | `format` | Format of the renderbuffer | They can be accessed after renderbuffer creation like this: ```javascript var r = regl.renderbuffer({shape: [1, 1], format: 'rgb5 a1' }) console.log('renderbuffer: ', r.width, r.height, r.format) ``` #### Renderbuffers destructor ```javascript rb.destroy() ``` **Relevant WebGL APIs** - [`gl.deleteRenderbuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteRenderbuffer.xml) #### Renderbuffer profiling The following stats are tracked for each renderbuffer in the `.stats` property: | Statistic | Meaning | | --------- | ------------------------------------- | | `size` | The size of the renderbuffer in bytes | * * * ### Framebuffers #### Framebuffer constructor ```javascript // Creating a simple 2x2 framebuffer: var fbo2x2 = regl.framebuffer(2) // A 256x256 framebuffer without a stencil attachment var fbo = regl.framebuffer({ width: 256, height: 256, stencil: false }) // A framebuffer with a color buffer var texture = regl.texture(16) var texFBO = regl.framebuffer({ color: texture }) ``` | Property | Description | Default | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | | `shape` | Sets the dimensions [width, height] for the framebuffer. | | | `radius` | Sets the dimensions `radius` x `radius` for the framebuffer. | | | `width` | Sets the width of the framebuffer | `gl.drawingBufferWidth` | | `height` | Sets the height of the framebuffer | `gl.drawingBufferHeight` | | `color`/`colors` | A texture or renderbuffer (or an array of these) for the color attachment. | | | `depth` | If boolean, toggles the depth attachment. If a renderbuffer or texture, sets the depth attachment. | `true` | | `stencil` | If boolean, toggles the stencil attachment. If a renderbuffer or texture, sets the stencil attachment. | `true` | | `depthStencil` | If boolean, toggles both the depth and stencil attachments. If a renderbuffer or texture, sets the combined depth/stencil attachment. | `true` | | `colorFormat` | Sets the format of the color buffer. Ignored if `color` is specified. | `'rgba'` | | `colorType` | Sets the type of the color buffer if it is a texture. | `'uint8'` | | `colorCount` | Sets the number of color buffers. Values > 1 require [WEBGL_draw_buffers](https://www.khronos.org/registry/webgl/extensions/WEBGL_draw_buffers/) | `1` | | `depthTexture` | Toggles whether depth/stencil attachments should be in texture. Requires [WEBGL_depth_texture](https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) | `false` | | Color format | Description | Attachment | Notes | | ------------ | ----------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------- | | `'rgba'` | `gl.RGBA` | Texture | | | `'rgba4'` | `gl.RGBA4` | Renderbuffer | | | `'rgb565'` | `gl.RGB565` | Renderbuffer | | | `'rgb5 a1'` | `gl.RGB5_A1` | Renderbuffer | | | `'rgb16f'` | `gl.RGB16F` | Renderbuffer | only if [EXT_color_buffer_half_float](https://www.khronos.org/registry/webgl/extensions/EXT_color_buffer_half_float/) | | `'rgba16f'` | `gl.RGBA16F` | Renderbuffer | only if [EXT_color_buffer_half_float](https://www.khronos.org/registry/webgl/extensions/EXT_color_buffer_half_float/) | | `'rgba32f'` | `gl.RGBA32F` | Renderbuffer | only if [WEBGL_color_buffer_float](https://www.khronos.org/registry/webgl/extensions/WEBGL_color_buffer_float/) supported | | `'srgba'` | `gl.SRGB8_ALPHA8` | Renderbuffer | only if [EXT_sRGB](https://www.khronos.org/registry/webgl/extensions/EXT_sRGB/) supported | | Color type | Description | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | `'uint8'` | `gl.UNSIGNED_BYTE` | | `'half float'` | `ext.HALF_FLOAT_OES` (16-bit float), requires [OES_texture_half_float](https://www.khronos.org/registry/webgl/extensions/OES_texture_half_float/) | | `'float'` | `gl.FLOAT` (32-bit float), requires [OES_texture_float](https://www.khronos.org/registry/webgl/extensions/OES_texture_float/) | **Notes** - If neither `color` nor `colors` is specified, then color attachments are created automatically. - `shape`, `radius`, and `width`/`height` are alternative (and mutually exclusive) means for setting the size of the framebuffer. **Relevant WebGL APIs** - [`gl.createFramebuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCreateFramebuffer.xml) - [`gl.deleteFramebuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteFramebuffer.xml) - [`gl.framebufferRenderbuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glFramebufferRenderbuffer.xml) - [`gl.framebufferTexture2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glFramebufferTexture2D.xml) - [`gl.bindFramebuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBindFramebuffer.xml) #### Framebuffer update Like all other objects, a framebuffer can be updated in place: ```javascript var framebuffer = regl.framebuffer(3, 4) framebuffer({ shape: [8, 10], depth: false }) ``` ##### Framebuffer binding For convenience it is possible to bind a framebuffer directly. This is a short cut for creating a command which sets the framebuffer: ```javascript var framebuffer = regl.framebuffer(5) framebuffer.use(function () { // now we can draw to the framebuffer }) // // This is the same as doing the following: // var setFBO = regl({ framebuffer: framebuffer }) setFBO(function () { // .. same situation as above }) ``` ##### Framebuffer resize Framebuffers can be resized using the `.resize()` method. This method will also modify all of the framebuffer's attachments. ```javascript var framebuffer = regl.framebuffer(20, 4) framebuffer.resize(3, 3) // set both width and height to 3. framebuffer.resize(3) ``` #### Framebuffer destructor Calling `.destroy()` on a framebuffer removes it and recursively destroys any non-shared attachments. ```javascript fbo.destroy() ``` **Relevant WebGL APIs** - [`gl.deleteFramebuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteFramebuffer.xml) * * * ### Cubic frame buffers #### Cube framebuffer constructor ```javascript var cubeFbo = regl.framebufferCube(512) var cubeAlt = regl.framebufferCube({ radius: 32, color: regl.cube(32), depth: false, stencil: false }) ``` | Property | Description | Default | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | | `shape` | Sets the dimensions [width, height] for each face of the cube. Width must equal height. | | | `radius` | Sets the dimensions `radius` x `radius` for each face of the cube. | | | `width` | Sets the width dimension for each face of the cube. Must equal `height`. | `gl.drawingBufferWidth` | | `height` | Sets the height dimension for each face of the cube. Must equal `width`. | `gl.drawingBufferHeight` | | `color`/`colors` | A TextureCube or array of TextureCubes for the color attachment. | | | `depth` | If boolean, toggles the depth attachment. If texture, sets the depth attachment. | `true` | | `stencil` | If boolean, toggles the stencil attachment. If texture, sets the stencil attachment. | `true` | | `depthStencil` | If boolean, toggles both the depth and stencil attachments. If texture, sets the combined depth/stencil attachment. | `true` | | `colorFormat` | Sets the format of the color buffer. Ignored if `color` is specified. | `'rgba'` | | `colorType` | Sets the type of the color buffer. | `'uint8'` | | `colorCount` | Sets the number of color buffers. Values > 1 require [WEBGL_draw_buffers](https://www.khronos.org/registry/webgl/extensions/WEBGL_draw_buffers/) | `1` | | `depthTexture` | Toggles whether depth/stencil attachments should be in texture. Requires [WEBGL_depth_texture](https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) | `false` | | Color format | Description | Attachment | | ------------ | ----------- | ---------- | | `'rgba'` | `gl.RGBA` | Texture | | Color type | Description | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | `'uint8'` | `gl.UNSIGNED_BYTE` | | `'half float'` | `ext.HALF_FLOAT_OES` (16-bit float), requires [OES_texture_half_float](https://www.khronos.org/registry/webgl/extensions/OES_texture_half_float/) | | `'float'` | `gl.FLOAT` (32-bit float), requires [OES_texture_float](https://www.khronos.org/registry/webgl/extensions/OES_texture_float/) | **Notes** - The specified depth/stencil/depth-stencil attachment will be reused for all 6 cube faces. - `shape`, `radius`, and `width`/`height` are alternative (and mutually exclusive) means for setting the size of the framebuffer. #### Cube framebuffer update ```javascript // reinitialize fboCube({ radius: 10 }) ``` ##### Cube framebuffer resize ```javascript fboCube.resize(16) ``` #### Cube framebuffer destructor ```javascript fboCube.destroy() ``` * * * ## Other tasks Other than draw commands and resources, there are a few miscellaneous parts of the WebGL API which REGL wraps for completeness. * * * ### Clear the draw buffer `regl.clear` combines `gl.clearColor, gl.clearDepth, gl.clearStencil` and `gl.clear` into a single procedure, which has the following usage: ```javascript regl.clear({ color: [0, 0, 0, 1], depth: 1, stencil: 0 }) ``` | Property | Description | | ------------- | ------------------------------------------------------------------------------------------ | | `color` | Sets the clear color | | `depth` | Sets the clear depth value | | `stencil` | Sets the clear stencil value | | `framebuffer` | Sets the target framebuffer to clear (if unspecified, uses the current framebuffer object) | If an option is not present, then the corresponding buffer is not cleared **Relevant WebGL APIs** - [`gl.clearColor`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glClearColor.xml) - [`gl.clearDepth`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glClearDepth.xml) - [`gl.clearStencil`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glClearStencil.xml) - [`gl.clear`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glClear.xml) * * * ### Reading pixels ```javascript // read entire screen var snapshot = regl.read() // Can also reuse a buffer by passing it to regl.read() var bytes = new Uint8Array(100) regl.read(bytes) // It is also possible to specify a region to read from var pixels = regl.read({ x: 2, y: 3, width: 3, height: 1, data: new Uint8Array(12) }) // You can also read from the currently bound fbo. // Note that `pixels` will be of type `Float32Array` // in this case. fbo = regl.framebuffer({ width: W, height: H, colorFormat: 'rgba', colorType: 'float' }) regl({framebuffer: fbo})(() => { regl.clear({color: [0.5, 0.25, 0.5, 0.25]}) var pixels = regl.read() }) ``` | Property | Description | Default | | ------------- | ------------------------------------------------------------------------- | ------------------------------- | | `data` | An optional `ArrayBufferView` which gets the result of reading the pixels | `null` | | `x` | The x-offset of the upper-left corner of the rectangle in pixels | `0` | | `y` | The y-offset of the upper-left corner of the rectangle in pixels | `0` | | `width` | The width of the rectangle in pixels | Current framebuffer width | | `height` | The height of the rectangle in pixels | Current framebuffer height | | `framebuffer` | Sets the framebuffer to read pixels from | The currently bound framebuffer | **Notes** - In order to read pixels from the drawing buffer, you must create your webgl context with `preserveDrawingBuffer` set to `true`. If this is not set, then `regl.read` will throw an exception. - You can only read pixels from a framebuffer of type `'uint8'` or `'float'`. Furthermore, it is not possible to read from a renderbuffer. **Relevant WebGL APIs** - [`gl.pixelStorei`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) - [`gl.readPixels`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glReadPixels.xml) * * * ### Per-frame callbacks `regl` also provides a common wrapper over `requestAnimationFrame` and `cancelAnimationFrame` that integrates gracefully with context loss events. `regl.frame()` also calls `gl.flush` and drains several internal buffers, so you should try to do all your rendering to the drawing buffer within the frame callback. ```javascript // Hook a callback to run each frame var tick = regl.frame(function (context) { // context is the default state of the regl context variables // ... }) // When we are done, we can unsubscribe by calling cancel on the callback tick.cancel() ``` It is possible to manage framecallbacks manually, however before any loop it is essential to call `regl.poll()` which updates all timers and viewports. * * * ### Extensions In `regl`, extensions must be declared before they can be used. An extension may be specified as a 'hard' requirement, meaning that if it is not present then context creation fails or as a 'soft' requirement. This can be done by passing a list of extensions to the `extensions` and `optionalExtensions` fields in the regl constructor respectively. ```javascript require('regl')({ extensions: ['OES_texture_float'], optionalExtensions: ['oes_texture_float_linear'], onDone: function (err, regl) { if (err) { console.log(err) return } // now we can use regl as usual if (regl.hasExtension('oes_texture_float_linear')) { // can use texture float linear } } }) ``` The method `regl.hasExtension()` can be used to test if an extension is present. Arguments to `regl.hasExtension()` are case insensitive. For more information on WebGL extensions, see the [WebGL extension registry](https://www.khronos.org/registry/webgl/extensions/). **Relevant WebGL APIs** - [WebGL Extension Registry](https://www.khronos.org/registry/webgl/extensions/) - `gl.getExtension` - `gl.getSupportedExtensions` * * * ### Device capabilities and limits regl exposes info about the WebGL context limits and capabilities via the `regl.limits` object. The following properties are supported, | Property | Description | | ------------------------- | ------------------------------------------------------------------- | | `colorBits` | An array of bits depths for the red, green, blue and alpha channels | | `depthBits` | Bit depth of drawing buffer | | `stencilBits` | Bit depth of stencil buffer | | `subpixelBits` | `gl.SUBPIXEL_BITS` | | `extensions` | A list of all supported extensions | | `maxAnisotropic` | Maximum number of anisotropic filtering samples | | `maxDrawbuffers` | Maximum number of draw buffers | | `maxColorAttachments` | Maximum number of color attachments | | `pointSizeDims` | `gl.ALIASED_POINT_SIZE_RANGE` | | `lineWidthDims` | `gl.ALIASED_LINE_WIDTH_RANGE` | | `maxViewportDims` | `gl.MAX_VIEWPORT_DIMS` | | `maxCombinedTextureUnits` | `gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS` | | `maxCubeMapSize` | `gl.MAX_CUBE_MAP_TEXTURE_SIZE` | | `maxRenderbufferSize` | `gl.MAX_RENDERBUFFER_SIZE` | | `maxTextureUnits` | `gl.MAX_TEXTURE_IMAGE_UNITS` | | `maxTextureSize` | `gl.MAX_TEXTURE_SIZE` | | `maxAttributes` | `gl.MAX_VERTEX_ATTRIBS` | | `maxVertexUniforms` | `gl.MAX_VERTEX_UNIFORM_VECTORS` | | `maxVertexTextureUnits` | `gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS` | | `maxVaryingVectors` | `gl.MAX_VARYING_VECTORS` | | `maxFragmentUniforms` | `gl.MAX_FRAGMENT_UNIFORM_VECTORS` | | `glsl` | `gl.SHADING_LANGUAGE_VERSION` | | `renderer` | `gl.RENDERER` | | `vendor` | `gl.VENDOR` | | `version` | `gl.VERSION` | | `textureFormats` | A list of all supported texture formats | | `readFloat` | If reading float numbers is supported | | `npotTextureCube` | If non power of two cube texture dimensions are supported | **Relevant WebGL APIs** - [`gl.getParameter`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGetParameter.xml) * * * ### Performance metrics `regl` tracks several metrics for performance monitoring. These can be read using the `regl.stats` object: | Metric | Meaning | | ---------------------------- | -------------------------------------------------------------------------- | | `bufferCount` | The number of array buffers currently allocated | | `elementsCount` | The number of element buffers currently allocated | | `framebufferCount` | The number of framebuffers currently allocated | | `shaderCount` | The number of shaders currently allocated | | `textureCount` | The number of textures currently allocated | | `cubeCount` | The number of cube maps currently allocated | | `renderbufferCount` | The number of renderbuffers currently allocated | | `maxTextureUnits` | The maximum number of texture units used | | `getTotalTextureSize()` | The total amount of memory allocated for textures and cube maps | | `getTotalBufferSize()` | The total amount of memory allocated for array buffers and element buffers | | `getTotalRenderbufferSize()` | The total amount of memory allocated for renderbuffers | | `getMaxUniformsCount()` | The maximum number of uniforms in any shader | | `getMaxAttributesCount()` | The maximum number of attributes in any shader | **Notes** - The functions `getTotalTextureSize`, `getTotalBufferSize`, `getTotalRenderbufferSize`, `getMaxUniformsCount`, and `getMaxAttributesCount` are only available if `regl` is initialized with the option `profile: true`. * * * ### Clocks and timers It may be desirable to synchronize external events (like button presses or mouse movements) with the internal timer in regl. To sample the current time stamp outside of the frame callback you can use the following command: ```javascript // Samples current timestamp of regl's local clock regl.now() ``` * * * ### Clean up When a `regl` context is no longer needed, it can be destroyed releasing all associated resources with the following command: ```javascript regl.destroy() ``` * * * ### Context loss `regl` makes a best faith effort to handle context loss by default. This means that buffers and textures are reinitialized on a context restore with their contents. This can be done using the context loss events exposed by `regl`. For example: ```javascript var regl = require('regl')() regl.on('lost', function () { console.log('lost webgl context') }) regl.on('restore', function () { console.log('webgl context restored') }) ``` * * * ### Unsafe escape hatch **WARNING**: `regl` is designed in such a way that you should never have to directly access the underlying WebGL context. However, if you really absolutely need to do this for some reason (for example to interface with an external library), you can still get a reference to the WebGL context. Note though that if you do this you will need to restore the `regl` state in order to prevent rendering errors. This can be done with the following unsafe methods: ```javascript // This retrieves a reference to the underlying WebGL context (don't do this!) var gl = regl._gl // ... do some crazy direct manipulation here // now restore the regl state regl._refresh() // Resume using regl as normal ``` Note that you must call `regl._refresh()` if you have changed the WebGL state. * * * ## Tips The following are some random tips for writing WebGL programs. Some are regl specific and some are more generic. ### Reuse commands Creating commands in `regl` is expensive because `regl` does many complex optimizations up front in order to ensure the best possible performance. As a result, it is expected that users should declare commands once and then call them many times. For example: ```javascript // Good usage: var command = regl({ vert: `...`, frag: `...` }) regl.frame(() => { command() }) ``` **Do not generate a command in your frame loop**: ```javascript // BAD! Do not do this! regl.frame(() => { // This creates a new command object and executes it each frame. // It will be very slow. regl({ vert: `...`, frag: `...` })() }) ``` ### Reuse resources (buffers, elements, textures, etc.) Similarly, you should reuse buffers and textures wherever possible. If you are continually uploading data to the GPU you should reuse whatever buffers or textures you can. For example, suppose you want to play a video. Then it is better to reuse the buffer as follows: ```javascript // Get a reference to the video element const myVideo = document.querySelector('video') // Create a video texture const videoTexture = regl.texture(myVideo) regl.frame(() => { // Update the frames of the video videoTexture.subimage(myVideo) }) ``` For dynamic buffers or elements, remember to allocate them using `stream` or `dynamic` usage. ### Preallocate memory The most common cause of jank in JavaScript applications is garbage collection. In general, the only way to avoid this is to not allocate temporary objects. To avoid this in `regl` you can reuse parameter objects which are passed to commands and preallocate arrays/matrices. ### Removing assertions By default, `regl` is compiled with a number of assertions, checks and validations to make it easier to find and fix errors. However these assertions will increase your code size and in some cases may slightly slow things down. Fortunately, they can be removed with the help of a transform in `bin/remove-check`. ### Profiling tips If your application is running too slow and you want to understand what is going on, regl provides many hooks which you can use to monitor and [debug your performance](https://github.com/regl-project/regl/blob/gh-pages/API.md#profiling). ### Context loss mitigation A WebGL application must be prepared to lose context at any time. This is an unfortunate part of life when working on the web. If this happens `regl` will make a best faith effort to recover functionality after the context is restored, however it is still up to the user to handle this situation. ### Use batch mode If you want to draw a bunch of copies of the same object, only with different properties, be sure to use [batch mode](https://github.com/regl-project/regl/blob/gh-pages/API.md#batch-rendering). Commands rendered in batch mode can be optimized by avoiding certain state checks which are required for serial commands.