tslFn - how to update things after compute?

Hi, after seeing how performant the TSL is, i’ve tried to rewrite my little light baking system to it. It works fine, but i’ve found a problem when i tried to update the baked mesh. Code runs, it takes time (1M vertices over 100 lights, so it is noticeable), but it seems like none of the variables are changed or the color buffer, to which i am writing, isnt updated. Here is a minimum code to show how i am doing it and what i expected:

import * as THREE from 'three';
import { tslFn, storage, vec3, instanceIndex, uniform } from 'three/nodes';
import { oscSine, timerLocal} from 'three/nodes';

import StorageBufferAttribute from 'three/addons/renderers/common/StorageBufferAttribute.js';
function doIt( mesh, renderer ){
	const positionBaseAttribute = mesh.geometry.attributes.position;
	const colorStorageBufferAttribute = new StorageBufferAttribute(  positionBaseAttribute.count, 3 );
	mesh.geometry.setAttribute( 'color', colorStorageBufferAttribute );

	let x = uniform( Math.random() );
	const powerFn = tslFn ( ({ color } ) => {
		const colorSingle = color.element( instanceIndex );
		colorSingle.assign(vec3(oscSine(x), 0, 0));
	});

	const computedFn = powerFn ({
		color:  storage( colorStorageBufferAttribute, 'vec3', colorStorageBufferAttribute.count ),
	}).compute( colorStorageBufferAttribute.count );

	renderer.computeAsync(computedFn);
	mesh.material.vertexColors = mesh.material.needsUpdate = true;
}

I expected, that every time i run doIt function, i will fill the color buffer of mesh geometry with random shade of red. But after firing it once, i can do it to my hearts content and nothing changes, even if its recomputed every time. I’ve tried adding needsUpdate = true on geometry, buffer, and even computed function and nothing works. None of the examples on computed helped.

I’d like to mention that i’d like to avoid using NodeMaterials for now, i just want to speed up existing projects. Also i really need to pass variable from outside of tslFn and be able to update it reliably.

Am i missing something?
Thanks in advance for any help and advice

Time to answer my own question. I know now what i need to do, to get updates, but i don’t fully understand why. For anyone, who hits the same wall, here is updated code, that should work:

import * as THREE from 'three';
import { tslFn, storage, vec3, instanceIndex, uniform } from 'three/nodes';
import { oscSine, timerLocal} from 'three/nodes';

import StorageBufferAttribute from 'three/addons/renderers/common/StorageBufferAttribute.js';

const powerFn = tslFn ( ({ color, x } ) => {
	const colorSingle = color.element( instanceIndex );
	colorSingle.assign(vec3(oscSine(x), 0, 0));
});
	
function doIt( mesh, renderer ){
	const positionBaseAttribute = mesh.geometry.attributes.position;
	
	if(!mesh.userData.powerFn) {
		
		// this should be created only once in my case. 
		// It seems thath binding another StorageBufferAttribute to color makes it unaccessible, like it holds an old reference, 
		// or it needs to be explicitly updated, just like `x` below
		const colorStorageBufferAttribute = new StorageBufferAttribute(  positionBaseAttribute.count, 3 ); 
		
		mesh.geometry.setAttribute( 'color', colorStorageBufferAttribute );
		
		mesh.userData.powerFn = powerFn( {
			
			color:  storage( colorStorageBufferAttribute, 'vec3', colorStorageBufferAttribute.count ),
			// x will be updated automatically, if it's passed by reference, not value. so objects arrays and propably storage() will be updated on the go
			x: Math.random() 
			
		} );
			
	} else {
		
		// there could be better way to update things
		mesh.userData.powerFn.inputNodes.x.value = Math.random(); 
		
		// for good measure
		mesh.userData.powerFn.needsUpdate = true;
	}

	// compute every time
	renderer.computeAsync( mesh.userData.powerFn.compute( positionBaseAttribute.count ) );
	
	mesh.material.vertexColors = mesh.material.needsUpdate = true;
	
}
2 Likes