Animating vertices of buffergeometry

This was quite straightforward on Geometry, but seems v difficult on BufferGeometry. I am trying to animate the vertices on a low budget river and make the surface modulate a bit.


                        var position = child.geometry.attributes.position;
                        var v = new THREE.Vector3();
                        console.log("river verticies:"+position.count);
                        for ( var i = 0; i<position.count; i ++ ){
                           v.fromBufferAttribute( position, i );
                           v.applyMatrix4( child.matrixWorld ); //???
                           if(0){ //can we check if this is a duplicate?
                                    y:           v.y,
                                    x:           v.x,
                                    z:           v.z,
                                    ang:         0, //already put random angles into model
                                    amp:         this.river_settings.amplitude + (Math.random()*this.river_settings.amplitude),
                                    speed:       this.river_settings.speed + (Math.random()*this.river_settings.noise)

update loop:

        if(this.river && this.river_waves.length>0){
            var v = new THREE.Vector3();
            for ( var i=0;i<this.river_waves.length;i++ ){
                v.fromBufferAttribute( this.river.geometry.attributes.position, i );
                var vprops = this.river_waves[i];
                v.x = vprops.x + Math.cos(vprops.ang)*this.river_settings.amplitude;
                v.y = vprops.y + Math.sin(vprops.ang)*(this.river_settings.amplitude*1.2);
                v.z = vprops.z + Math.sin(vprops.ang)*this.river_settings.amplitude;
                vprops.ang += this.river_settings.speed;
                this.river.geometry.setAttribute( 'position', new THREE.BufferAttribute( v, 3 ) );
            this.river.geometry.attributes.position.needsUpdate = true;
  1. The river seems to be gone, assumed to be blasted off-world somewhere. Any idea where I went wrong?
  2. In blender the river has around 3k vertices however in threejs it says there is around 6k. Is this because of duplicates?

Any chance to provide an editable live code example?
And a reference pic or video of the desired result?

Here is how it looked back in the Geometry days. Quite simple and straightforward. Sadly I don’t have a public working example.

Ok, you can use noise to do such thing:


Very cool. Looks great.

Why do you need to use UV instead of just using position? I don’t quite understand that part of it.

Force of habit :slight_smile:
Feel free to use position’s x and z.

Ok rad let me try to implement that code on my end and report back. Thanks so much ( :


I’ve noticed there’s a BufferAttribute.usage field which can be set to a bunch of (seemingly undocumented) constants such as DynamicDrawUsage, StaticDrawUsage, etc. Have you made use of these? I haven’t done a lot of vertex animation myself but I’m curious if you’ve seen any tangible performance benefits from any of these flags.

I can’t say anything about pros and cons of these flags, have never used or seen the using of them.

1 Like

I implemented your code on the imported FBX. the white lines show the actual geometry of the river from the imported fbx. the blue is the river : U!/threejs-basic

Looks like you need to change z coordinate:

  if(river && river_buffer_pos){
    var v = new THREE.Vector2();
    var lastz = 0;
    //let t = clock.getElapsedTime();
    for(let i = 0; i < river_buffer_pos.count; i++){
      v.fromBufferAttribute(river_buffer_pos, i).multiplyScalar(3);
      var z = perlin.noise(v.x, v.y + t, t * 0.1) * 0.05;
      lastz = z;
      river_buffer_pos.setZ(i, z+0.1);
    river_buffer_pos.needsUpdate = true;


Thanks. Getting closer. Still looks a bit off. Any idea how to keep the lines attached to the noise animation? The second link above is editable.

I would re-use the same geometry of the river, instead of creation of a new one for lines.

    var geo = new THREE.EdgesGeometry(child.geometry);
    var linemat = new THREE.LineBasicMaterial({ 
    color:         0xffffff, 
    linewidth:     1, 
    transparent:   true,
    opacity:       0.4 
    var line = new THREE.LineSegments(geo, linemat);
    var line = new THREE.Mesh(river.geometry, new THREE.MeshBasicMaterial({color: 0xffffff, wireframe: true}));

And some more responsible values for waves:

      v.fromBufferAttribute(river_buffer_pos, i).multiplyScalar(5);
      var z = perlin.noise(v.x, v.y + t, t);
      river_buffer_pos.setZ(i, z*0.05 + 0.1);

Awesome, thanks for the help

1 Like