Skip to content

Instantly share code, notes, and snippets.

@greggman
Last active Sep 30, 2021
Embed
What would you like to do?
Emulated (Better) gl.POINTS
html, body { margin: 0; height: 100%; }
canvas { width: 100%; height: 100%; display: block; }
#info {
position: absolute;
left: 0;
top: 0;
background: rgba(0,0,0,0.8);
color: white;
padding: 0.5em;
font-family: monospace;
}
#info a {
color: orange;
}
<canvas></canvas>
<div id="info">See <a target="_blank" href="https://jsgist.org/?src=dd9bea6a850447dde59dc50eeb402c1c">here</a> for non-emulated gl.POINTS</div>
import * as twgl from 'https://twgljs.org/dist/4.x/twgl-full.module.js';
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext) {
alert('Need ANGLE_instanced_arrays');
}
const pointsVS = `
attribute vec4 position;
attribute vec2 unitQuad;
uniform vec2 resolution;
varying vec2 vemu_PointCoord;
void main() {
float emu_PointSize = 64.0;
gl_Position = position;
// emu
gl_Position.xy += (unitQuad - 0.5) * 2.0 * emu_PointSize / resolution;
vemu_PointCoord = vec2(unitQuad.x, 1.0 - unitQuad.y);
}
`;
const pointsFS = `
precision mediump float;
varying vec2 vemu_PointCoord;
void main() {
gl_FragColor = vec4(vemu_PointCoord, 0, 1);
}
`;
const prg = twgl.createProgram(gl, [pointsVS, pointsFS]);
gl.useProgram(prg);
const posLoc = gl.getAttribLocation(prg, "position");
const unitQuadLoc = gl.getAttribLocation(prg, "unitQuad");
const resLoc = gl.getUniformLocation(prg, "resolution");
const unitQuadBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, unitQuadBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
]), gl.STATIC_DRAW);
const rand = (min, max) => Math.random() * (max - min) + min;
const numPoints = 100;
const pointPositions = new Float32Array(numPoints * 2);
const pointVelocities = new Float32Array(numPoints * 2);
for (let i = 0; i < numPoints; ++i) {
const offset = i * 2;
pointPositions[offset ] = rand(-2, 2);
pointPositions[offset + 1] = rand(-2, 2);
pointVelocities[offset ] = rand(-1, 1);
pointVelocities[offset + 1] = rand(-1, 1);
}
const pointsBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, pointsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, pointPositions.byteLength, gl.DYNAMIC_DRAW);
let then = 0;
function render(now) {
now *= 0.001; // convert to seconds
const elapsedTime = Math.min(now - then, 0.5);
then = now;
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// I'd update this positions on the GPU if I can but
// just for the sake of an example we're doing it on the CPU
for (let i = 0; i < numPoints * 2; ++i) {
pointPositions[i] = (pointPositions[i] + pointVelocities[i] * elapsedTime + 6) % 4 - 2;
}
gl.bindBuffer(gl.ARRAY_BUFFER, pointsBuffer);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, pointPositions);
gl.enableVertexAttribArray(posLoc);
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(posLoc, 1); // don't forget to reset to this zero if you draw something else
gl.bindBuffer(gl.ARRAY_BUFFER, unitQuadBuffer);
gl.enableVertexAttribArray(unitQuadLoc);
gl.vertexAttribPointer(unitQuadLoc, 2, gl.FLOAT, false, 0, 0);
gl.useProgram(prg);
gl.uniform2f(resLoc, gl.canvas.width, gl.canvas.height);
/* ADDED */ ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, numPoints);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
{"name":"Emulated (Better) gl.POINTS","settings":{},"filenames":["index.html","index.css","index.js"]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment