TSL computeshader does not .assign to storage buffer

Pretty sure I am doing something wrong or there are some API changes but my compute function does not assign to the storage buffer.

const count = 10
const colArray = new THREE.StorageInstancedBufferAttribute(new Float32Array(count * 3), 3);
scene.children[0].children[0].instanceColor = colArray;
for (let i = 0; i < count; i++) {
                colArray.array.set([1,0,0],i*3);
            }
//at this point all instances are red

const computeColor = Fn(() => {
        const instanceColor = storage( colArray, 'vec3', count );
        instanceColor.element(instanceIndex).assign(vec3(0.0, 1.0, 0.0))
    })().compute(count)

    const { gl } = useThree()
    gl.compute(computeColor)
// ^^ this is basically renderer.compute(computeColor), just R3F

console.log(colArray)

//at this point all instances are still red

Any idea why?

Neither using instancedArray as in this example nor StorageInstancedBufferAttribute from this comment seem to work.

I’ve managed to force an error somehow, maybe anyone encountered something similar?

ERROR: 0:142: 'assign' : l-value required (can't modify an input "nodeVarying5")

Disappears when the assign line in the shader is commented.

To read the array back from the GPU, use renderer.getArrayBufferAsync. Both renderer.compute and renderer.getArrayBufferAsync are asynchronous, so you’ll need to await them to get the resulting buffer.

The following code worked for me. Here’s a working example:

renderer
    .compute(computeColor)
    .then(() => renderer.getArrayBufferAsync(instanceColor.value))
    .then((arrayBuffer) => {
        const computedColorArray = new Float32Array(arrayBuffer);
        console.log(computedColorArray);
    })

or if you prefer using await:

await renderer.compute(computeColor);
  
const arrayBuffer = await renderer.getArrayBufferAsync(instanceColor.value);
const computedColorArray = new Float32Array(arrayBuffer);
console.log(computedColorArray);
4 Likes

Thank you a lot! This makes sense.

1 Like

EDIT: Ignore this, it seems to be something to do with how R3F accesses the renderer, not the three.js itself.

EDIT2: for anyone using fiber, do not

const { gl } = useThree();
useFrame(() => {
    gl.computeAsync(...) }

but grab renderer properly with

useFrame(({ gl }) => {
    gl.computeAsync(...) }

Or getArrayBufferAsync will return zeroes after the first frame.

1 Like