In WebGL - when instancing geometry, is it possible to pass per-vertex attribute information for each instance?Calculate surface areaWorking around gl_PointSize limitations in three.js / webGLMethod for enumerating polyhedra vertex positions, normals, and texture coordinates GLSLVertex Displacement Doesn't Work in Three.jsOpenGL 4.4/ES 3.0 and caching vertex data between passesWebGL - Rendering multiple copies of a single object in different locations in shadersWebGL display loaded model without matrixTHREEjs: GLSL Vertex Position to CPUHow to store and access per fragment attributes in WebGLGet modified mesh displaced by displacementMap in three.js

Why A=2 and B=1 in the call signs for Spirit and Opportunity?

Heat lost in ideal capacitor charging

How to determine if a hyphen (-) exists inside a column

Are cells guaranteed to get at least one mitochondrion when they divide?

How would a developer who mostly fixed bugs for years at a company call out their contributions in their CV?

Is superuser the same as root?

Is my plasma cannon concept viable?

The disk image is 497GB smaller than the target device

A burglar's sunglasses, a lady's odyssey

Can a UK national work as a paid shop assistant in the USA?

What are nvme namespaces? How do they work?

How does the Earth's center produce heat?

...And they were stumped for a long time

Why did other houses not demand this?

Which European Languages are not Indo-European?

What weight should be given to writers groups critiques?

Dad jokes are fun

“For nothing” = “pour rien”?

Why would a rational buyer offer to buy with no conditions precedent?

Is it legal to have an abortion in another state or abroad?

Low voltage shutdown with regulator using microcontroller

If I arrive in the UK, and then head to mainland Europe, does my Schengen visa 90 day limit start when I arrived in the UK, or mainland Europe?

Are there any German nonsense poems (Jabberwocky)?

What is the use case for non-breathable waterproof pants?



In WebGL - when instancing geometry, is it possible to pass per-vertex attribute information for each instance?


Calculate surface areaWorking around gl_PointSize limitations in three.js / webGLMethod for enumerating polyhedra vertex positions, normals, and texture coordinates GLSLVertex Displacement Doesn't Work in Three.jsOpenGL 4.4/ES 3.0 and caching vertex data between passesWebGL - Rendering multiple copies of a single object in different locations in shadersWebGL display loaded model without matrixTHREEjs: GLSL Vertex Position to CPUHow to store and access per fragment attributes in WebGLGet modified mesh displaced by displacementMap in three.js






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;








1















I am trying to recreate through raw WebGL the following three.js example with instanced geometry; though, after testing it looks like I may just have to draw multiple meshes like in the sample but I thought I would ask here first to double check.



The question now is essentially - is it possible to pass per-vertex data for each instance of the geometry that gets rendered?



Hopefully that makes sense but if it doesn't -



  • The geometry I'm rendering has 4 vertices.

  • For each instance of the geometry - I need to assign uv coords to each vertex of each instance of the geometry

  • it seems that setting a stride or offset does not appear to work.

Basically what is happening now is given the current data which looks something like



[s1,t1,s2,t2,s3,t3,s4,t4,s1,t1,s2,t2,s3,t3,s4,t4] // enough uvs for 2 instances, each s and t value makes up 1 uv coord for 1 vertex


Instead of data being read and assigned in blocks like this



 // instance 1 // instance 2
[[s1,t1,s2,t2,s3,t3,s4,t4], [s1,t1,s2,t2,s3,t3,s4,t4]]

// Each s and t forms 1 uv coord for 1 vertex


Data appears to be instead being read like this



 [s1,t1,s2,t2,s3,t3,s4,t4,s1,t1,s2,t2,s3,t3,s4,t4]
// Each s and t pair is assigned to every vertex of each instance


Is it at all possible to get things working like in the second block? If not that's fine but thought I ought to ask.










share|improve this question

















  • 1





    "The geometry I'm rendering has 4 vertices." OK, let me stop you right there. Don't do instancing just to render quads. Just repeat the position data; you'll almost certainly be better off, performance-wise.

    – Nicol Bolas
    Mar 24 at 6:04











  • Agree with what Nicol said. It's faster to repeat the data than to use instancing so the less vertices per instance the better it is to just repeat the data.

    – gman
    Mar 24 at 15:34











  • ah got it - thanks for the tip Nicol.

    – sortofsleepy
    Mar 24 at 22:53

















1















I am trying to recreate through raw WebGL the following three.js example with instanced geometry; though, after testing it looks like I may just have to draw multiple meshes like in the sample but I thought I would ask here first to double check.



The question now is essentially - is it possible to pass per-vertex data for each instance of the geometry that gets rendered?



Hopefully that makes sense but if it doesn't -



  • The geometry I'm rendering has 4 vertices.

  • For each instance of the geometry - I need to assign uv coords to each vertex of each instance of the geometry

  • it seems that setting a stride or offset does not appear to work.

Basically what is happening now is given the current data which looks something like



[s1,t1,s2,t2,s3,t3,s4,t4,s1,t1,s2,t2,s3,t3,s4,t4] // enough uvs for 2 instances, each s and t value makes up 1 uv coord for 1 vertex


Instead of data being read and assigned in blocks like this



 // instance 1 // instance 2
[[s1,t1,s2,t2,s3,t3,s4,t4], [s1,t1,s2,t2,s3,t3,s4,t4]]

// Each s and t forms 1 uv coord for 1 vertex


Data appears to be instead being read like this



 [s1,t1,s2,t2,s3,t3,s4,t4,s1,t1,s2,t2,s3,t3,s4,t4]
// Each s and t pair is assigned to every vertex of each instance


Is it at all possible to get things working like in the second block? If not that's fine but thought I ought to ask.










share|improve this question

















  • 1





    "The geometry I'm rendering has 4 vertices." OK, let me stop you right there. Don't do instancing just to render quads. Just repeat the position data; you'll almost certainly be better off, performance-wise.

    – Nicol Bolas
    Mar 24 at 6:04











  • Agree with what Nicol said. It's faster to repeat the data than to use instancing so the less vertices per instance the better it is to just repeat the data.

    – gman
    Mar 24 at 15:34











  • ah got it - thanks for the tip Nicol.

    – sortofsleepy
    Mar 24 at 22:53













1












1








1








I am trying to recreate through raw WebGL the following three.js example with instanced geometry; though, after testing it looks like I may just have to draw multiple meshes like in the sample but I thought I would ask here first to double check.



The question now is essentially - is it possible to pass per-vertex data for each instance of the geometry that gets rendered?



Hopefully that makes sense but if it doesn't -



  • The geometry I'm rendering has 4 vertices.

  • For each instance of the geometry - I need to assign uv coords to each vertex of each instance of the geometry

  • it seems that setting a stride or offset does not appear to work.

Basically what is happening now is given the current data which looks something like



[s1,t1,s2,t2,s3,t3,s4,t4,s1,t1,s2,t2,s3,t3,s4,t4] // enough uvs for 2 instances, each s and t value makes up 1 uv coord for 1 vertex


Instead of data being read and assigned in blocks like this



 // instance 1 // instance 2
[[s1,t1,s2,t2,s3,t3,s4,t4], [s1,t1,s2,t2,s3,t3,s4,t4]]

// Each s and t forms 1 uv coord for 1 vertex


Data appears to be instead being read like this



 [s1,t1,s2,t2,s3,t3,s4,t4,s1,t1,s2,t2,s3,t3,s4,t4]
// Each s and t pair is assigned to every vertex of each instance


Is it at all possible to get things working like in the second block? If not that's fine but thought I ought to ask.










share|improve this question














I am trying to recreate through raw WebGL the following three.js example with instanced geometry; though, after testing it looks like I may just have to draw multiple meshes like in the sample but I thought I would ask here first to double check.



The question now is essentially - is it possible to pass per-vertex data for each instance of the geometry that gets rendered?



Hopefully that makes sense but if it doesn't -



  • The geometry I'm rendering has 4 vertices.

  • For each instance of the geometry - I need to assign uv coords to each vertex of each instance of the geometry

  • it seems that setting a stride or offset does not appear to work.

Basically what is happening now is given the current data which looks something like



[s1,t1,s2,t2,s3,t3,s4,t4,s1,t1,s2,t2,s3,t3,s4,t4] // enough uvs for 2 instances, each s and t value makes up 1 uv coord for 1 vertex


Instead of data being read and assigned in blocks like this



 // instance 1 // instance 2
[[s1,t1,s2,t2,s3,t3,s4,t4], [s1,t1,s2,t2,s3,t3,s4,t4]]

// Each s and t forms 1 uv coord for 1 vertex


Data appears to be instead being read like this



 [s1,t1,s2,t2,s3,t3,s4,t4,s1,t1,s2,t2,s3,t3,s4,t4]
// Each s and t pair is assigned to every vertex of each instance


Is it at all possible to get things working like in the second block? If not that's fine but thought I ought to ask.







glsl webgl






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Mar 23 at 23:27









sortofsleepysortofsleepy

2716




2716







  • 1





    "The geometry I'm rendering has 4 vertices." OK, let me stop you right there. Don't do instancing just to render quads. Just repeat the position data; you'll almost certainly be better off, performance-wise.

    – Nicol Bolas
    Mar 24 at 6:04











  • Agree with what Nicol said. It's faster to repeat the data than to use instancing so the less vertices per instance the better it is to just repeat the data.

    – gman
    Mar 24 at 15:34











  • ah got it - thanks for the tip Nicol.

    – sortofsleepy
    Mar 24 at 22:53












  • 1





    "The geometry I'm rendering has 4 vertices." OK, let me stop you right there. Don't do instancing just to render quads. Just repeat the position data; you'll almost certainly be better off, performance-wise.

    – Nicol Bolas
    Mar 24 at 6:04











  • Agree with what Nicol said. It's faster to repeat the data than to use instancing so the less vertices per instance the better it is to just repeat the data.

    – gman
    Mar 24 at 15:34











  • ah got it - thanks for the tip Nicol.

    – sortofsleepy
    Mar 24 at 22:53







1




1





"The geometry I'm rendering has 4 vertices." OK, let me stop you right there. Don't do instancing just to render quads. Just repeat the position data; you'll almost certainly be better off, performance-wise.

– Nicol Bolas
Mar 24 at 6:04





"The geometry I'm rendering has 4 vertices." OK, let me stop you right there. Don't do instancing just to render quads. Just repeat the position data; you'll almost certainly be better off, performance-wise.

– Nicol Bolas
Mar 24 at 6:04













Agree with what Nicol said. It's faster to repeat the data than to use instancing so the less vertices per instance the better it is to just repeat the data.

– gman
Mar 24 at 15:34





Agree with what Nicol said. It's faster to repeat the data than to use instancing so the less vertices per instance the better it is to just repeat the data.

– gman
Mar 24 at 15:34













ah got it - thanks for the tip Nicol.

– sortofsleepy
Mar 24 at 22:53





ah got it - thanks for the tip Nicol.

– sortofsleepy
Mar 24 at 22:53












1 Answer
1






active

oldest

votes


















0














It depends on what you mean by




is it possible to pass per-vertex data for each instance of the geometry that gets rendered?




If you mean via attributes then the answer is no. You can get data per vertex or data per instance but not data per vertex per instance.



But you also get 2 extra inputs, gl_VertexID and gl_InstanceID (in WebGL2) or you can add your own which you could use to either compute UVs or use to look up data in a texture to effectively achieve your desired result.



For example let's say you have 20x10 cubes. You could do something like



attribute vec4 position; // 36 cube positions
attribute vec2 uv; // 36 cube UVs
attribute float instanceId; // 1 per instance

uniform float cubesAcross; // set to 20
uniform float cubesDown; // set to 10

varying v_uv;

void main()
float cubeX = mod(instanceId, cubesAcross);
float cubeY = floor(instanceId / cubesAcross);

v_vu = (vec2(cubeX, cubeY) + uv) / vec2(cubesAcross, cubesDown);

gl_Position = ...whatever you were doing for position before...



example:






"use strict";

function main()
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext)
return alert('need ANGLE_instanced_arrays');

twgl.addExtensionsToContext(gl);
const vs = `
attribute vec4 position;
attribute vec2 texcoord;
attribute mat4 matrix;
attribute float instanceId;

uniform float cubesAcross; // set to 20
uniform float cubesDown; // set to 10

varying vec2 v_texcoord;

void main()
gl_Position = matrix * position;

float cubeX = mod(instanceId, cubesAcross);
float cubeY = floor(instanceId / cubesAcross);

v_texcoord = (vec2(cubeX, cubeY) + texcoord) / vec2(cubesAcross, cubesDown);

`;

const fs = `
precision mediump float;

varying vec2 v_texcoord;

void main()
gl_FragColor = vec4(v_texcoord, 0, 1);

`;

// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

const cubesAcross = 20;
const cubesDown = 10;
const numCubes = cubesAcross * cubesDown;
// matrix per instance
const matrixData = new Float32Array(16 * numCubes);
const matrices = [];
const instanceIds = new Float32Array(numCubes);
for (let i = 0; i < numCubes; ++i)
instanceIds[i] = i;
// make a typedarray view for each matrix
matrices.push(matrixData.subarray(i * 16, (i + 1) * 16));


const arrays =
position: [
1, 1, -1,
1, 1, 1,
1, -1, 1,
1, -1, -1,

-1, 1, 1,
-1, 1, -1,
-1, -1, -1,
-1, -1, 1,

-1, 1, 1,
1, 1, 1,
1, 1, -1,
-1, 1, -1,

-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,

1, 1, 1,
-1, 1, 1,
-1, -1, 1,
1, -1, 1,

-1, 1, -1,
1, 1, -1,
1, -1, -1,
-1, -1, -1,
],
texcoord: [
1, 0,
0, 0,
0, 1,
1, 1,

1, 0,
0, 0,
0, 1,
1, 1,

0, 1,
1, 1,
1, 0,
0, 0,

0, 1,
1, 1,
1, 0,
0, 0,

1, 1,
0, 1,
0, 0,
1, 0,

1, 0,
0, 0,
0, 1,
1, 1,
],
indices: [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23,
],
instanceId: numComponents: 1, data: instanceIds, divisor: 1 ,
matrix: numComponents: 16, data: matrices, divisor: 1 ,
;
// create buffers, upload data (gl.bufferData)
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

function render(time) gl.DEPTH_BUFFER_BIT);

const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 24, 76];
const target = [18, 10, 0];
const up = [0, 1, 0];

const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);

// update the instance for each matrix
const spacing = 2.5 + Math.sin(time) * .5;
let i = 0;
for (let y = 0; y < cubesDown; ++y)
for (let x = 0; x < cubesAcross; ++x)
const matrix = matrices[i++];
m4.translate(viewProjection, [
x * spacing,
y * spacing,
0,
], matrix);



gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer, ext.vertexAttribDivisorANGLE
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo,
cubesAcross,
cubesDown,
);

// upload instance matrices to buffer
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.matrix.buffer);
gl.bufferData(gl.ARRAY_BUFFER, matrixData, gl.DYNAMIC_DRAW);

ext.drawElementsInstancedANGLE(
gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0, numCubes);

requestAnimationFrame(render);

requestAnimationFrame(render);


main();

body 
margin: 0;


canvas
width: 100vw;
height: 100vh;
display: block;

<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>





If you actually want unique per vertex per instance data then put it in a texture and pass in a vertexId as well. Then use both vertexId and instanceId to compute a texture coordinate



 attribute float instanceId;
attribute float vertexId;

uniform sampler2D dataTexture;
uniform vec2 dataTextureSize;

varying vec2 v_texcoord;

void main() {

// each row is for an instance, each texel
// per vertex. Of course if you want more data
// per vertex then multiply vertexId by the number of
// vec4s of data you need. If you need more instances
// then compute a more complex offset based off instanceId
vec2 uv = (vec2(vertexId, instanceId) + .5) / dataTextureSize;
vec4 data = texture2D(dataTexture, uv);

v_texcoord = data.xy;





"use strict";

function main()
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const ext1 = gl.getExtension('OES_texture_float');
if (!ext1)
return alert('need OES_texture_float');

const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext)
return alert('need ANGLE_instanced_arrays');

twgl.addExtensionsToContext(gl);
const vs = `
attribute vec4 position;
attribute mat4 matrix;
attribute float instanceId;
attribute float vertexId;

uniform sampler2D dataTexture;
uniform vec2 dataTextureSize;

varying vec2 v_texcoord;

void main()
gl_Position = matrix * position;

// each row is for an instance, each texel
// per vertex. Of course if you want more data
// per vertex then multiply vertexId by the number of
// vec4s of data you need. If you need more instances
// then compute a more complex offset based off instanceId
vec2 uv = (vec2(vertexId, instanceId) + .5) / dataTextureSize;
vec4 data = texture2D(dataTexture, uv);

v_texcoord = data.xy;

`;

const fs = `
precision mediump float;

varying vec2 v_texcoord;

void main()
gl_FragColor = vec4(v_texcoord, 0, 1);

`;

// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

const cubesAcross = 20;
const cubesDown = 10;
const numCubes = cubesAcross * cubesDown;
// matrix per instance
const matrixData = new Float32Array(16 * numCubes);
const matrices = [];
const instanceIds = new Float32Array(numCubes);
for (let i = 0; i < numCubes; ++i)
instanceIds[i] = i;
// make a typedarray view for each matrix
matrices.push(matrixData.subarray(i * 16, (i + 1) * 16));


const arrays =
position: [
1, 1, -1,
1, 1, 1,
1, -1, 1,
1, -1, -1,

-1, 1, 1,
-1, 1, -1,
-1, -1, -1,
-1, -1, 1,

-1, 1, 1,
1, 1, 1,
1, 1, -1,
-1, 1, -1,

-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,

1, 1, 1,
-1, 1, 1,
-1, -1, 1,
1, -1, 1,

-1, 1, -1,
1, 1, -1,
1, -1, -1,
-1, -1, -1,
],
vertexId:
numComponents: 1,
data: [
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15,
16, 17, 18, 19,
20, 21, 21, 23,
],
,
indices: [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23,
],
instanceId: numComponents: 1, data: instanceIds, divisor: 1 ,
matrix: numComponents: 16, data: matrices, divisor: 1 ,
;
// create buffers, upload data (gl.bufferData)
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

// put UV data in texture
const uvs = [];
for (let y = 0; y < cubesDown; ++y)
const v0 = (y ) / cubesDown;
const v1 = (y + 1) / cubesDown;
for (let x = 0; x < cubesAcross; ++x)
const u0 = (x ) / cubesAcross;
const u1 = (x + 1) / cubesAcross;
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);



const texWidth = 24; // width = 24 vertices * 1 texel per
const texHeight = numCubes; // height = numInstances
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(
gl.TEXTURE_2D,
0, // level
gl.RGBA,
texWidth,
texHeight,
0, // border
gl.RGBA,
gl.FLOAT,
new Float32Array(uvs));


function render(time) gl.DEPTH_BUFFER_BIT);

const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 24, 76];
const target = [18, 10, 0];
const up = [0, 1, 0];

const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);

// update the instance for each matrix
const spacing = 2.5 + Math.sin(time) * .5;
let i = 0;
for (let y = 0; y < cubesDown; ++y)
for (let x = 0; x < cubesAcross; ++x)
const matrix = matrices[i++];
m4.translate(viewProjection, [
x * spacing,
y * spacing,
0,
], matrix);



gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer, ext.vertexAttribDivisorANGLE
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo,
dataTexture: tex,
dataTextureSize: [texWidth, texHeight],
);

// upload instance matrices to buffer
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.matrix.buffer);
gl.bufferData(gl.ARRAY_BUFFER, matrixData, gl.DYNAMIC_DRAW);

ext.drawElementsInstancedANGLE(
gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0, numCubes);

requestAnimationFrame(render);

requestAnimationFrame(render);


main();

body 
margin: 0;


canvas
width: 100vw;
height: 100vh;
display: block;

<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>








share|improve this answer

























  • Thanks for the help and many thanks for the detailed sample! Figured it would have to end up something along these lines if I didn't just re-render things.

    – sortofsleepy
    Mar 24 at 22:51











Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55319323%2fin-webgl-when-instancing-geometry-is-it-possible-to-pass-per-vertex-attribute%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









0














It depends on what you mean by




is it possible to pass per-vertex data for each instance of the geometry that gets rendered?




If you mean via attributes then the answer is no. You can get data per vertex or data per instance but not data per vertex per instance.



But you also get 2 extra inputs, gl_VertexID and gl_InstanceID (in WebGL2) or you can add your own which you could use to either compute UVs or use to look up data in a texture to effectively achieve your desired result.



For example let's say you have 20x10 cubes. You could do something like



attribute vec4 position; // 36 cube positions
attribute vec2 uv; // 36 cube UVs
attribute float instanceId; // 1 per instance

uniform float cubesAcross; // set to 20
uniform float cubesDown; // set to 10

varying v_uv;

void main()
float cubeX = mod(instanceId, cubesAcross);
float cubeY = floor(instanceId / cubesAcross);

v_vu = (vec2(cubeX, cubeY) + uv) / vec2(cubesAcross, cubesDown);

gl_Position = ...whatever you were doing for position before...



example:






"use strict";

function main()
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext)
return alert('need ANGLE_instanced_arrays');

twgl.addExtensionsToContext(gl);
const vs = `
attribute vec4 position;
attribute vec2 texcoord;
attribute mat4 matrix;
attribute float instanceId;

uniform float cubesAcross; // set to 20
uniform float cubesDown; // set to 10

varying vec2 v_texcoord;

void main()
gl_Position = matrix * position;

float cubeX = mod(instanceId, cubesAcross);
float cubeY = floor(instanceId / cubesAcross);

v_texcoord = (vec2(cubeX, cubeY) + texcoord) / vec2(cubesAcross, cubesDown);

`;

const fs = `
precision mediump float;

varying vec2 v_texcoord;

void main()
gl_FragColor = vec4(v_texcoord, 0, 1);

`;

// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

const cubesAcross = 20;
const cubesDown = 10;
const numCubes = cubesAcross * cubesDown;
// matrix per instance
const matrixData = new Float32Array(16 * numCubes);
const matrices = [];
const instanceIds = new Float32Array(numCubes);
for (let i = 0; i < numCubes; ++i)
instanceIds[i] = i;
// make a typedarray view for each matrix
matrices.push(matrixData.subarray(i * 16, (i + 1) * 16));


const arrays =
position: [
1, 1, -1,
1, 1, 1,
1, -1, 1,
1, -1, -1,

-1, 1, 1,
-1, 1, -1,
-1, -1, -1,
-1, -1, 1,

-1, 1, 1,
1, 1, 1,
1, 1, -1,
-1, 1, -1,

-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,

1, 1, 1,
-1, 1, 1,
-1, -1, 1,
1, -1, 1,

-1, 1, -1,
1, 1, -1,
1, -1, -1,
-1, -1, -1,
],
texcoord: [
1, 0,
0, 0,
0, 1,
1, 1,

1, 0,
0, 0,
0, 1,
1, 1,

0, 1,
1, 1,
1, 0,
0, 0,

0, 1,
1, 1,
1, 0,
0, 0,

1, 1,
0, 1,
0, 0,
1, 0,

1, 0,
0, 0,
0, 1,
1, 1,
],
indices: [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23,
],
instanceId: numComponents: 1, data: instanceIds, divisor: 1 ,
matrix: numComponents: 16, data: matrices, divisor: 1 ,
;
// create buffers, upload data (gl.bufferData)
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

function render(time) gl.DEPTH_BUFFER_BIT);

const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 24, 76];
const target = [18, 10, 0];
const up = [0, 1, 0];

const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);

// update the instance for each matrix
const spacing = 2.5 + Math.sin(time) * .5;
let i = 0;
for (let y = 0; y < cubesDown; ++y)
for (let x = 0; x < cubesAcross; ++x)
const matrix = matrices[i++];
m4.translate(viewProjection, [
x * spacing,
y * spacing,
0,
], matrix);



gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer, ext.vertexAttribDivisorANGLE
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo,
cubesAcross,
cubesDown,
);

// upload instance matrices to buffer
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.matrix.buffer);
gl.bufferData(gl.ARRAY_BUFFER, matrixData, gl.DYNAMIC_DRAW);

ext.drawElementsInstancedANGLE(
gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0, numCubes);

requestAnimationFrame(render);

requestAnimationFrame(render);


main();

body 
margin: 0;


canvas
width: 100vw;
height: 100vh;
display: block;

<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>





If you actually want unique per vertex per instance data then put it in a texture and pass in a vertexId as well. Then use both vertexId and instanceId to compute a texture coordinate



 attribute float instanceId;
attribute float vertexId;

uniform sampler2D dataTexture;
uniform vec2 dataTextureSize;

varying vec2 v_texcoord;

void main() {

// each row is for an instance, each texel
// per vertex. Of course if you want more data
// per vertex then multiply vertexId by the number of
// vec4s of data you need. If you need more instances
// then compute a more complex offset based off instanceId
vec2 uv = (vec2(vertexId, instanceId) + .5) / dataTextureSize;
vec4 data = texture2D(dataTexture, uv);

v_texcoord = data.xy;





"use strict";

function main()
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const ext1 = gl.getExtension('OES_texture_float');
if (!ext1)
return alert('need OES_texture_float');

const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext)
return alert('need ANGLE_instanced_arrays');

twgl.addExtensionsToContext(gl);
const vs = `
attribute vec4 position;
attribute mat4 matrix;
attribute float instanceId;
attribute float vertexId;

uniform sampler2D dataTexture;
uniform vec2 dataTextureSize;

varying vec2 v_texcoord;

void main()
gl_Position = matrix * position;

// each row is for an instance, each texel
// per vertex. Of course if you want more data
// per vertex then multiply vertexId by the number of
// vec4s of data you need. If you need more instances
// then compute a more complex offset based off instanceId
vec2 uv = (vec2(vertexId, instanceId) + .5) / dataTextureSize;
vec4 data = texture2D(dataTexture, uv);

v_texcoord = data.xy;

`;

const fs = `
precision mediump float;

varying vec2 v_texcoord;

void main()
gl_FragColor = vec4(v_texcoord, 0, 1);

`;

// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

const cubesAcross = 20;
const cubesDown = 10;
const numCubes = cubesAcross * cubesDown;
// matrix per instance
const matrixData = new Float32Array(16 * numCubes);
const matrices = [];
const instanceIds = new Float32Array(numCubes);
for (let i = 0; i < numCubes; ++i)
instanceIds[i] = i;
// make a typedarray view for each matrix
matrices.push(matrixData.subarray(i * 16, (i + 1) * 16));


const arrays =
position: [
1, 1, -1,
1, 1, 1,
1, -1, 1,
1, -1, -1,

-1, 1, 1,
-1, 1, -1,
-1, -1, -1,
-1, -1, 1,

-1, 1, 1,
1, 1, 1,
1, 1, -1,
-1, 1, -1,

-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,

1, 1, 1,
-1, 1, 1,
-1, -1, 1,
1, -1, 1,

-1, 1, -1,
1, 1, -1,
1, -1, -1,
-1, -1, -1,
],
vertexId:
numComponents: 1,
data: [
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15,
16, 17, 18, 19,
20, 21, 21, 23,
],
,
indices: [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23,
],
instanceId: numComponents: 1, data: instanceIds, divisor: 1 ,
matrix: numComponents: 16, data: matrices, divisor: 1 ,
;
// create buffers, upload data (gl.bufferData)
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

// put UV data in texture
const uvs = [];
for (let y = 0; y < cubesDown; ++y)
const v0 = (y ) / cubesDown;
const v1 = (y + 1) / cubesDown;
for (let x = 0; x < cubesAcross; ++x)
const u0 = (x ) / cubesAcross;
const u1 = (x + 1) / cubesAcross;
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);



const texWidth = 24; // width = 24 vertices * 1 texel per
const texHeight = numCubes; // height = numInstances
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(
gl.TEXTURE_2D,
0, // level
gl.RGBA,
texWidth,
texHeight,
0, // border
gl.RGBA,
gl.FLOAT,
new Float32Array(uvs));


function render(time) gl.DEPTH_BUFFER_BIT);

const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 24, 76];
const target = [18, 10, 0];
const up = [0, 1, 0];

const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);

// update the instance for each matrix
const spacing = 2.5 + Math.sin(time) * .5;
let i = 0;
for (let y = 0; y < cubesDown; ++y)
for (let x = 0; x < cubesAcross; ++x)
const matrix = matrices[i++];
m4.translate(viewProjection, [
x * spacing,
y * spacing,
0,
], matrix);



gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer, ext.vertexAttribDivisorANGLE
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo,
dataTexture: tex,
dataTextureSize: [texWidth, texHeight],
);

// upload instance matrices to buffer
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.matrix.buffer);
gl.bufferData(gl.ARRAY_BUFFER, matrixData, gl.DYNAMIC_DRAW);

ext.drawElementsInstancedANGLE(
gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0, numCubes);

requestAnimationFrame(render);

requestAnimationFrame(render);


main();

body 
margin: 0;


canvas
width: 100vw;
height: 100vh;
display: block;

<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>








share|improve this answer

























  • Thanks for the help and many thanks for the detailed sample! Figured it would have to end up something along these lines if I didn't just re-render things.

    – sortofsleepy
    Mar 24 at 22:51















0














It depends on what you mean by




is it possible to pass per-vertex data for each instance of the geometry that gets rendered?




If you mean via attributes then the answer is no. You can get data per vertex or data per instance but not data per vertex per instance.



But you also get 2 extra inputs, gl_VertexID and gl_InstanceID (in WebGL2) or you can add your own which you could use to either compute UVs or use to look up data in a texture to effectively achieve your desired result.



For example let's say you have 20x10 cubes. You could do something like



attribute vec4 position; // 36 cube positions
attribute vec2 uv; // 36 cube UVs
attribute float instanceId; // 1 per instance

uniform float cubesAcross; // set to 20
uniform float cubesDown; // set to 10

varying v_uv;

void main()
float cubeX = mod(instanceId, cubesAcross);
float cubeY = floor(instanceId / cubesAcross);

v_vu = (vec2(cubeX, cubeY) + uv) / vec2(cubesAcross, cubesDown);

gl_Position = ...whatever you were doing for position before...



example:






"use strict";

function main()
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext)
return alert('need ANGLE_instanced_arrays');

twgl.addExtensionsToContext(gl);
const vs = `
attribute vec4 position;
attribute vec2 texcoord;
attribute mat4 matrix;
attribute float instanceId;

uniform float cubesAcross; // set to 20
uniform float cubesDown; // set to 10

varying vec2 v_texcoord;

void main()
gl_Position = matrix * position;

float cubeX = mod(instanceId, cubesAcross);
float cubeY = floor(instanceId / cubesAcross);

v_texcoord = (vec2(cubeX, cubeY) + texcoord) / vec2(cubesAcross, cubesDown);

`;

const fs = `
precision mediump float;

varying vec2 v_texcoord;

void main()
gl_FragColor = vec4(v_texcoord, 0, 1);

`;

// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

const cubesAcross = 20;
const cubesDown = 10;
const numCubes = cubesAcross * cubesDown;
// matrix per instance
const matrixData = new Float32Array(16 * numCubes);
const matrices = [];
const instanceIds = new Float32Array(numCubes);
for (let i = 0; i < numCubes; ++i)
instanceIds[i] = i;
// make a typedarray view for each matrix
matrices.push(matrixData.subarray(i * 16, (i + 1) * 16));


const arrays =
position: [
1, 1, -1,
1, 1, 1,
1, -1, 1,
1, -1, -1,

-1, 1, 1,
-1, 1, -1,
-1, -1, -1,
-1, -1, 1,

-1, 1, 1,
1, 1, 1,
1, 1, -1,
-1, 1, -1,

-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,

1, 1, 1,
-1, 1, 1,
-1, -1, 1,
1, -1, 1,

-1, 1, -1,
1, 1, -1,
1, -1, -1,
-1, -1, -1,
],
texcoord: [
1, 0,
0, 0,
0, 1,
1, 1,

1, 0,
0, 0,
0, 1,
1, 1,

0, 1,
1, 1,
1, 0,
0, 0,

0, 1,
1, 1,
1, 0,
0, 0,

1, 1,
0, 1,
0, 0,
1, 0,

1, 0,
0, 0,
0, 1,
1, 1,
],
indices: [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23,
],
instanceId: numComponents: 1, data: instanceIds, divisor: 1 ,
matrix: numComponents: 16, data: matrices, divisor: 1 ,
;
// create buffers, upload data (gl.bufferData)
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

function render(time) gl.DEPTH_BUFFER_BIT);

const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 24, 76];
const target = [18, 10, 0];
const up = [0, 1, 0];

const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);

// update the instance for each matrix
const spacing = 2.5 + Math.sin(time) * .5;
let i = 0;
for (let y = 0; y < cubesDown; ++y)
for (let x = 0; x < cubesAcross; ++x)
const matrix = matrices[i++];
m4.translate(viewProjection, [
x * spacing,
y * spacing,
0,
], matrix);



gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer, ext.vertexAttribDivisorANGLE
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo,
cubesAcross,
cubesDown,
);

// upload instance matrices to buffer
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.matrix.buffer);
gl.bufferData(gl.ARRAY_BUFFER, matrixData, gl.DYNAMIC_DRAW);

ext.drawElementsInstancedANGLE(
gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0, numCubes);

requestAnimationFrame(render);

requestAnimationFrame(render);


main();

body 
margin: 0;


canvas
width: 100vw;
height: 100vh;
display: block;

<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>





If you actually want unique per vertex per instance data then put it in a texture and pass in a vertexId as well. Then use both vertexId and instanceId to compute a texture coordinate



 attribute float instanceId;
attribute float vertexId;

uniform sampler2D dataTexture;
uniform vec2 dataTextureSize;

varying vec2 v_texcoord;

void main() {

// each row is for an instance, each texel
// per vertex. Of course if you want more data
// per vertex then multiply vertexId by the number of
// vec4s of data you need. If you need more instances
// then compute a more complex offset based off instanceId
vec2 uv = (vec2(vertexId, instanceId) + .5) / dataTextureSize;
vec4 data = texture2D(dataTexture, uv);

v_texcoord = data.xy;





"use strict";

function main()
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const ext1 = gl.getExtension('OES_texture_float');
if (!ext1)
return alert('need OES_texture_float');

const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext)
return alert('need ANGLE_instanced_arrays');

twgl.addExtensionsToContext(gl);
const vs = `
attribute vec4 position;
attribute mat4 matrix;
attribute float instanceId;
attribute float vertexId;

uniform sampler2D dataTexture;
uniform vec2 dataTextureSize;

varying vec2 v_texcoord;

void main()
gl_Position = matrix * position;

// each row is for an instance, each texel
// per vertex. Of course if you want more data
// per vertex then multiply vertexId by the number of
// vec4s of data you need. If you need more instances
// then compute a more complex offset based off instanceId
vec2 uv = (vec2(vertexId, instanceId) + .5) / dataTextureSize;
vec4 data = texture2D(dataTexture, uv);

v_texcoord = data.xy;

`;

const fs = `
precision mediump float;

varying vec2 v_texcoord;

void main()
gl_FragColor = vec4(v_texcoord, 0, 1);

`;

// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

const cubesAcross = 20;
const cubesDown = 10;
const numCubes = cubesAcross * cubesDown;
// matrix per instance
const matrixData = new Float32Array(16 * numCubes);
const matrices = [];
const instanceIds = new Float32Array(numCubes);
for (let i = 0; i < numCubes; ++i)
instanceIds[i] = i;
// make a typedarray view for each matrix
matrices.push(matrixData.subarray(i * 16, (i + 1) * 16));


const arrays =
position: [
1, 1, -1,
1, 1, 1,
1, -1, 1,
1, -1, -1,

-1, 1, 1,
-1, 1, -1,
-1, -1, -1,
-1, -1, 1,

-1, 1, 1,
1, 1, 1,
1, 1, -1,
-1, 1, -1,

-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,

1, 1, 1,
-1, 1, 1,
-1, -1, 1,
1, -1, 1,

-1, 1, -1,
1, 1, -1,
1, -1, -1,
-1, -1, -1,
],
vertexId:
numComponents: 1,
data: [
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15,
16, 17, 18, 19,
20, 21, 21, 23,
],
,
indices: [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23,
],
instanceId: numComponents: 1, data: instanceIds, divisor: 1 ,
matrix: numComponents: 16, data: matrices, divisor: 1 ,
;
// create buffers, upload data (gl.bufferData)
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

// put UV data in texture
const uvs = [];
for (let y = 0; y < cubesDown; ++y)
const v0 = (y ) / cubesDown;
const v1 = (y + 1) / cubesDown;
for (let x = 0; x < cubesAcross; ++x)
const u0 = (x ) / cubesAcross;
const u1 = (x + 1) / cubesAcross;
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);



const texWidth = 24; // width = 24 vertices * 1 texel per
const texHeight = numCubes; // height = numInstances
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(
gl.TEXTURE_2D,
0, // level
gl.RGBA,
texWidth,
texHeight,
0, // border
gl.RGBA,
gl.FLOAT,
new Float32Array(uvs));


function render(time) gl.DEPTH_BUFFER_BIT);

const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 24, 76];
const target = [18, 10, 0];
const up = [0, 1, 0];

const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);

// update the instance for each matrix
const spacing = 2.5 + Math.sin(time) * .5;
let i = 0;
for (let y = 0; y < cubesDown; ++y)
for (let x = 0; x < cubesAcross; ++x)
const matrix = matrices[i++];
m4.translate(viewProjection, [
x * spacing,
y * spacing,
0,
], matrix);



gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer, ext.vertexAttribDivisorANGLE
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo,
dataTexture: tex,
dataTextureSize: [texWidth, texHeight],
);

// upload instance matrices to buffer
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.matrix.buffer);
gl.bufferData(gl.ARRAY_BUFFER, matrixData, gl.DYNAMIC_DRAW);

ext.drawElementsInstancedANGLE(
gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0, numCubes);

requestAnimationFrame(render);

requestAnimationFrame(render);


main();

body 
margin: 0;


canvas
width: 100vw;
height: 100vh;
display: block;

<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>








share|improve this answer

























  • Thanks for the help and many thanks for the detailed sample! Figured it would have to end up something along these lines if I didn't just re-render things.

    – sortofsleepy
    Mar 24 at 22:51













0












0








0







It depends on what you mean by




is it possible to pass per-vertex data for each instance of the geometry that gets rendered?




If you mean via attributes then the answer is no. You can get data per vertex or data per instance but not data per vertex per instance.



But you also get 2 extra inputs, gl_VertexID and gl_InstanceID (in WebGL2) or you can add your own which you could use to either compute UVs or use to look up data in a texture to effectively achieve your desired result.



For example let's say you have 20x10 cubes. You could do something like



attribute vec4 position; // 36 cube positions
attribute vec2 uv; // 36 cube UVs
attribute float instanceId; // 1 per instance

uniform float cubesAcross; // set to 20
uniform float cubesDown; // set to 10

varying v_uv;

void main()
float cubeX = mod(instanceId, cubesAcross);
float cubeY = floor(instanceId / cubesAcross);

v_vu = (vec2(cubeX, cubeY) + uv) / vec2(cubesAcross, cubesDown);

gl_Position = ...whatever you were doing for position before...



example:






"use strict";

function main()
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext)
return alert('need ANGLE_instanced_arrays');

twgl.addExtensionsToContext(gl);
const vs = `
attribute vec4 position;
attribute vec2 texcoord;
attribute mat4 matrix;
attribute float instanceId;

uniform float cubesAcross; // set to 20
uniform float cubesDown; // set to 10

varying vec2 v_texcoord;

void main()
gl_Position = matrix * position;

float cubeX = mod(instanceId, cubesAcross);
float cubeY = floor(instanceId / cubesAcross);

v_texcoord = (vec2(cubeX, cubeY) + texcoord) / vec2(cubesAcross, cubesDown);

`;

const fs = `
precision mediump float;

varying vec2 v_texcoord;

void main()
gl_FragColor = vec4(v_texcoord, 0, 1);

`;

// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

const cubesAcross = 20;
const cubesDown = 10;
const numCubes = cubesAcross * cubesDown;
// matrix per instance
const matrixData = new Float32Array(16 * numCubes);
const matrices = [];
const instanceIds = new Float32Array(numCubes);
for (let i = 0; i < numCubes; ++i)
instanceIds[i] = i;
// make a typedarray view for each matrix
matrices.push(matrixData.subarray(i * 16, (i + 1) * 16));


const arrays =
position: [
1, 1, -1,
1, 1, 1,
1, -1, 1,
1, -1, -1,

-1, 1, 1,
-1, 1, -1,
-1, -1, -1,
-1, -1, 1,

-1, 1, 1,
1, 1, 1,
1, 1, -1,
-1, 1, -1,

-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,

1, 1, 1,
-1, 1, 1,
-1, -1, 1,
1, -1, 1,

-1, 1, -1,
1, 1, -1,
1, -1, -1,
-1, -1, -1,
],
texcoord: [
1, 0,
0, 0,
0, 1,
1, 1,

1, 0,
0, 0,
0, 1,
1, 1,

0, 1,
1, 1,
1, 0,
0, 0,

0, 1,
1, 1,
1, 0,
0, 0,

1, 1,
0, 1,
0, 0,
1, 0,

1, 0,
0, 0,
0, 1,
1, 1,
],
indices: [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23,
],
instanceId: numComponents: 1, data: instanceIds, divisor: 1 ,
matrix: numComponents: 16, data: matrices, divisor: 1 ,
;
// create buffers, upload data (gl.bufferData)
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

function render(time) gl.DEPTH_BUFFER_BIT);

const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 24, 76];
const target = [18, 10, 0];
const up = [0, 1, 0];

const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);

// update the instance for each matrix
const spacing = 2.5 + Math.sin(time) * .5;
let i = 0;
for (let y = 0; y < cubesDown; ++y)
for (let x = 0; x < cubesAcross; ++x)
const matrix = matrices[i++];
m4.translate(viewProjection, [
x * spacing,
y * spacing,
0,
], matrix);



gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer, ext.vertexAttribDivisorANGLE
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo,
cubesAcross,
cubesDown,
);

// upload instance matrices to buffer
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.matrix.buffer);
gl.bufferData(gl.ARRAY_BUFFER, matrixData, gl.DYNAMIC_DRAW);

ext.drawElementsInstancedANGLE(
gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0, numCubes);

requestAnimationFrame(render);

requestAnimationFrame(render);


main();

body 
margin: 0;


canvas
width: 100vw;
height: 100vh;
display: block;

<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>





If you actually want unique per vertex per instance data then put it in a texture and pass in a vertexId as well. Then use both vertexId and instanceId to compute a texture coordinate



 attribute float instanceId;
attribute float vertexId;

uniform sampler2D dataTexture;
uniform vec2 dataTextureSize;

varying vec2 v_texcoord;

void main() {

// each row is for an instance, each texel
// per vertex. Of course if you want more data
// per vertex then multiply vertexId by the number of
// vec4s of data you need. If you need more instances
// then compute a more complex offset based off instanceId
vec2 uv = (vec2(vertexId, instanceId) + .5) / dataTextureSize;
vec4 data = texture2D(dataTexture, uv);

v_texcoord = data.xy;





"use strict";

function main()
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const ext1 = gl.getExtension('OES_texture_float');
if (!ext1)
return alert('need OES_texture_float');

const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext)
return alert('need ANGLE_instanced_arrays');

twgl.addExtensionsToContext(gl);
const vs = `
attribute vec4 position;
attribute mat4 matrix;
attribute float instanceId;
attribute float vertexId;

uniform sampler2D dataTexture;
uniform vec2 dataTextureSize;

varying vec2 v_texcoord;

void main()
gl_Position = matrix * position;

// each row is for an instance, each texel
// per vertex. Of course if you want more data
// per vertex then multiply vertexId by the number of
// vec4s of data you need. If you need more instances
// then compute a more complex offset based off instanceId
vec2 uv = (vec2(vertexId, instanceId) + .5) / dataTextureSize;
vec4 data = texture2D(dataTexture, uv);

v_texcoord = data.xy;

`;

const fs = `
precision mediump float;

varying vec2 v_texcoord;

void main()
gl_FragColor = vec4(v_texcoord, 0, 1);

`;

// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

const cubesAcross = 20;
const cubesDown = 10;
const numCubes = cubesAcross * cubesDown;
// matrix per instance
const matrixData = new Float32Array(16 * numCubes);
const matrices = [];
const instanceIds = new Float32Array(numCubes);
for (let i = 0; i < numCubes; ++i)
instanceIds[i] = i;
// make a typedarray view for each matrix
matrices.push(matrixData.subarray(i * 16, (i + 1) * 16));


const arrays =
position: [
1, 1, -1,
1, 1, 1,
1, -1, 1,
1, -1, -1,

-1, 1, 1,
-1, 1, -1,
-1, -1, -1,
-1, -1, 1,

-1, 1, 1,
1, 1, 1,
1, 1, -1,
-1, 1, -1,

-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,

1, 1, 1,
-1, 1, 1,
-1, -1, 1,
1, -1, 1,

-1, 1, -1,
1, 1, -1,
1, -1, -1,
-1, -1, -1,
],
vertexId:
numComponents: 1,
data: [
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15,
16, 17, 18, 19,
20, 21, 21, 23,
],
,
indices: [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23,
],
instanceId: numComponents: 1, data: instanceIds, divisor: 1 ,
matrix: numComponents: 16, data: matrices, divisor: 1 ,
;
// create buffers, upload data (gl.bufferData)
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

// put UV data in texture
const uvs = [];
for (let y = 0; y < cubesDown; ++y)
const v0 = (y ) / cubesDown;
const v1 = (y + 1) / cubesDown;
for (let x = 0; x < cubesAcross; ++x)
const u0 = (x ) / cubesAcross;
const u1 = (x + 1) / cubesAcross;
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);



const texWidth = 24; // width = 24 vertices * 1 texel per
const texHeight = numCubes; // height = numInstances
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(
gl.TEXTURE_2D,
0, // level
gl.RGBA,
texWidth,
texHeight,
0, // border
gl.RGBA,
gl.FLOAT,
new Float32Array(uvs));


function render(time) gl.DEPTH_BUFFER_BIT);

const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 24, 76];
const target = [18, 10, 0];
const up = [0, 1, 0];

const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);

// update the instance for each matrix
const spacing = 2.5 + Math.sin(time) * .5;
let i = 0;
for (let y = 0; y < cubesDown; ++y)
for (let x = 0; x < cubesAcross; ++x)
const matrix = matrices[i++];
m4.translate(viewProjection, [
x * spacing,
y * spacing,
0,
], matrix);



gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer, ext.vertexAttribDivisorANGLE
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo,
dataTexture: tex,
dataTextureSize: [texWidth, texHeight],
);

// upload instance matrices to buffer
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.matrix.buffer);
gl.bufferData(gl.ARRAY_BUFFER, matrixData, gl.DYNAMIC_DRAW);

ext.drawElementsInstancedANGLE(
gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0, numCubes);

requestAnimationFrame(render);

requestAnimationFrame(render);


main();

body 
margin: 0;


canvas
width: 100vw;
height: 100vh;
display: block;

<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>








share|improve this answer















It depends on what you mean by




is it possible to pass per-vertex data for each instance of the geometry that gets rendered?




If you mean via attributes then the answer is no. You can get data per vertex or data per instance but not data per vertex per instance.



But you also get 2 extra inputs, gl_VertexID and gl_InstanceID (in WebGL2) or you can add your own which you could use to either compute UVs or use to look up data in a texture to effectively achieve your desired result.



For example let's say you have 20x10 cubes. You could do something like



attribute vec4 position; // 36 cube positions
attribute vec2 uv; // 36 cube UVs
attribute float instanceId; // 1 per instance

uniform float cubesAcross; // set to 20
uniform float cubesDown; // set to 10

varying v_uv;

void main()
float cubeX = mod(instanceId, cubesAcross);
float cubeY = floor(instanceId / cubesAcross);

v_vu = (vec2(cubeX, cubeY) + uv) / vec2(cubesAcross, cubesDown);

gl_Position = ...whatever you were doing for position before...



example:






"use strict";

function main()
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext)
return alert('need ANGLE_instanced_arrays');

twgl.addExtensionsToContext(gl);
const vs = `
attribute vec4 position;
attribute vec2 texcoord;
attribute mat4 matrix;
attribute float instanceId;

uniform float cubesAcross; // set to 20
uniform float cubesDown; // set to 10

varying vec2 v_texcoord;

void main()
gl_Position = matrix * position;

float cubeX = mod(instanceId, cubesAcross);
float cubeY = floor(instanceId / cubesAcross);

v_texcoord = (vec2(cubeX, cubeY) + texcoord) / vec2(cubesAcross, cubesDown);

`;

const fs = `
precision mediump float;

varying vec2 v_texcoord;

void main()
gl_FragColor = vec4(v_texcoord, 0, 1);

`;

// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

const cubesAcross = 20;
const cubesDown = 10;
const numCubes = cubesAcross * cubesDown;
// matrix per instance
const matrixData = new Float32Array(16 * numCubes);
const matrices = [];
const instanceIds = new Float32Array(numCubes);
for (let i = 0; i < numCubes; ++i)
instanceIds[i] = i;
// make a typedarray view for each matrix
matrices.push(matrixData.subarray(i * 16, (i + 1) * 16));


const arrays =
position: [
1, 1, -1,
1, 1, 1,
1, -1, 1,
1, -1, -1,

-1, 1, 1,
-1, 1, -1,
-1, -1, -1,
-1, -1, 1,

-1, 1, 1,
1, 1, 1,
1, 1, -1,
-1, 1, -1,

-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,

1, 1, 1,
-1, 1, 1,
-1, -1, 1,
1, -1, 1,

-1, 1, -1,
1, 1, -1,
1, -1, -1,
-1, -1, -1,
],
texcoord: [
1, 0,
0, 0,
0, 1,
1, 1,

1, 0,
0, 0,
0, 1,
1, 1,

0, 1,
1, 1,
1, 0,
0, 0,

0, 1,
1, 1,
1, 0,
0, 0,

1, 1,
0, 1,
0, 0,
1, 0,

1, 0,
0, 0,
0, 1,
1, 1,
],
indices: [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23,
],
instanceId: numComponents: 1, data: instanceIds, divisor: 1 ,
matrix: numComponents: 16, data: matrices, divisor: 1 ,
;
// create buffers, upload data (gl.bufferData)
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

function render(time) gl.DEPTH_BUFFER_BIT);

const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 24, 76];
const target = [18, 10, 0];
const up = [0, 1, 0];

const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);

// update the instance for each matrix
const spacing = 2.5 + Math.sin(time) * .5;
let i = 0;
for (let y = 0; y < cubesDown; ++y)
for (let x = 0; x < cubesAcross; ++x)
const matrix = matrices[i++];
m4.translate(viewProjection, [
x * spacing,
y * spacing,
0,
], matrix);



gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer, ext.vertexAttribDivisorANGLE
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo,
cubesAcross,
cubesDown,
);

// upload instance matrices to buffer
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.matrix.buffer);
gl.bufferData(gl.ARRAY_BUFFER, matrixData, gl.DYNAMIC_DRAW);

ext.drawElementsInstancedANGLE(
gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0, numCubes);

requestAnimationFrame(render);

requestAnimationFrame(render);


main();

body 
margin: 0;


canvas
width: 100vw;
height: 100vh;
display: block;

<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>





If you actually want unique per vertex per instance data then put it in a texture and pass in a vertexId as well. Then use both vertexId and instanceId to compute a texture coordinate



 attribute float instanceId;
attribute float vertexId;

uniform sampler2D dataTexture;
uniform vec2 dataTextureSize;

varying vec2 v_texcoord;

void main() {

// each row is for an instance, each texel
// per vertex. Of course if you want more data
// per vertex then multiply vertexId by the number of
// vec4s of data you need. If you need more instances
// then compute a more complex offset based off instanceId
vec2 uv = (vec2(vertexId, instanceId) + .5) / dataTextureSize;
vec4 data = texture2D(dataTexture, uv);

v_texcoord = data.xy;





"use strict";

function main()
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const ext1 = gl.getExtension('OES_texture_float');
if (!ext1)
return alert('need OES_texture_float');

const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext)
return alert('need ANGLE_instanced_arrays');

twgl.addExtensionsToContext(gl);
const vs = `
attribute vec4 position;
attribute mat4 matrix;
attribute float instanceId;
attribute float vertexId;

uniform sampler2D dataTexture;
uniform vec2 dataTextureSize;

varying vec2 v_texcoord;

void main()
gl_Position = matrix * position;

// each row is for an instance, each texel
// per vertex. Of course if you want more data
// per vertex then multiply vertexId by the number of
// vec4s of data you need. If you need more instances
// then compute a more complex offset based off instanceId
vec2 uv = (vec2(vertexId, instanceId) + .5) / dataTextureSize;
vec4 data = texture2D(dataTexture, uv);

v_texcoord = data.xy;

`;

const fs = `
precision mediump float;

varying vec2 v_texcoord;

void main()
gl_FragColor = vec4(v_texcoord, 0, 1);

`;

// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

const cubesAcross = 20;
const cubesDown = 10;
const numCubes = cubesAcross * cubesDown;
// matrix per instance
const matrixData = new Float32Array(16 * numCubes);
const matrices = [];
const instanceIds = new Float32Array(numCubes);
for (let i = 0; i < numCubes; ++i)
instanceIds[i] = i;
// make a typedarray view for each matrix
matrices.push(matrixData.subarray(i * 16, (i + 1) * 16));


const arrays =
position: [
1, 1, -1,
1, 1, 1,
1, -1, 1,
1, -1, -1,

-1, 1, 1,
-1, 1, -1,
-1, -1, -1,
-1, -1, 1,

-1, 1, 1,
1, 1, 1,
1, 1, -1,
-1, 1, -1,

-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,

1, 1, 1,
-1, 1, 1,
-1, -1, 1,
1, -1, 1,

-1, 1, -1,
1, 1, -1,
1, -1, -1,
-1, -1, -1,
],
vertexId:
numComponents: 1,
data: [
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15,
16, 17, 18, 19,
20, 21, 21, 23,
],
,
indices: [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23,
],
instanceId: numComponents: 1, data: instanceIds, divisor: 1 ,
matrix: numComponents: 16, data: matrices, divisor: 1 ,
;
// create buffers, upload data (gl.bufferData)
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

// put UV data in texture
const uvs = [];
for (let y = 0; y < cubesDown; ++y)
const v0 = (y ) / cubesDown;
const v1 = (y + 1) / cubesDown;
for (let x = 0; x < cubesAcross; ++x)
const u0 = (x ) / cubesAcross;
const u1 = (x + 1) / cubesAcross;
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);



const texWidth = 24; // width = 24 vertices * 1 texel per
const texHeight = numCubes; // height = numInstances
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(
gl.TEXTURE_2D,
0, // level
gl.RGBA,
texWidth,
texHeight,
0, // border
gl.RGBA,
gl.FLOAT,
new Float32Array(uvs));


function render(time) gl.DEPTH_BUFFER_BIT);

const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 24, 76];
const target = [18, 10, 0];
const up = [0, 1, 0];

const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);

// update the instance for each matrix
const spacing = 2.5 + Math.sin(time) * .5;
let i = 0;
for (let y = 0; y < cubesDown; ++y)
for (let x = 0; x < cubesAcross; ++x)
const matrix = matrices[i++];
m4.translate(viewProjection, [
x * spacing,
y * spacing,
0,
], matrix);



gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer, ext.vertexAttribDivisorANGLE
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo,
dataTexture: tex,
dataTextureSize: [texWidth, texHeight],
);

// upload instance matrices to buffer
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.matrix.buffer);
gl.bufferData(gl.ARRAY_BUFFER, matrixData, gl.DYNAMIC_DRAW);

ext.drawElementsInstancedANGLE(
gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0, numCubes);

requestAnimationFrame(render);

requestAnimationFrame(render);


main();

body 
margin: 0;


canvas
width: 100vw;
height: 100vh;
display: block;

<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>








"use strict";

function main()
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext)
return alert('need ANGLE_instanced_arrays');

twgl.addExtensionsToContext(gl);
const vs = `
attribute vec4 position;
attribute vec2 texcoord;
attribute mat4 matrix;
attribute float instanceId;

uniform float cubesAcross; // set to 20
uniform float cubesDown; // set to 10

varying vec2 v_texcoord;

void main()
gl_Position = matrix * position;

float cubeX = mod(instanceId, cubesAcross);
float cubeY = floor(instanceId / cubesAcross);

v_texcoord = (vec2(cubeX, cubeY) + texcoord) / vec2(cubesAcross, cubesDown);

`;

const fs = `
precision mediump float;

varying vec2 v_texcoord;

void main()
gl_FragColor = vec4(v_texcoord, 0, 1);

`;

// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

const cubesAcross = 20;
const cubesDown = 10;
const numCubes = cubesAcross * cubesDown;
// matrix per instance
const matrixData = new Float32Array(16 * numCubes);
const matrices = [];
const instanceIds = new Float32Array(numCubes);
for (let i = 0; i < numCubes; ++i)
instanceIds[i] = i;
// make a typedarray view for each matrix
matrices.push(matrixData.subarray(i * 16, (i + 1) * 16));


const arrays =
position: [
1, 1, -1,
1, 1, 1,
1, -1, 1,
1, -1, -1,

-1, 1, 1,
-1, 1, -1,
-1, -1, -1,
-1, -1, 1,

-1, 1, 1,
1, 1, 1,
1, 1, -1,
-1, 1, -1,

-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,

1, 1, 1,
-1, 1, 1,
-1, -1, 1,
1, -1, 1,

-1, 1, -1,
1, 1, -1,
1, -1, -1,
-1, -1, -1,
],
texcoord: [
1, 0,
0, 0,
0, 1,
1, 1,

1, 0,
0, 0,
0, 1,
1, 1,

0, 1,
1, 1,
1, 0,
0, 0,

0, 1,
1, 1,
1, 0,
0, 0,

1, 1,
0, 1,
0, 0,
1, 0,

1, 0,
0, 0,
0, 1,
1, 1,
],
indices: [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23,
],
instanceId: numComponents: 1, data: instanceIds, divisor: 1 ,
matrix: numComponents: 16, data: matrices, divisor: 1 ,
;
// create buffers, upload data (gl.bufferData)
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

function render(time) gl.DEPTH_BUFFER_BIT);

const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 24, 76];
const target = [18, 10, 0];
const up = [0, 1, 0];

const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);

// update the instance for each matrix
const spacing = 2.5 + Math.sin(time) * .5;
let i = 0;
for (let y = 0; y < cubesDown; ++y)
for (let x = 0; x < cubesAcross; ++x)
const matrix = matrices[i++];
m4.translate(viewProjection, [
x * spacing,
y * spacing,
0,
], matrix);



gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer, ext.vertexAttribDivisorANGLE
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo,
cubesAcross,
cubesDown,
);

// upload instance matrices to buffer
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.matrix.buffer);
gl.bufferData(gl.ARRAY_BUFFER, matrixData, gl.DYNAMIC_DRAW);

ext.drawElementsInstancedANGLE(
gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0, numCubes);

requestAnimationFrame(render);

requestAnimationFrame(render);


main();

body 
margin: 0;


canvas
width: 100vw;
height: 100vh;
display: block;

<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>





"use strict";

function main()
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext)
return alert('need ANGLE_instanced_arrays');

twgl.addExtensionsToContext(gl);
const vs = `
attribute vec4 position;
attribute vec2 texcoord;
attribute mat4 matrix;
attribute float instanceId;

uniform float cubesAcross; // set to 20
uniform float cubesDown; // set to 10

varying vec2 v_texcoord;

void main()
gl_Position = matrix * position;

float cubeX = mod(instanceId, cubesAcross);
float cubeY = floor(instanceId / cubesAcross);

v_texcoord = (vec2(cubeX, cubeY) + texcoord) / vec2(cubesAcross, cubesDown);

`;

const fs = `
precision mediump float;

varying vec2 v_texcoord;

void main()
gl_FragColor = vec4(v_texcoord, 0, 1);

`;

// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

const cubesAcross = 20;
const cubesDown = 10;
const numCubes = cubesAcross * cubesDown;
// matrix per instance
const matrixData = new Float32Array(16 * numCubes);
const matrices = [];
const instanceIds = new Float32Array(numCubes);
for (let i = 0; i < numCubes; ++i)
instanceIds[i] = i;
// make a typedarray view for each matrix
matrices.push(matrixData.subarray(i * 16, (i + 1) * 16));


const arrays =
position: [
1, 1, -1,
1, 1, 1,
1, -1, 1,
1, -1, -1,

-1, 1, 1,
-1, 1, -1,
-1, -1, -1,
-1, -1, 1,

-1, 1, 1,
1, 1, 1,
1, 1, -1,
-1, 1, -1,

-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,

1, 1, 1,
-1, 1, 1,
-1, -1, 1,
1, -1, 1,

-1, 1, -1,
1, 1, -1,
1, -1, -1,
-1, -1, -1,
],
texcoord: [
1, 0,
0, 0,
0, 1,
1, 1,

1, 0,
0, 0,
0, 1,
1, 1,

0, 1,
1, 1,
1, 0,
0, 0,

0, 1,
1, 1,
1, 0,
0, 0,

1, 1,
0, 1,
0, 0,
1, 0,

1, 0,
0, 0,
0, 1,
1, 1,
],
indices: [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23,
],
instanceId: numComponents: 1, data: instanceIds, divisor: 1 ,
matrix: numComponents: 16, data: matrices, divisor: 1 ,
;
// create buffers, upload data (gl.bufferData)
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

function render(time) gl.DEPTH_BUFFER_BIT);

const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 24, 76];
const target = [18, 10, 0];
const up = [0, 1, 0];

const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);

// update the instance for each matrix
const spacing = 2.5 + Math.sin(time) * .5;
let i = 0;
for (let y = 0; y < cubesDown; ++y)
for (let x = 0; x < cubesAcross; ++x)
const matrix = matrices[i++];
m4.translate(viewProjection, [
x * spacing,
y * spacing,
0,
], matrix);



gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer, ext.vertexAttribDivisorANGLE
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo,
cubesAcross,
cubesDown,
);

// upload instance matrices to buffer
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.matrix.buffer);
gl.bufferData(gl.ARRAY_BUFFER, matrixData, gl.DYNAMIC_DRAW);

ext.drawElementsInstancedANGLE(
gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0, numCubes);

requestAnimationFrame(render);

requestAnimationFrame(render);


main();

body 
margin: 0;


canvas
width: 100vw;
height: 100vh;
display: block;

<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>





"use strict";

function main()
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const ext1 = gl.getExtension('OES_texture_float');
if (!ext1)
return alert('need OES_texture_float');

const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext)
return alert('need ANGLE_instanced_arrays');

twgl.addExtensionsToContext(gl);
const vs = `
attribute vec4 position;
attribute mat4 matrix;
attribute float instanceId;
attribute float vertexId;

uniform sampler2D dataTexture;
uniform vec2 dataTextureSize;

varying vec2 v_texcoord;

void main()
gl_Position = matrix * position;

// each row is for an instance, each texel
// per vertex. Of course if you want more data
// per vertex then multiply vertexId by the number of
// vec4s of data you need. If you need more instances
// then compute a more complex offset based off instanceId
vec2 uv = (vec2(vertexId, instanceId) + .5) / dataTextureSize;
vec4 data = texture2D(dataTexture, uv);

v_texcoord = data.xy;

`;

const fs = `
precision mediump float;

varying vec2 v_texcoord;

void main()
gl_FragColor = vec4(v_texcoord, 0, 1);

`;

// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

const cubesAcross = 20;
const cubesDown = 10;
const numCubes = cubesAcross * cubesDown;
// matrix per instance
const matrixData = new Float32Array(16 * numCubes);
const matrices = [];
const instanceIds = new Float32Array(numCubes);
for (let i = 0; i < numCubes; ++i)
instanceIds[i] = i;
// make a typedarray view for each matrix
matrices.push(matrixData.subarray(i * 16, (i + 1) * 16));


const arrays =
position: [
1, 1, -1,
1, 1, 1,
1, -1, 1,
1, -1, -1,

-1, 1, 1,
-1, 1, -1,
-1, -1, -1,
-1, -1, 1,

-1, 1, 1,
1, 1, 1,
1, 1, -1,
-1, 1, -1,

-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,

1, 1, 1,
-1, 1, 1,
-1, -1, 1,
1, -1, 1,

-1, 1, -1,
1, 1, -1,
1, -1, -1,
-1, -1, -1,
],
vertexId:
numComponents: 1,
data: [
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15,
16, 17, 18, 19,
20, 21, 21, 23,
],
,
indices: [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23,
],
instanceId: numComponents: 1, data: instanceIds, divisor: 1 ,
matrix: numComponents: 16, data: matrices, divisor: 1 ,
;
// create buffers, upload data (gl.bufferData)
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

// put UV data in texture
const uvs = [];
for (let y = 0; y < cubesDown; ++y)
const v0 = (y ) / cubesDown;
const v1 = (y + 1) / cubesDown;
for (let x = 0; x < cubesAcross; ++x)
const u0 = (x ) / cubesAcross;
const u1 = (x + 1) / cubesAcross;
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);



const texWidth = 24; // width = 24 vertices * 1 texel per
const texHeight = numCubes; // height = numInstances
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(
gl.TEXTURE_2D,
0, // level
gl.RGBA,
texWidth,
texHeight,
0, // border
gl.RGBA,
gl.FLOAT,
new Float32Array(uvs));


function render(time) gl.DEPTH_BUFFER_BIT);

const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 24, 76];
const target = [18, 10, 0];
const up = [0, 1, 0];

const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);

// update the instance for each matrix
const spacing = 2.5 + Math.sin(time) * .5;
let i = 0;
for (let y = 0; y < cubesDown; ++y)
for (let x = 0; x < cubesAcross; ++x)
const matrix = matrices[i++];
m4.translate(viewProjection, [
x * spacing,
y * spacing,
0,
], matrix);



gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer, ext.vertexAttribDivisorANGLE
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo,
dataTexture: tex,
dataTextureSize: [texWidth, texHeight],
);

// upload instance matrices to buffer
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.matrix.buffer);
gl.bufferData(gl.ARRAY_BUFFER, matrixData, gl.DYNAMIC_DRAW);

ext.drawElementsInstancedANGLE(
gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0, numCubes);

requestAnimationFrame(render);

requestAnimationFrame(render);


main();

body 
margin: 0;


canvas
width: 100vw;
height: 100vh;
display: block;

<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>





"use strict";

function main()
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const ext1 = gl.getExtension('OES_texture_float');
if (!ext1)
return alert('need OES_texture_float');

const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext)
return alert('need ANGLE_instanced_arrays');

twgl.addExtensionsToContext(gl);
const vs = `
attribute vec4 position;
attribute mat4 matrix;
attribute float instanceId;
attribute float vertexId;

uniform sampler2D dataTexture;
uniform vec2 dataTextureSize;

varying vec2 v_texcoord;

void main()
gl_Position = matrix * position;

// each row is for an instance, each texel
// per vertex. Of course if you want more data
// per vertex then multiply vertexId by the number of
// vec4s of data you need. If you need more instances
// then compute a more complex offset based off instanceId
vec2 uv = (vec2(vertexId, instanceId) + .5) / dataTextureSize;
vec4 data = texture2D(dataTexture, uv);

v_texcoord = data.xy;

`;

const fs = `
precision mediump float;

varying vec2 v_texcoord;

void main()
gl_FragColor = vec4(v_texcoord, 0, 1);

`;

// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

const cubesAcross = 20;
const cubesDown = 10;
const numCubes = cubesAcross * cubesDown;
// matrix per instance
const matrixData = new Float32Array(16 * numCubes);
const matrices = [];
const instanceIds = new Float32Array(numCubes);
for (let i = 0; i < numCubes; ++i)
instanceIds[i] = i;
// make a typedarray view for each matrix
matrices.push(matrixData.subarray(i * 16, (i + 1) * 16));


const arrays =
position: [
1, 1, -1,
1, 1, 1,
1, -1, 1,
1, -1, -1,

-1, 1, 1,
-1, 1, -1,
-1, -1, -1,
-1, -1, 1,

-1, 1, 1,
1, 1, 1,
1, 1, -1,
-1, 1, -1,

-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,

1, 1, 1,
-1, 1, 1,
-1, -1, 1,
1, -1, 1,

-1, 1, -1,
1, 1, -1,
1, -1, -1,
-1, -1, -1,
],
vertexId:
numComponents: 1,
data: [
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15,
16, 17, 18, 19,
20, 21, 21, 23,
],
,
indices: [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23,
],
instanceId: numComponents: 1, data: instanceIds, divisor: 1 ,
matrix: numComponents: 16, data: matrices, divisor: 1 ,
;
// create buffers, upload data (gl.bufferData)
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

// put UV data in texture
const uvs = [];
for (let y = 0; y < cubesDown; ++y)
const v0 = (y ) / cubesDown;
const v1 = (y + 1) / cubesDown;
for (let x = 0; x < cubesAcross; ++x)
const u0 = (x ) / cubesAcross;
const u1 = (x + 1) / cubesAcross;
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);



const texWidth = 24; // width = 24 vertices * 1 texel per
const texHeight = numCubes; // height = numInstances
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(
gl.TEXTURE_2D,
0, // level
gl.RGBA,
texWidth,
texHeight,
0, // border
gl.RGBA,
gl.FLOAT,
new Float32Array(uvs));


function render(time) gl.DEPTH_BUFFER_BIT);

const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 24, 76];
const target = [18, 10, 0];
const up = [0, 1, 0];

const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);

// update the instance for each matrix
const spacing = 2.5 + Math.sin(time) * .5;
let i = 0;
for (let y = 0; y < cubesDown; ++y)
for (let x = 0; x < cubesAcross; ++x)
const matrix = matrices[i++];
m4.translate(viewProjection, [
x * spacing,
y * spacing,
0,
], matrix);



gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer, ext.vertexAttribDivisorANGLE
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo,
dataTexture: tex,
dataTextureSize: [texWidth, texHeight],
);

// upload instance matrices to buffer
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.matrix.buffer);
gl.bufferData(gl.ARRAY_BUFFER, matrixData, gl.DYNAMIC_DRAW);

ext.drawElementsInstancedANGLE(
gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0, numCubes);

requestAnimationFrame(render);

requestAnimationFrame(render);


main();

body 
margin: 0;


canvas
width: 100vw;
height: 100vh;
display: block;

<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>






share|improve this answer














share|improve this answer



share|improve this answer








edited Mar 24 at 16:31

























answered Mar 24 at 5:43









gmangman

50.8k17119214




50.8k17119214












  • Thanks for the help and many thanks for the detailed sample! Figured it would have to end up something along these lines if I didn't just re-render things.

    – sortofsleepy
    Mar 24 at 22:51

















  • Thanks for the help and many thanks for the detailed sample! Figured it would have to end up something along these lines if I didn't just re-render things.

    – sortofsleepy
    Mar 24 at 22:51
















Thanks for the help and many thanks for the detailed sample! Figured it would have to end up something along these lines if I didn't just re-render things.

– sortofsleepy
Mar 24 at 22:51





Thanks for the help and many thanks for the detailed sample! Figured it would have to end up something along these lines if I didn't just re-render things.

– sortofsleepy
Mar 24 at 22:51



















draft saved

draft discarded
















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55319323%2fin-webgl-when-instancing-geometry-is-it-possible-to-pass-per-vertex-attribute%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Kamusi Yaliyomo Aina za kamusi | Muundo wa kamusi | Faida za kamusi | Dhima ya picha katika kamusi | Marejeo | Tazama pia | Viungo vya nje | UrambazajiKuhusu kamusiGo-SwahiliWiki-KamusiKamusi ya Kiswahili na Kiingerezakuihariri na kuongeza habari

Swift 4 - func physicsWorld not invoked on collision? The Next CEO of Stack OverflowHow to call Objective-C code from Swift#ifdef replacement in the Swift language@selector() in Swift?#pragma mark in Swift?Swift for loop: for index, element in array?dispatch_after - GCD in Swift?Swift Beta performance: sorting arraysSplit a String into an array in Swift?The use of Swift 3 @objc inference in Swift 4 mode is deprecated?How to optimize UITableViewCell, because my UITableView lags

Access current req object everywhere in Node.js ExpressWhy are global variables considered bad practice? (node.js)Using req & res across functionsHow do I get the path to the current script with Node.js?What is Node.js' Connect, Express and “middleware”?Node.js w/ express error handling in callbackHow to access the GET parameters after “?” in Express?Modify Node.js req object parametersAccess “app” variable inside of ExpressJS/ConnectJS middleware?Node.js Express app - request objectAngular Http Module considered middleware?Session variables in ExpressJSAdd properties to the req object in expressjs with Typescript