regl-gpu-lines

API

Draw command constructor

import reglLines from 'regl-gpu-lines';

reglLines(regl, {vert, frag, reorder, debug, ...})

Instantiate a drawing command using the specified shaders.

Additional configuration parameters are forwarded to a regl command which wraps drawing.


Vertex shader data flow

The vertex shader is parsed for GLSL #pragma directives which define data flow as well as line properties.


Vertex attributes (at least one required)

Attributes represent per-vertex data. This module internally decides how many times to pass each attribute—between one and four times and each time with a different offset, and depending on its required usage in computing various properties.

#pragma lines: attribute <dataType> <attributeName>

Example

#pragma lines: attribute vec4 position
#pragma lines: attribute vec3 color
#pragma lines: attribute float alpha

Vertex position (required)

#pragma lines: position = <functionName>(<attributeList>)

Example

#pragma lines: attribute vec2 xy
#pragma lines: attribute float z
#pragma lines: position = computePosition(xy, z)

vec4 computePosition(vec2 xy, float z) { return vec4(xy, z, 1); }

Line width (required)

#pragma lines: width = <functionName>(<attributeList>)

Example

#pragma lines: attribute float width
#pragma lines: width = computeWith(width)

float computeWidth(float width) { return width; }

End cap orientation (optional)

#pragma lines: orientation = <functionName>(<attributeList>)

If orientation is not provided, then end caps are rendered in two passes, first starting line caps, then ending line caps. If provided, then all caps are rendered in a single pass. (This complication results from the fact that there’s no mechanism to distinguish the direction of end caps and get the lineCoord varying oriented correctly since GLSL ES 1.00 does not support gl_InstanceID.)

Example

#pragma lines: attribute float orientation
#pragma lines: orientation = getOrientation(orientation)

float getOrientation(float orientation) { return orientation; }

Varyings (optional)

#pragma lines: [extrapolate] varying <type> <name> = <functionName>(<attributeList>)

Example

It may make sense to extrapolate distance along the line, for example, to continue dashes on extrapolated portions of end caps and joins, while it may not make sense to extrapolate colors in such regions.

#pragma lines: attribute vec3 color
#pragma lines: varying vec3 color = getColor(color)
vec3 getColor(vec3 color) { return color; }

#pragma lines: attribute float distance
#pragma lines: extrapolate varying float distance = getDistance(distance)
float getDistance(float distance) { return distance; }

Post-projection

In some cases, you may want to draw lines projected onto a surface rather than the screen. In this case, you may specify a function which receives the outgoing vec4 gl_Position and returns a reprojected vec4 position. In order to accomplish the correct width scale and aspect ratio, you may set viewportSize as a draw property. This size is divided out before post-projection, leaving coordinates in clip space ([-1, 1] × [-1, 1] × [-1, 1]).

#pragma lines: postproject = <functionName>

Example

The following example receives the final gl_Position and applies one more transformation before return—in this case a camera projection-view matrix.

#pragma lines: postproject = project
uniform mat4 projectionView;
vec4 project(vec4 position) {
  return projectionView * position;
}

Fragment shader

You may define the fragment shader as you desire. The only builtin parameter is a varying vec3 called lineCoord, which assists in rendering end caps and variation across the width of the line. lineCoord.xy lives in the square [-1, 1] × [-1, 1]. Starting caps lie in the left half-plane, [-1, 0] × [-1, 1]. The full length of the line lies along a vertical slice [0] × [-1, 1]. End caps lie in the right half-plane, [0, 1] × [-1, 1]. lineCoord.z is zero on line segments and is non-zero on caps and joins. This can help to render caps and joins differently, for example so that caps and joins would not entirely disappear when rendering dashed lines.


Drawing lines

Drawing is invoked by passing an object with the following optional properties to the constructed draw command.

drawLines({...})

Property Type Default Description
join 'miter' 'bevel' 'round' 'miter' Type of joins
cap 'square' 'round' 'none' 'square' Type of end caps
joinResolution number (1-20) 8 Number of triangles used to construct round joins
capResolution number (1-20) 12 Number of triangles used to construct round end caps
insertCaps boolean false Automatically insert caps at line breaks
miterLimit number 4 Maximum extension of miter joins, in multiples of line widths, before they fall back to bevel joins
vertexCount number 0 Total number of line vertices
endpointCount number 0 Number end caps drawn (number of endpoint vertices divided by three)
vertexAttribues object {} Object of named attributes corresponding to those defined the vertex shader
endpointAttributes object {} Object of named attributes corresponding to those defined the vertex shader
viewportSize Array[number] [viewportSize, viewportWidth] Size of screen-projection viewport
vao object null Predefined Vertex Array Object

Example

// Be careful not to instantiate buffers on every draw call
const positions = [[-1, -1], [-0.5, 0.5], [0, -0.5], [0.5, 0.5], [1, -1]];
const xy = regl.buffer(positions);
const endpointXY = regl.buffer([positions.slice(0, 3), positions.slice(-3).reverse()])

drawLines({
  join: 'miter',
  vertexCount: 5,
  vertexAttributes: { xy }
  endpointCount: 2,
  endpointAttributes: { xy: endpointXY }
});

Notes

Vertex Array Objects

Drawing lines involves repeated computation of strides and offsets on every frame. Vertex Array Objects (VAOs) exist to optimize this pathway. To use VAOs, simply extract vertexAttributes and endpointAttributes from render-time line data and move it to a call to drawLines.vao() instead. Then pass the resulting object in the draw call properties as the vao property. Call vao.destroy() to free resources.

drawLines.vao({ [endpointAttributes], [vertexAttributes] })

Allocate a Vertex Array Object for the specified draw command. The resulting object may be passed to this command via the vao property.

Example

const drawLines = reglLines(...);

// Once, outside of draw loop
const vao = drawLines.vao({
  endpointAttributes: { ... },
  vertexAttributes: { ... }
});

// Within draw loop
drawLines({
  join: 'miter',
  count: n,
  vao
});

// Clean up, later
vao.destroy();

Notes

To use vertex array objects, the OES_vertex_array_object extension must be enabled. If it is not available, then vertex array objects will be emulated.