Shader not updating when changing BufferGeometry

Dear Reader,

i create a tube extrusion along a curve path with 7 vertices
the amount of vertices are dynamic.

when i load my scene, the tube is nicely colored as expected, but when i add vertices to the curve(thus adding new faces), the newly created faces stay grey, instead of white, as the rest of the initially created faces.

how do i fix this?
Thank you…

‘’
function constructTube(arrPoints, amtCurveVtx, amtCirclePoints){

bufInitTube.dispose()
bufInitTube.setFromPoints(arrPoints)

let myIndexArray = []
for (let i = 0; i < ((amtCurveVtx - 1) * amtCirclePoints); i++) {
// for (let i = 0; i < ((1) * amtCirclePoints); i++) {

	let t1 = i
	let t2 = i + 1
	let t3 = t1 + amtCirclePoints
	let t4 = t3 - 1

	myIndexArray.push(t1, t3, t2, t1, t4, t3)
}

bufInitTube.setIndex(myIndexArray)

// bufInitTube.needsUpdate = true;
// tubeMat.dispose()
// tubeMat.needsUpdate = true;
bufInitTube.computeVertexNormals()

}
‘’

‘’
tubeMat = new THREE.MeshPhongMaterial({
side: THREE.DoubleSide,
color: iColorsAll.white,
// depthTest: true,
// depthWrite: false,
flatShading: false,
// dithering: true,
// transparent: false,
// opacity: 0.7
})
// tubeMat.needsUpdate = true
‘’

‘’
function animate() {
requestAnimationFrame(animate);

    tubeMat.needsUpdate = true
    // tubeMat.update()

    controls.update();
    stats.update();

    camera.updateProjectionMatrix();
    scene.updateMatrix()
    renderer.render(scene, camera);
}
animate();

‘’

I’m not sure disposing the geometry and then increasing its size is supported, disposal is (as far as I know…) intended to be permanent.

Setting the material aside for a moment, a better pattern would be:

  1. Allocate a BufferGeometry with more vertices than you need
  2. Call geometry.setDrawRange to use the range you actually need
  3. When you need to extend the geometry, write the new data, set attribute.needsUpdate = true for each attribute modified to re-upload that attribute to the GPU, and call geometry.setDrawRange() with the new draw range.

If you eventually run out of allocated room in the BufferGeometry, dispose the original geometry and create a new one — consider the allocated size to be immutable for that geometry.

1 Like

Hi Don, thank you for sharing wisdom and time…

got rid of the dispose, i thought i need it to clean the cache of my browser
did set a huge drawrange
tried updating the position attribute, but then the scene crashes
updating the index
then updating the material.

no change in end result, am i using the needs update wrong?

it is as if the new faces are not linked with the lights…
in Maya i need to link objects to the lights when i use custom lights in my render module
this looks like similar…

do you have more ideas to share?

bufInitTube = new THREE.BufferGeometry()
bufInitTube.setDrawRange(0, 10000)
let tubeMesh = new THREE.Mesh(bufInitTube, tubeMat)
scene.add(tubeMesh)

bufInitTube.setFromPoints(arrPoints)
// bufInitTube.position.needsUpdate = true
bufInitTube.needsUpdate = true;

let myIndexArray = []
for (let i = 0; i < ((amtCurveVtx - 1) * amtCirclePoints); i++) {

	let t1 = i
	let t2 = i + 1
	let t3 = t1 + amtCirclePoints
	let t4 = t3 - 1

	myIndexArray.push(t1, t3, t2, t1, t4, t3)
}

bufInitTube.setIndex(myIndexArray)
bufInitTube.index.needsUpdate = true

bufInitTube.computeVertexNormals()
console.log(bufInitTube)

tubeMat.needsUpdate = true;

Hi Don,

updating my ass off… :wink:
i kinda tried updating everything that made a little sense to my understanding…
no success yet, i hope you or someone else knows the solution…

when i do not computeVertexNormals, the whole tube is similar grey on initial load, not nicely white anymore…

so it looks like it is not updating the vertexNormals of the newly created faces, although i compute the vertexNormals, after the new points are in the positions, and after i indexed

when i set the initial load to max curve vertices amount, all faces are always white, then it does not matter when i alter the vertices with the GUI slider, this is not ideal, because i also need initial loads of only 3 curve vertices that later need to be increased…

thank you…

bufInitTube.setFromPoints(arrPoints)

bufInitTube.drawRange.needsUpdate = true
bufInitTube.attributes.position.needsUpdate = true

bufInitTube.needsUpdate = true;
bufInitTube.elementsNeedUpdate = true;


let myIndexArray = []
for (let i = 0; i < ((amtCurveVtx - 1) * amtCirclePoints); i++) {

	let t1 = i
	let t2 = i + 1
	let t3 = t1 + amtCirclePoints
	let t4 = t3 - 1

	myIndexArray.push(t1, t3, t2, t1, t4, t3)
}

bufInitTube.setIndex(myIndexArray)
bufInitTube.computeVertexNormals()
// bufInitTube.attributes.vertexNormalsModel = true


console.log(bufInitTube)
bufInitTube.index.needsUpdate = true

tubeMesh.geometry.elementsNeedUpdate = true;
tubeMesh.material.needsUpdate = true;
console.log(tubeMesh)
// tubeMesh.attributes.uv = true;

tubeMat.needsUpdate = true;
tubeMat.elementsNeedUpdate = true;
// tubeMat.aoMapIntensity.needsUpdate = true
console.log(tubeMat)

Would it be possible to share a JSFiddle or Codepen or something like that?

Note that only geometry.attributes.position.needsUpdate = true is required to update vertex positions. If you are updating the indices then similarly, a large BufferAttribute index should be allocated and updated later, not overwritten. The draw range is defined in terms of the index. The vertex attributes and index should be created only once per BufferGeometry.

About normals, it might be easier to set material.flatShading = true and forget about normals until the rest is working.

1 Like

its a big project and do not have JSFiddle or Codepen accounts…

teamviewer meeting or skype screenshare, could be an option.
its a vite poject, in a private message i can send you a download link to the project…
also very curious what a professional thinks of my newby 3d adventure… :wink:

Since i am adding new vertices, the position update is needed i guess, i now also update the index

Good hunch you have…!!!
if flatshading is turned on, it works as it should

but the surface looks rubbish… :wink:

as long as flatshading is true, no updates needed

bufInitTube.setFromPoints(arrPoints)
// bufInitTube.attributes.position.needsUpdate = true
// bufInitTube.drawRange.needsUpdate = true
// bufInitTube.needsUpdate = true;
// bufInitTube.elementsNeedUpdate = true;

let myIndexArray = []
for (let i = 0; i < ((amtCurveVtx - 1) * amtCirclePoints); i++) {

	let t1 = i
	let t2 = i + 1
	let t3 = t1 + amtCirclePoints
	let t4 = t3 - 1

	myIndexArray.push(t1, t3, t2, t1, t4, t3)
}

bufInitTube.setIndex(myIndexArray)
// bufInitTube.index.needsUpdate = true
bufInitTube.computeVertexNormals()
console.log(bufInitTube)

// tubeMesh.geometry.elementsNeedUpdate = true;
// tubeMesh.material.needsUpdate = true;
console.log(tubeMesh)
// tubeMesh.attributes.uv = true;

// tubeMat.needsUpdate = true;
// tubeMat.elementsNeedUpdate = true;
// tubeMat.aoMapIntensity.needsUpdate = true
console.log(tubeMat)

Hi Don,

did create a jsFiddle

line 145 = flatShading
line 231 = Set initial Vertex Amount

In GUI, use VTX amount slider to adjust, and see the shader stays black when adding new faces…

thank you…
curious how this can be fixed… maybe it is a bug in THREE…???

The pattern you need to use is something like this:

// initialize geometry (one time)
const tubeGeometry = new THREE.BufferGeometry();
const tubePositions = new THREE.BufferAttribute( new Float32Array( 30000 ), 3 );
const tubeIndex = new THREE.BufferAttribute( new Uint32Array( 10000 ), 1 );
tubeGeometry.setAttribute( 'position', tubePositions );
tubeGeometry.setIndex( tubeIndex );

// update geometry (each time geometry changes)
for ( let i = 0; i < numVertices; i++ ) {
  tubePositions.setXYZ( i, x, y, z );
}
for ( let i = 0; i < numFaces; i++ ) {
  tubeIndex.setX( i, a );
  tubeIndex.setX( i, b );
  tubeIndex.setX( i, c );
}
tubePositions.needsUpdate = true;
tubeIndex.needsUpdate = true;
tubeGeometry.setDrawRange( 0, numFaces * 3 );

You must not use methods like setIndex and setFromPoints because they replace the original BufferAttributes, and this isn’t allowed after initialization. The documentation page for BufferAttribute is very important — this shows what you can do without modifying the existing BufferAttribute.

Also note that you must call setDrawRange each time the number drawn vertices changes — this is how three.js knows how many faces to draw, compared to the (much larger) number we are allocating.


It might also be easier to get this started just recreating the BufferGeometry entirely each time. Dispose the old BufferGeometry, and do not reuse any part of it. :slight_smile:

2 Likes

Hi Don,

Dont we all agree… a getXYZ( i ) function would be awesome… will it ever be reality?

Will it do the same as: Vector3.fromBufferAttribute?

1 Like

it would do exactly the same… thank you…
cheers…

1 Like

Hi Don,

after some fiddling, did get it working both ways…
line 135 you can switch between points and vectors…

any ideas on how to test it on performance???

Very nice!

Not sure what you mean by “how to test it on performance”? Unless you know what goals you’re optimizing toward, or what you’re trying to fix, it might be too soon to optimize this further I think. You may find that your application has entirely different bottlenecks, and that it’s better to invest time elsewhere.

1 Like

the tube is part of a large art/game project, coding custom dynamic shapes from scratch, later being animated by audio, in THREE first, then code plugins for Maya for some cool video’s, then build a game with the shapes in Unreal engine, Big project for this newby… currently developing the shapes in THREE, trying to keep every element as lean as possible and coding as basic as possible because later i need to write it in different coding languages, the dynamic surfaces later will have many many vertices that needs to be updated in real-time, so every gain at any element is useful… trying to keep the stats running at 60f/sec…:wink:
using the buffer is already an improvement, the tip of Pavel made the buffer coding more easy and less lines… just trying to think ahead on the performance side of the project… thank you again for pointing me in the right direction… it made my understanding of THREE under the hood expand… and improved performance… setting the drawRange took a big chunk already…
cheers…

1 Like