Skip to content

rreusser/regl-gpu-lines

Repository files navigation

Lines with round joins and caps

regl-gpu-lines

Pure GPU, instanced, screen-projected lines for regl

API documentation →

Live example →

All examples →

This module implements a very general command for drawing lines using the regl WebGL library.

Architecturally this module has two goals:

  • Data may live on the GPU.
  • Minimize unnecessary constraints.

To accomplish this, it implements a simple pragma-based interface for specifying how your attributes connect to line properties and to varyings.

Features:

  • Configure your own attributes, varyings, uniforms, and shaders
  • Compute position and line width in the vertex shader
  • GPU computation of round joins, bevels, and miters (with miter limit), and square and rounded end caps.
  • Optional end cap insertion, using position.w == 0.0 or alternatively position.x NaN to signal a line break (see: docs/multiple.html)
  • Regl-compatible attribute specification with strides and offsets
  • Forward additional regl configuration to the draw command
  • Seamlessly permits substitution of Vertex Array Objects (VAOs)
  • 13.4 KB minified, 5.3 KB gzipped. No dependencies (although it is expected to be used together with regl, which requirement I'd like to consider removing)

Limitations:

  • ANGLE_instanced_arrays extension is required (which ought to be universally supported)
  • Width is best varied slowly as line joins to not take into account varying width
  • Automatic end cap insertion wastes vertices when the cap resolution exceeds the join resolution
  • Line dashes are not built-in, but you can easily build them yourself

Install

The dist/ directory contains UMD bundles, or you may use from a CDN, e.g. https://unpkg.com/regl-gpu-lines@latest. Both expose the module as reglLines.

Otherwise install from npm:

npm install regl-gpu-lines

API

See API documentation.

Examples

  • Basic example: A minimal example. Just a line.
  • Variable width: A basic line with variable width and color
  • Multiple lines: Use position NaN to break up lines into multiple segments
  • Depth: Layer lines using the z-coordinate
  • Closed loop: Create a closed loop by omitting end caps and repeating the first three vertices at the end.
  • Line border: Use lineCoord to draw a SDF line border
  • Interleaved attributes: Instead of a regl buffer, you can pass a regl-style attribute description with buffer, stride, offset, and divisor properties in order to used interleaved, packed attribute data.
  • Torus knot: A torus knot with layering which can be difficult to draw with SVG.
  • Batched rendering: Illustrates how to take advantage of reorder: true to reduce shader program changes from, in this example, thirty to four.
  • Post-projection: Illustrates post-project to draw lines projected onto a plane from some other angle.
  • Vertex Array Object (VAO): Illustrates seamless swapping of VAO for vertex and endpoint attributes.
  • Fake instancing: Sort of allows you to mimic instanced rendering on top of instanced rendering, albeit with some duplication of data.
  • Debug page: Shows how to use instanceID and triStripCoord varyings to draw a wireframe
  • Full debug view: A page for exploring all the bells and whistles
  • GPGPU Strange Attractors: Feed a GPU particle simulation from texture data directly into line rendering

GPGPU Lorenz Attractor

A minimal example looks like the following, where a vec2 attribute xy is connected to line position via a GLSL #pragma.

const drawLines = reglLines(regl, {
  vert: `
    precision highp float;

    #pragma lines: attribute vec2 xy;
    #pragma lines: position = getPosition(xy);
    vec4 getPosition(vec2 xy) { return vec4(xy, 0, 1); }

    #pragma lines: width = getWidth();
    uniform float width;
    float getWidth() { return width; }`,
  frag: `
    precision lowp float;
    void main () {
      gl_FragColor = vec4(1);
    }`,
  uniforms: {
    width: (ctx, props) => props.customWidth * ctx.pixelRatio
  }
});

const xy = [[-1, 1], [-0.5, -1], [0, 1], [0.5, -1], [1, 1]];
const lineData = {
  customWidth: 20,
  join: 'round',
  cap: 'round',
  vertexCount: xy.length,
  vertexAttributes: {
    xy: regl.buffer(xy)
  },
  endpointCount: 2,
  endpointAttributes: {
    xy: regl.buffer([xy.slice(0, 3), xy.slice(-3).reverse()])
  }
};

drawLines(lineData);

Development

Examples

Serve example pages—e.g. the example in examples/closed-loop.js—with

npm start closed-loop

Tests

Run live-reloading render tests with

npm run serve-render-tests

Executing the render tests from the CLI requires the headless-gl module. You may use nodemon to live-run the render tests when the code is changed.

npm install -g nodemon
nodemon test/render.js

Filter tests with

FILTER=miter/basic nodemon test/render.js

and update expectation images with

UPDATE=1 FILTER=miter/basic node test/render.js

You may view the tests, run against the latest version from unpkg.com, at https://rreusser.github.io/regl-gpu-lines/docs/tests.html

See also

License

© 2021 Ricky Reusser. MIT License.