Right now I am generating a large number of quads that all have the same vertices, and then passing in positions for the centers of those quads as an attribute, and a scale as a uniform.
For example, assume the following buffers
verts buffer:
-1,-1, -1,1, 1,1, 1,1, 1,-1, -1,1 // quad #1
-1,-1, -1,1, 1,1, 1,1, 1,-1, -1,1 // quad #2
-1,-1, -1,1, 1,1, 1,1, 1,-1, -1,1 // quad #3
-1,-1, -1,1, 1,1, 1,1, 1,-1, -1,1 // quad #3
position buffer:
10,10, 10,10, 10,10, 10,10, 10,10, 10,10 // position of quad #1
20,20, 20,20, 20,20, 20,20, 20,20, 20,20 // position of quad #2
30,30, 30,30, 30,30, 30,30, 30,30, 30,30 // position of quad #3
40,40, 40,40, 40,40, 40,40, 40,40, 40,40 // position of quad #4
Then in the vertex shader something like:
attribute vec2 aVert;
attribute vec2 aPos;
uniform float uSize;
void main() {
vec2 realVertexPosition = aVert * uSize + aPos;
gl_Position = toScreenSpaceMagic(realVertexPosition);
}
The thing that's weirding me out about this is how much data repetition there is here.
If I have 1000 quads, then I have identical vertex data repeated 1000 times. And the position of every quad is repeated 6 times.
So is there a way to reuse a small buffer and tell WebGL how it repeats? Like, for example, tell WebGL that a buffer 1000 times, which has the vertices of only one quad?
Here's how I make the verts:
// pointsToQuads returns a [number, number][] with 6 elements
// for each point, 3 points for each triangle in quad.
// Note how each quad has a `pos` (meaning the center) of [0,0].
const quads = pointsToQuads(
layoutNodes.map(() => ({
pos: [0, 0],
size: { width: 1, height: 1 },
})),
)
// calls gl.bufferData() with the flattened array of values.
this.buffers.quads.updateBuffer(quads)
// Fill a buffer of center positions for every vertex.
// dupeEntries() here changes clones each element at it's
// original index 6 times, one for each vertex of that quad.
// This duplicates the same point six times.
this.buffers.positions.updateBuffer(
dupeEntries(
layoutNodes.map((node) => node.position),
6,
),
)
// Generate other buffers (UVs, vertex colors, etc)
Then renders with something like this, assuming 1000 quads:
// Calls gl.useProgram()
shaderPrograms.layoutPoints.use((shader) => {
// Calls gl.vertexAttribPointer() for each attribute.
shader.setAttributes({
// here are the same six points cloned 1000 times.
aVert: this.buffers.quads,
aUV: this.buffers.uv,
aIconUV: this.buffers.iconUv,
aPos: this.buffers.positions,
aColor: this.buffers.stateColors,
})
// Calls gl.uniform[1,2,3,4]f() for each uniform
shader.setUniforms({ uSize: styles.pointsLayout.size })
// One draw calls now renders all 1000 quads, so count here is 1000 * 6.
gl.drawArrays(gl.TRIANGLES, 0, this.buffers.quads.count)
})
But what I want is something like where there are only six coordinates in the quads buffer, but call still draw 1000 quads with one draw call.
Something like:
// only six points in `buffer`
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.glBuffer)
// Repeat buffer 1000 times.
gl.vertexAttribPointer(attribute, 1000 * 6, gl.FLOAT, false, 0, 0)
// Then one draw call
gl.drawArrays(gl.TRIANGLES, 0, 1000 * 6)
But this obviously doesn't work because there are not 6000 vertices in the buffer, only 6.
Lastly, you may be asking why? In part this is academic. This duplication of data has me questioning if this is the right approach. But secondly, this is a Expo application to be run on mobile devices, some of which have lower much lower memory limits. So shuffling around huge arrays of duplicated data seems quite wasteful.