Code
const regl = createREGL({
extensions: ['ANGLE_instanced_arrays']
});
// Instantiate a command for drawing lines
const drawLines = reglLines(regl, {
vert: `
precision highp float;
uniform float pixelRatio;
#pragma lines: attribute vec2 xy;
#pragma lines: attribute vec4 color;
#pragma lines: attribute float width;
#pragma lines: position = getPosition(xy);
#pragma lines: varying vec4 color = getColor(color);
#pragma lines: width = getWidth(width);
vec4 getPosition(vec2 xy) { return vec4(xy, 0, 1); }
vec4 getColor(vec4 color) { return color; }
float getWidth(float width) { return pixelRatio * width; }
`,
frag: `
precision lowp float;
varying vec4 color;
void main () {
gl_FragColor = color;
}`,
uniforms: {
pixelRatio: regl.context('pixelRatio')
}
});
// Construct an array of xy pairs
const n = 101;
const verticesU8 = new Uint8ClampedArray(n * 16);
const verticesF32 = new Float32Array(verticesU8.buffer);
for (let i = 0; i < n; i++) {
let t = i / (n - 1);
let x = (t * 2 - 1) * 0.8;
let y = 0.8 * Math.sin(x * 2.0 * Math.PI);
let width = 50.0 * (0.5 - 0.4 * Math.cos(Math.PI * 4 * x));
let color = [0, 1, 2].map(i => 0.5 + Math.cos(2 * (x - 2 * i * Math.PI / 3)));
// x, y (float32): bytes 0-7
// width (float32): bytes 8-11
// color (float32): bytes 12-23
verticesF32[i * 4] = x;
verticesF32[i * 4 + 1] = y;
verticesF32[i * 4 + 2] = width;
verticesU8[i * 16 + 12] = color[0] * 255;
verticesU8[i * 16 + 13] = color[1] * 255;
verticesU8[i * 16 + 14] = color[2] * 255;
verticesU8[i * 16 + 15] = 255;
}
// Pack the interleaved values into a buffer
const verticesBuffer = regl.buffer(verticesU8);
// Packing the endpoints into a buffer is a little trickier. We need blocks of sixteen
// bytes for each vertex packed into an array for the first three vertices and the last
// three (in reverse order), so basically vertices [0, 1, 2] and [n-1, n-2, n-3].
const endpointsU8 = new Uint8Array(2 * 3 * 16);
for (let j = 0; j < 16; j++) {
for (let i = 0; i < 3; i++) {
endpointsU8[i * 16 + j] = verticesU8[i * 16 + j];
endpointsU8[(3 + i) * 16 + j] = verticesU8[(n - 1 - i) * 16 + j];
}
}
const endpointsBuffer = regl.buffer(endpointsU8);
// Set up the data to be drawn.
const lineData = {
join: 'round',
cap: 'round',
vertexCount: n,
vertexAttributes: {
// Attributes are compatible with regl specification
xy: {
type: 'float32',
buffer: verticesBuffer,
offset: Float32Array.BYTES_PER_ELEMENT * 0,
stride: Float32Array.BYTES_PER_ELEMENT * 4,
// divisor: 1 // implicit (but configurable)
},
width: {
type: 'float32',
buffer: verticesBuffer,
offset: Float32Array.BYTES_PER_ELEMENT * 2,
stride: Float32Array.BYTES_PER_ELEMENT * 4,
},
color: {
type: 'uint8',
normalized: true,
buffer: verticesBuffer,
offset: Float32Array.BYTES_PER_ELEMENT * 3,
stride: Float32Array.BYTES_PER_ELEMENT * 4,
}
},
endpointCount: 2,
endpointAttributes: {
xy: {
type: 'float32',
buffer: endpointsBuffer,
offset: Float32Array.BYTES_PER_ELEMENT * 0,
stride: Float32Array.BYTES_PER_ELEMENT * 4,
},
width: {
type: 'float32',
buffer: endpointsBuffer,
offset: Float32Array.BYTES_PER_ELEMENT * 2,
stride: Float32Array.BYTES_PER_ELEMENT * 4,
},
color: {
type: 'uint8',
normalized: true,
buffer: endpointsBuffer,
offset: Float32Array.BYTES_PER_ELEMENT * 3,
stride: Float32Array.BYTES_PER_ELEMENT * 4,
}
}
};
function draw () {
regl.poll();
regl.clear({color: [0.2, 0.2, 0.2, 1]});
drawLines(lineData);
}
draw();
window.addEventListener('resize', draw);