InterleavedBuffer stride and offset units

Can i use different types when using a single InstancedBuffer by providing an ArrayBuffer?

I also don’t understand which units are the offsets and strides supposed to be. Is it in bytes?

Currently InterleavedBuffer is contrained to interleave vertex attributes of the same type (f32 or uint16 or uint8 or …), see Improving BufferAttribute (maybe) · Issue #17089 · mrdoob/three.js · GitHub. I’d like for that API to become more flexible.

The InterleavedBuffer#stride and InterleavedBufferAttribute#offset properties are both expressed in elements. For example, suppose you have two vec3, float32 vertex attributes interleaved. The stride of the buffer is 6 (equivalent to 6 * 4 = 24 bytes), the offset of the first attribute is 0, and the offset of the second attribute is 3 (equivalent to 3 * 4 = 12 bytes).

1 Like

Im having some trouble updating the offset. Do you happen to know how this should be done? I’m trying to mutate the InterleavedBufferAttribute by setting it’s .offset but it doesn’t seem to be picked up.

I want to have one geometry, that I render several times with different instance counts and hopefully offsets, but I’m having trouble setting the offsets.

Hm, I don’t know whether the offsets can be updated after the geometry is uploaded.Do the offsets seem to be working only for initialization and not after that?

I’m trying to isolate some code, i made a little sandbox.

The image im generating is this:

And the code is something like this:


const instanceAttribute = new InstancedBufferAttribute(
  new Float32Array(positions),
  3
);
const instanceInterleaved = new InstancedInterleavedBuffer(
  new Float32Array(positions),
  3
);
const iiAttribute = new InterleavedBufferAttribute(instanceInterleaved, 3, 0);

const instancedGeometry0 = new InstancedBufferGeometry();
instancedGeometry0.instanceCount = 10;
instancedGeometry0.setIndex(pg.index);
instancedGeometry0.setAttribute("position", pg.attributes.position);
instancedGeometry0.setAttribute("aInstancePosition", iiAttribute);

const instancedGeometry1 = new InstancedBufferGeometry();
instancedGeometry1.instanceCount = 40;
instancedGeometry1.setIndex(pg.index);
instancedGeometry1.setAttribute("position", pg.attributes.position);
instancedGeometry1.setAttribute("aInstancePosition", iiAttribute);
const multiGeom = new Scene();

color.setStyle("#ff0000");
const m0 = new Mesh(instancedGeometry0, material.clone());
m0.renderOrder = 0;
multiGeom.add(m0);
m0.onBeforeRender = () => {
  iiAttribute.offset = 0;
};

color.setStyle("#ffff00");
const m1 = new Mesh(instancedGeometry1, material.clone());
m1.renderOrder = 1;
multiGeom.add(m1);
m1.onBeforeRender = () => {
  iiAttribute.offset = 10 * 3;
};

Using the interleaved attributes does give me the parameters i am looking for, naimly offset in addition to the stride.

So i generate an array of 10x10 points, and make an instanced buffer out of it. What im trying to achieve is to render several passes using the same geometry but different ranges. I can update the instance count in onBeforeRender for example, and draw N instances. However, in order to tell the next pass to start drawing after a certain instance, it seems that i need to generate new pointers.

Looking at the code, it seems that the offsets are only honored when using the InterleavedBufferAttribute

I was able to change this in setupVertexAttributes:

						const MY_TEMP_OFFSET_FROM_INSTANCED_ATTRIBUTE = geometryAttribute.offset ?? 0
						for ( let i = 0; i < programAttribute.locationSize; i ++ ) {
							console.log('i',i)
							vertexAttribPointer(
								programAttribute.location + i,
								size / programAttribute.locationSize,
								type,
								normalized,
								size * bytesPerElement,
								(MY_TEMP_OFFSET_FROM_INSTANCED_ATTRIBUTE+( size / programAttribute.locationSize ) * i )* bytesPerElement
							);

						}

Which allowed the InstancedBufferAttribute to use offsets as well.
However, i don’t think i need this, i think the InterleavedBufferAttribute would suffice, but it seems to only be working if i have unique different instances of InstancedBufferGeometry.

As you can see, my callbacks are:

m0.onBeforeRender = () => {
  iiAttribute.offset = 0;
};
m1.onBeforeRender = () => {
  iiAttribute.offset = 10 * 3;
};

So i’m modifying the same InterleavedBufferAttributes offset, but it only works if this attribute is under a different geometry. If it’s the same geometry nothing seems to happen, there’s some caching in the renderer and the needsUpdate never gets triggered.

Oddly, i think when i tried to do needsUpdate i got the bufferData call, so the actual numbers got uploaded, but it still did not pick the offset.

I’m also not sure if i can dispose of the geometry, since i saw some delete calls in the renderer. It feels almost that there is something wrong with the needsUpdate on the interleaved attribute and the interleaved buffer, one should update the data, the other should possibly distinguish between these different pointers.