@plotly/regl
Version:
regl is a fast functional WebGL framework.
1,641 lines (1,197 loc) • 115 kB
Markdown
# 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` \* |
| `primi