pex-context
Modern WebGL state wrapper for PEX. With pex-context
you allocate GPU resources (textures, buffers), setup state pipelines and passes and combine them together into commands.
Example
const createContext = const createCube = const mat4 = const ctx = const cube = const clearCmd = pass: ctx const drawCmd = pass: ctx pipeline: ctx attributes: aPosition: ctx aNormal: ctx indices: ctx uniforms: uProjectionMatrix: mat4 uViewMatrix: mat4 ctxframe { ctx ctx}
You can find runnable examples in the /examples folder in this repository. To run an example install Node.js, clone or download this repository and then run:
# go to the example folder cd examples # install examples dependencies npm i # run the example in your default browser window npx budo example-name.js --open --start
API
Context
Creating gl context wrapper.
ctx = createContext(opts)
const createContext = // full window canvasconst ctx = // creates gl context from existing canvas and keeps it's sizeconst ctx = // creates gl context from existing canvas and keeps it's sizeconst ctx = // creates new canvas with given width and heightconst ctx =
ctx.set(opts)
ctx
property | info | default |
---|---|---|
pixelRatio |
canvas resolution, can't be bigger than window.devicePixelRatio | 1 |
width |
canvas width | - |
height |
canvas height | - |
Note 1: The new size and resolution will be applied not immediately but before drawing the next frame to avoid flickering.
Note 2: Context's canvas doesn't resize automatically, even if you skip width/height on init and the canvas will be asigned dimensions of the window. To handle resizing use the following code:
window
Render Loop
ctx.frame(cb)
cb
: Function - Request Animation Frame callback
Commands
Commands are plain js objects with GPU resources needed to complete a draw call
const cmd = pass: Pass pipeline: Pipeline attributes: name: VertexBuffer // or name: buffer: VertexBuffer offset: Number stride: Number indices: IndexBuffer // or indices: buffer: IndexBuffer offset: Number count: Number // or count: Number instances: Number uniforms: name: Number name: Array name: Texture2D viewport: 0 0 1920 1080 scissor: 0 0 1920 1080
property | info | type |
---|---|---|
pass |
render pass info | ctx.Pass |
pipeline |
rendering pipeline info | ctx.Pipeline |
attributes |
vertex attributes | map of : |
attibuteName: ctx.VertexBuffer |
||
attributeName: { buffer: VertexBuffer, offset: Number, stride: Number, divisor: Number } |
||
indices |
indices | either: |
ctx.IndexBuffer |
||
{ buffer: IndexBuffer, offset: Number, stride: Number } |
||
count |
number of vertices to draw | Integer |
instances |
number instances to draw | Integer |
uniforms |
shader uniforms | map of name: value |
viewport |
drawing viewport bounds | [x, y, w, h] |
scissor |
scissor test bounds | [x, y, w, h] |
Note: either indices or count need to be specified when drawing geometry Note: scissor region is by default set to null and scissor test disabled
Submitting commands to the GPU
ctx.submit(cmd)
ctx
ctx.submit(cmd, opts)
Submit partially updated command without modifying the original one
// E.g. draw mesh with custom colorctx
ctx.submit(cmd, [opts1, opts2, opts3...])
Submit a batch of commands differences in opts.
// E.g. draw same mesh twice with different material and positionctx
Subcommands
ctx.submit(cmd, cb)
Submit command while preserving state from another command.
This approach allows to simulate state stack with automatic cleanup at the end of callback.
// E.g. render to texturectx
Resources
All resources are plain js object and once constructed their properties can be accessed directly. Please note those props are read only. To set new values or upload new data to GPU see updating resources.
const tex = ctx texwidth //256texpixelFormat //'rgba8' //but also those properties has been addedtextype //gl.UNSIGNED_BYTEtexinternalFormat //gl.RGBA
Pass
Passes are responsible for setting render targets (textures) and their clearing values. FBOs are created internally and automatically by pex-context.
pass = ctx.pass(opts)
const pass = ctx
property | info | type | default |
---|---|---|---|
color |
color render target | Array of Texture2D or { texture, target} pairs | null |
depth |
depth render target | Texture2D | null |
clearColor |
clear color value | Array | null |
clearDepth |
clear depth value | Number | null |
Pipeline
Pipelines represent the state of the GPU rendering pipeline (shaders, blending, depth test etc).
pipeline = ctx.pipeline(opts)
const pipeline = ctx
property | info | type | default |
---|---|---|---|
vert |
vertex shader code | String | null |
frag |
fragment shader code | String | null |
depthWrite |
depth write mask | Boolean | true |
depthTest |
depth test on/off | Boolean | false |
depthFunc |
depth test function | DepthFunc | LessEqual |
blend |
blending on/off | Boolean | false |
blendSrcRGBFactor |
blending source color factor | BlendFactor | One |
blendSrcAlphaFactor |
blending source alpha factor | BlendFactor | One |
blendDstRGBFactor |
blending destination color factor | BlendFactor | One |
blendDstAlphaFactor |
blending destination alpha factor | BlendFactor | One |
cullFace |
face culling on/off | Boolean | false |
cullFaceMode |
face culling mode | Face | Back |
colorMask |
color write mask for [r, g, b, a] | Array of Boolean | [true, true, true, true] |
primitive |
geometry primitive | Primitive | Triangles |
Texture
Textures represent pixel data uploaded to the GPU.
texture = ctx.texture2D(opts)
const tex = ctx
property | info | type | default |
---|---|---|---|
data |
pixel data | Array, Uint8Array, Float32Array, HTMLCanvas, HTMLImage, HTMLVideo | null |
width |
texture width | Number/Int | 0 |
height |
texture height | Number/Int | 0 |
pixelFormat |
pixel data format | ctx.PixelFormat | ctx.PixelFormat.RGB8 |
encoding |
pixel data encoding | ctx.Encoding | ctx.Encoding.Linear |
wrapS |
wrapS mode | ctx.Wrap | ctx.Wrap.ClampToEdge |
wrapT |
wrapT mode | ctx.Wrap | ctx.Wrap.ClampToEdge |
wrap |
combines wrapS and wrapT | ctx.Wrap | ctx.Wrap.ClampToEdge |
min |
min filtering mode | ctx.Filter | ctx.Filter.Nearest |
mag |
mag filtering mode | ctx.Filter | ctx.Filter.Nearest |
aniso |
aniso level 1 | Number/Int | 0 |
mipmap |
generate mipmaps on update 2 | Boolean | false |
flipY |
flip pixel data on upload | Boolean | false |
name |
texture name for debugging | String | '' |
target |
texture target 3 | gl enum | gl.TEXTURE_2D or gl.TEXTURE_CUBE |
1 requries EXT_texture_filter_anisotropic
2 requires min
to be set to ctx.Filter.LinearMipmapLinear
or similar
3 read only
texture = ctx.textureCube(opts)
opts
: Object - seectx.texture2D(opts)
opts.data
: Array of Images or TypedArrays - 6 images, one for each face +X, -X, +Y, -Y, +Z, -Z
const tex = ctx
Renderbuffer
Renderbuffers represent pixel data store for rendering operations
renderbuffer = ctx.renderbuffer(opts)
const tex = ctx
property | info | type | default |
---|---|---|---|
width |
renderbuffer width | Number/Int | 0 |
height |
renderbuffer height | Number/Int | 0 |
pixelFormat |
pixel data format1 | ctx.PixelFormat | null |
1 only PixelFormat.Depth16
equal to gl.DEPTH_COMPONENT16
is currently supported for use as render pass depth storage (e.g. ctx.pass({ depth: renderbuffer})
) for platforms with no WEBGL_depth_texture
support.
Buffer
Buffers store vertex and index data in the GPU memory.
buffer = ctx.vertexBuffer(opts)
buffer = ctx.indexBuffer(opts)
const buf = ctx // aka Attribute Buffer const buf = ctx // aka Index Buffer
property | info | type | default |
---|---|---|---|
data |
pixel data | Array, Uint8Array, Float32Array | null |
type |
data type | ctx.DataType | ctx.DataType.Float32 |
usage |
buffer usage | ctx.Usage | ctx.Usage.StaticDraw |
offset |
data offset in the buffer (update only) | Number/Int | undefined |
Query
Queries are used for GPU timers.
query = ctx.query(opts)
Note: Requires EXT_disjoint_timer_query
const query = ctx
property | info | type | default |
---|---|---|---|
target |
query type | ctx.QueryTarget | ctx.QueryTarget.TimeElapsed |
state |
query state | ctx.QueryState | ctx.QueryState.Ready |
result |
result of the measurement | Number | null |
ctx.beginQuery(q)
Begin the query measurement.
Note: There can be only one query running at the time.
ctx.endQuery(q)
End the query measurement.
Note: The result is not available immediately and will be null
until the state changes from ctx.QueryState.Pending
to ctx.QueryState.Ready
Updating resources
ctx.update(res, opts)
Update a resource.
ctx const tex = ctxctx
property | info | type |
---|---|---|
res |
resource to be updated | ctx.Buffer, ctx.Framebuffer, ctx.Pass, ctx.Pipeline, ctx.Program, ctx.Query, ctx.Texture |
opts |
whatever data the given resource accepts in constructor | Object |
Disposing resources
ctx.dispose()
Delete all allocated resources and stop render loop. Disposed context is no longer valid to use.
ctx
ctx.dispose(res)
Delete a resource. Disposed resource is no longer valid for use.
const tex = ctx...ctx
property | info | type |
---|---|---|
res |
resource to be deleted | ctx.Buffer, ctx.Framebuffer, ctx.Pass, ctx.Pipeline, ctx.Program, ctx.Query, ctx.Texture |
Note: Framebuffers are ref counted and released by Pass, Programs are also ref counted and released by Pipeline
Capabilities
Get capabilities and extensions availability.
const maxTextureSize = ctxmaxTextureSize
property | info | type |
---|---|---|
maxColorAttachments |
gl.getParameter('MAX_COLOR_ATTACHMENTS') or gl.getParameter(ext.MAX_COLOR_ATTACHMENTS_WEBGL) | Number |
maxTextureImageUnits |
gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) | Number |
maxVertexTextureImageUnits |
gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) | Number |
maxTextureSize |
gl.getParameter(gl.MAX_TEXTURE_SIZE) | Number |
maxCubeMapTextureSize |
gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE) | Number |
instancedArrays |
false | Boolean |
instancing |
false (deprecated) | Boolean |
elementIndexUint32 |
!!gl.getExtension('OES_element_index_uint') | Boolean |
standardDerivatives |
!!gl.getExtension('OES_standard_derivatives') | Boolean |
depthTexture |
!!gl.getExtension('WEBGL_depth_texture') | Boolean |
shaderTextureLod |
!!gl.getExtension('EXT_shader_texture_lod') | Boolean |
textureFloat |
!!gl.getExtension('OES_texture_float') | Boolean |
textureFloatLinear |
!!gl.getExtension('OES_texture_float_linear') | Boolean |
textureHalfFloat |
!!gl.getExtension('OES_texture_half_float') | Boolean |
textureHalfFloatLinear |
!!gl.getExtension('OES_texture_half_float_linear') | Boolean |
textureFilterAnisotropic |
!!gl.getExtension('EXT_texture_filter_anisotropic') | Boolean |
Enums
ctx.BlendFactor
const BlendFactor = One: glONE Zero: glZERO SrcAlpha: glSRC_ALPHA OneMinusSrcAlpha: glONE_MINUS_SRC_ALPHA DstAlpha: glDST_ALPHA OneMinusDstAlpha: glONE_MINUS_DST_ALPHA SrcColor: glSRC_COLOR OneMinusSrcColor: glONE_MINUS_SRC_COLOR DstColor: glDST_COLOR OneMinusDstColor: glONE_MINUS_DST_COLOR
ctx.CubemapFace
const CubemapFace = PositiveX: glTEXTURE_CUBE_MAP_POSITIVE_X NegativeX: glTEXTURE_CUBE_MAP_NEGATIVE_X PositiveY: glTEXTURE_CUBE_MAP_POSITIVE_Y NegativeY: glTEXTURE_CUBE_MAP_NEGATIVE_Y PositiveZ: glTEXTURE_CUBE_MAP_POSITIVE_Z NegativeZ: glTEXTURE_CUBE_MAP_NEGATIVE_Z
ctx.DataType
const DataType = Float32: glFLOAT Uint8: glUNSIGNED_BYTE Uint16: glUNSIGNED_SHORT Uint32: glUNSIGNED_INT
ctx.DepthFunc
const DepthFunc = Never: glNEVER Less: glLESS Equal: glEQUAL LessEqual: glLEQUAL Greater: glGREATER NotEqual: glNOTEQUAL GreaterEqual: glGEQUAL Always: glALWAYS
ctx.Face
const Face = Front: glFRONT Back: glBACK FrontAndBack: glFRONT_AND_BACK
ctx.PixelFormat
const PixelFormat = RGBA8: 'rgba8' // gl.RGBA + gl.UNSIGNED_BYTE RGBA32F: 'rgba32f' // gl.RGBA + gl.FLOAT RGBA16F: 'rgba16f' // gl.RGBA + gl.HALF_FLOAT R32F: 'r32f' // gl.ALPHA + gl.FLOAT R16F: 'r16f' // gl.ALPHA + gl.HALF_FLOAT Depth: 'depth' // gl.DEPTH_COMPONENT + gl.UNSIGNED_SHORT Depth16: 'depth16' // gl.DEPTH_COMPONENT + gl.UNSIGNED_SHORT Depth24: 'depth24' // gl.DEPTH_COMPONENT + gl.UNSIGNED_INT
ctx.Primitive
const Primitive = Points: glPOINTS Lines: glLINES LineStrip: glLINE_STRIP Triangles: glTRIANGLES TriangleStrip: glTRIANGLE_STRIP
ctx.Encoding
const Encoding = Linear: 1 Gamma: 2 SRGB: 3 RGBM: 4
ctx.Usage
const Usage = StaticDraw: glSTATIC_DRAW DynamicDraw: glDYNAMIC_DRAW StreamDraw: glSTREAM_DRAW
ctx.Wrap
const Wrap = ClampToEdge: glCLAMP_TO_EDGE Repeat: glREPEAT
ctx.QueryTarget
const QueryTarget = TimeElapsed: glTIME_ELAPSED
ctx.QueryState
const QueryState = Ready: 'ready' Active: 'active' Pending: 'pending'
License
MIT, see LICENSE.md for details.