2.10.4 • Published 11 months ago

pex-context v2.10.4

Weekly downloads
111
License
MIT
Repository
github
Last release
11 months ago

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 = require('pex-context')
const createCube = require('primitive-cube')
const mat4 = require('pex-math/mat4')

const ctx = createContext({ width: 640, height: 480 })
const cube = createCube()

const clearCmd = {
  pass: ctx.pass({
    clearColor: [0, 0, 0, 1],
    clearDepth: 1
  })
}

const drawCmd = {
  pass: ctx.pass({
    clearColor: [0.2, 0.2, 0.2, 1],
    clearDepth: 1
  }),
  pipeline: ctx.pipeline({
    depthTest: true,
    vert: `
      attribute vec3 aPosition;
      attribute vec3 aNormal;
      uniform mat4 uProjectionMatrix;
      uniform mat4 uViewMatrix;
      varying vec3 vNormal;
      void main () {
        gl_Position = uProjectionMatrix * uViewMatrix * vec4(aPosition, 1.0);
        vNormal = aNormal;
      }
    `,
    frag: `
      precision mediump float;
      varying vec3 vNormal;
      void main () {
        gl_FragColor.rgb = vNormal;
        gl_FragColor.a = 1.0;
      }
    `
  }),
  attributes: {
    aPosition: ctx.vertexBuffer(cube.positions),
    aNormal: ctx.vertexBuffer(cube.normals)
  },
  indices: ctx.indexBuffer(cube.cells),
  uniforms: {
    uProjectionMatrix: mat4.perspective(mat4.create(), Math.PI / 4, 640 / 480, 0.1, 100),
    uViewMatrix: mat4.lookAt(mat4.create(), [2, 2, 5], [0, 0, 0], [0, 1, 0])
  }
}

ctx.frame(() => {
  ctx.submit(clearCmd)
  ctx.submit(drawCmd)
})

Code Examples

To run e.g. shadow mapping example

cd examples
budo shadows.js --open --live -- -t glslify

API

Context

Creating gl context wrapper.

ctx = createContext(opts)

var createContext = require('pex-context')

// full window canvas
var ctx = createContext()

// creates gl context from existing canvas and keeps it's size
var ctx = createContext({ gl: gl })

// creates gl context from existing canvas and keeps it's size
var ctx = createContext({ canvas: canvas })

// creates new canvas with given width and height
var ctx = createContext({ width: Number, height: Number })

ctx.set(opts)

ctx.set({
  pixelRatio: 2,
  width: 1280,
  height: 720
})
propertyinfodefault
pixelRatiocanvas resolution, can't be bigger than window.devicePixelRatio1
widthcanvas width-
heightcanvas 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.addEventListener('resize', () => {
  ctx.set({
    width: window.innerWidth,
    height: window.innerWidth
  })
})

Render Loop

ctx.frame(cb)

  • cb: Function - Request Animation Frame callback

Commands

Commands are plain JavaScript objects with GPU resources needed to complete a draw call

var 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]
}
propertyinfotype
passrender pass infoctx.Pass
pipelinerendering pipeline infoctx.Pipeline
attributesvertex attributesmap of :
attibuteName: ctx.VertexBuffer
attributeName: { buffer: VertexBuffer, offset: Number, stride: Number, divisor: Number }
indicesindiceseither:
ctx.IndexBuffer
{ buffer: IndexBuffer, offset: Number, stride: Number }
countnumber of vertices to drawInteger
instancesnumber instances to drawInteger
uniformsshader uniformsmap of name: value
viewportdrawing viewport boundsx, y, w, h
scissorscissor test boundsx, 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.submit({
  pass: ctx.pass({
    clearColor: [1, 0, 0, 1]
  }),
  pipeline: ctx.pipeline({
    vert: '...',
    frag: '...'
  }),
  attributes: {...},
  indices: indexBuffer,
  uniforms: {...},
  ...
})

ctx.submit(cmd, opts)

Submit partially updated command without modifying the original one

// E.g. draw mesh with custom color
ctx.submit(cmd, {
  uniforms: {
    uColor: [1, 0, 0, 0]
  }
})

ctx.submit(cmd, opts1, opts2, opts3...)

Submit a batch of commands differences in opts.

// E.g. draw same mesh twice with different material and position
ctx.submit(cmd, [
  { pipeline: material1, uniforms: { uModelMatrix: position1 },
  { pipeline: material2, uniforms: { uModelMatrix: position2 }
])

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 texture
ctx.submit(renderToFboCmd, () => {
  ctx.submit(drawMeshCmd)
})

Resources

All resources are plain JavaScript 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.

var tex = ctx.texture2D({
  width: 256,
  pixelFormat: ctx.PixelFormat.RGBA8
})

tex.width //256
tex.pixelFormat //'rgba8'

//but also those properties has been added
tex.type //gl.UNSIGNED_BYTE
tex.internalFormat //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)

var pass = ctx.pass({
  color: [Texture2D, ...]
  color: [{ texture: Texture2D | TextureCube, target: CubemapFace }, ...]
  depth: Texture2D
  clearColor: Array,
  clearDepth: Number
})
propertyinfotypedefault
colorcolor render targetArray of Texture2D or { texture, target} pairsnull
depthdepth render targetTexture2Dnull
clearColorclear color valueArraynull
clearDepthclear depth valueNumbernull

Pipeline

Pipelines represent the state of the GPU rendering pipeline (shaders, blending, depth test etc).

pipeline = ctx.pipeline(opts)

var pipeline = ctx.pipeline({
  vert: String,
  frag: String,
  depthWrite: Boolean,
  depthTest: Boolean,
  depthFunc: DepthFunc,
  blend: Boolean,
  blendSrcRGBFactor: BlendFactor,
  blendSrcAlphaFactor: BlendFactor,
  blendDstRGBFactor: BlendFactor,
  blendDstAlphaFactor: BlendFactor,
  cullFace: Boolean,
  cullFaceMode: Face,
  colorMask: Array,
  primitive: Primitive
})
propertyinfotypedefault
vertvertex shader codeStringnull
fragfragment shader codeStringnull
depthWritedepth write maskBooleantrue
depthTestdepth test on/offBooleanfalse
depthFuncdepth test functionDepthFuncLessEqual
blendblending on/offBooleanfalse
blendSrcRGBFactorblending source color factorBlendFactorOne
blendSrcAlphaFactorblending source alpha factorBlendFactorOne
blendDstRGBFactorblending destination color factorBlendFactorOne
blendDstAlphaFactorblending destination alpha factorBlendFactorOne
cullFaceface culling on/offBooleanfalse
cullFaceModeface culling modeFaceBack
colorMaskcolor write mask for r, g, b, aArray of Booleantrue, true, true, true
primitivegeometry primitivePrimitiveTriangles

Texture

Textures represent pixel data uploaded to the GPU.

texture = ctx.texture2D(opts)

var tex = ctx.texture2D({
  data: [255, 255, 255, 255, 0, 0, 0, 255],
  width: 2,
  height: 1,
  pixelFormat: ctx.PixelFormat.RGB8,
  encoding: ctx.Encoding.Linear,
  wrap: ctx.Wrap.Repeat
})
propertyinfotypedefault
datapixel dataArray, Uint8Array, Float32Array, HTMLCanvas, HTMLImage, HTMLVideonull
widthtexture widthNumber/Int0
heighttexture heightNumber/Int0
pixelFormatpixel data formatctx.PixelFormatctx.PixelFormat.RGB8
encodingpixel data encodingctx.Encodingctx.Encoding.Linear
wrapSwrapS modectx.Wrapctx.Wrap.ClampToEdge
wrapTwrapT modectx.Wrapctx.Wrap.ClampToEdge
wrapcombines wrapS and wrapTctx.Wrapctx.Wrap.ClampToEdge
minmin filtering modectx.Filterctx.Filter.Nearest
magmag filtering modectx.Filterctx.Filter.Nearest
anisoaniso level 1Number/Int0
mipmapgenerate mipmaps on update 2Booleanfalse
flipYflip pixel data on uploadBooleanfalse
nametexture name for debuggingString''
targettexture target 3gl enumgl.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 - see ctx.texture2D(opts)
  • opts.data: Array of Images or TypedArrays - 6 images, one for each face +X, -X, +Y, -Y, +Z, -Z
var tex = ctx.textureCube({
  data: [ posx, negx, posy, negy, posz, negz ],
  width: 64,
  height: 64
])

Renderbuffer

Renderbuffers represent pixel data store for rendering operations

renderbuffer = ctx.renderbuffer(opts)

var tex = ctx.renderbuffer({
  width: 1280,
  height: 720,
  pixelFormat: ctx.PixelFormat.Depth16
})
propertyinfotypedefault
widthrenderbuffer widthNumber/Int0
heightrenderbuffer heightNumber/Int0
pixelFormatpixel data format1ctx.PixelFormatnull

1 only PixelFormat.Depth16 is currently supported and only 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)

var buf = ctx.vertexBuffer({ data: Array }) // aka Attribute Buffer

var buf = ctx.indexBuffer({ data: Array }) // aka Index Buffer
propertyinfotypedefault
datapixel dataArray, Uint8Array, Float32Arraynull
typedata typectx.DataTypectx.DataType.Float32
usagebuffer usagectx.Usagectx.Usage.StaticDraw

Query

Queries are used for GPU timers.

query = ctx.query(opts)

Note: Requires EXT_disjoint_timer_query

var query = ctx.query({
  target: QueryTarget
})
propertyinfotypedefault
targetquery typectx.QueryTargetctx.QueryTarget.TimeElapsed
statequery statectx.QueryStatectx.QueryState.Ready
resultresult of the measurementNumbernull

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.update(res, { data: [] })

var tex = ctx.texture2D({...})
ctx.update(tex, {
  width: 1,
  height: 1,
  data: new Uint8Array([255, 0, 0, 255])
})
propertyinfo
optswhatever data the given resource accepts in constructor

Disposing resources

ctx.dispose()

Delete all allocated resources and stop render loop. Disposed context is no longer valid to use.

ctx.dispose()

ctx.dispose(res)

Delete a resource. Disposed resource is no longer valid for use.

var tex = ctx.texture2D({})
...
ctx.dispose(tex)
propertyinfotype
targetresource to be deletedctx.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

Enums

ctx.BlendFactor

  const BlendFactor = {
    One: gl.ONE,
    Zero: gl.ZERO,
    SrcAlpha: gl.SRC_ALPHA,
    OneMinusSrcAlpha: gl.ONE_MINUS_SRC_ALPHA,
    DstAlpha: gl.DST_ALPHA,
    OneMinusDstAlpha: gl.ONE_MINUS_DST_ALPHA,
    SrcColor: gl.SRC_COLOR,
    OneMinusSrcColor: gl.ONE_MINUS_SRC_COLOR,
    DstColor: gl.DST_COLOR,
    OneMinusDstColor: gl.ONE_MINUS_DST_COLOR
  }

ctx.CubemapFace

  const CubemapFace = {
    PositiveX: gl.TEXTURE_CUBE_MAP_POSITIVE_X,
    NegativeX: gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
    PositiveY: gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
    NegativeY: gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
    PositiveZ: gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
    NegativeZ: gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
  }

ctx.DataType

  const DataType = {
    Float32: gl.FLOAT,
    Uint8: gl.UNSIGNED_BYTE,
    Uint16: gl.UNSIGNED_SHORT,
    Uint32: gl.UNSIGNED_INT
  }

ctx.DepthFunc

  const DepthFunc = {
    Never: gl.NEVER,
    Less: gl.LESS,
    Equal: gl.EQUAL,
    LessEqual: gl.LEQUAL,
    Greater: gl.GREATER,
    NotEqual: gl.NOTEQUAL,
    GreaterEqual: gl.GEQUAL,
    Always: gl.ALWAYS
  }

ctx.Face

  const Face = {
    Front: gl.FRONT,
    Back: gl.BACK,
    FrontAndBack: gl.FRONT_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
    Depth16: 'depth16' // gl.DEPTH_COMPONENT16, renderbuffer only
  }

ctx.Primitive

  const Primitive = {
    Points: gl.POINTS,
    Lines: gl.LINES,
    LineStrip: gl.LINE_STRIP,
    Triangles: gl.TRIANGLES,
    TriangleStrip: gl.TRIANGLE_STRIP
  }

ctx.Usage

  const Usage = {
    StaticDraw: gl.STATIC_DRAW,
    DynamicDraw: gl.DYNAMIC_DRAW,
    StreamDraw: gl.STREAM_DRAW
  }

ctx.Wrap

  const Wrap = {
    ClampToEdge: gl.CLAMP_TO_EDGE,
    Repeat: gl.REPEAT
  }

ctx.QueryTarget

  const QueryTarget = {
    TimeElapsed: gl.TIME_ELAPSED
  }

ctx.QueryState

  const QueryState = {
    Ready: 'ready',
    Active: 'active',
    Pending: 'pending'
  }
3.0.0-alpha.8

11 months ago

3.0.0-alpha.7

1 year ago

3.0.0-alpha.6

1 year ago

3.0.0-alpha.5

1 year ago

3.0.0-alpha.4

1 year ago

3.0.0-alpha.3

1 year ago

3.0.0-alpha.1

2 years ago

3.0.0-alpha.2

2 years ago

3.0.0-alpha.0

2 years ago

2.11.0-2

2 years ago

2.11.0-0

3 years ago

2.11.0-1

3 years ago

2.10.4

4 years ago

2.10.3

4 years ago

2.10.2

4 years ago

2.10.1

5 years ago

2.10.0

5 years ago

2.9.1

5 years ago

2.9.0

5 years ago

2.9.0-0

5 years ago

2.8.0

5 years ago

2.7.2

5 years ago

2.7.1

5 years ago

2.7.0

6 years ago

2.6.0

6 years ago

2.5.2

6 years ago

2.5.1

6 years ago

2.5.0

6 years ago

2.4.0

6 years ago

2.3.0

6 years ago

2.2.0

6 years ago

2.1.2

6 years ago

2.1.1

6 years ago

2.1.0

6 years ago

2.0.3

6 years ago

2.0.2

6 years ago

2.0.1

6 years ago

2.0.0

6 years ago

2.0.0-34

6 years ago

2.0.0-33

7 years ago

2.0.0-32

7 years ago

2.0.0-31

7 years ago

2.0.0-30

7 years ago

2.0.0-29

7 years ago

2.0.0-28

7 years ago

2.0.0-27

7 years ago

2.0.0-26

7 years ago

2.0.0-25

7 years ago

2.0.0-24

7 years ago

2.0.0-23

7 years ago

2.0.0-22

7 years ago

2.0.0-21

7 years ago

2.0.0-20

7 years ago

2.0.0-19

7 years ago

2.0.0-18

7 years ago

2.0.0-17

7 years ago

2.0.0-16

7 years ago

2.0.0-15

7 years ago

2.0.0-14

7 years ago

2.0.0-13

7 years ago

2.0.0-12

7 years ago

2.0.0-11

7 years ago

2.0.0-10

7 years ago

2.0.0-9

7 years ago

2.0.0-8

7 years ago

2.0.0-7

7 years ago

2.0.0-6

7 years ago

2.0.0-5

7 years ago

2.0.0-4

7 years ago

2.0.0-2

7 years ago

2.0.0-1

7 years ago

2.0.0-0

7 years ago

1.3.0

8 years ago

1.2.5

8 years ago

1.2.4

8 years ago

1.2.3

8 years ago

1.2.1

8 years ago

1.2.0

8 years ago

1.1.1

8 years ago

1.1.0

8 years ago

1.0.1

8 years ago

1.0.0

8 years ago

1.0.0-beta.3

8 years ago

1.0.0-beta.2

8 years ago

1.0.0-beta.1

8 years ago