Vertex Buffer Animations

Make good use of the GPU to handle your animations in the vertex shader

Animating geometries in JavaScript can be pretty slow. If you have thousands of objects's positions and try to update them at once, or even worse, if you try to update all the vertices of a geometry, your GPU will struggle to receive so much new information. The GPU is best at doing the math, not at receiving the data.

To make better use of our GPU, we can turn to the vertex shaders and store any animation data into custom Buffer Attributes at the geometry's creation. Then, the GPU takes care of the animating in the shaders without the need to take new updates.

These custom InstancedBufferAttributes are the same as the usual position, uv, normals, and attributes. Large arrays with data per vertex that you can use to calculate the animation of each vertex. Or, with the right data, animate each triangle, or even each breakable piece.

Getting started

sponsor

Triangle Image Transition

For the animation timing in this demo by Szenia, there are two buffers: the startTime, and endtime buffers. Vertices are animated as triangles by using the centroid to calculate the timing, however, each vertex adds a little bit of delay to create a little bit of stretchiness.

For the movement, the vertex shader uses a bezier curve to animate the triangles from one point to another. Bezier curves have four parameters: startPosition, endPosition, control0, and control1. So, each one of those gets its buffer attribute. Then, with the timing and the

transformed += cubicBezier(aStartPosition, aControl0, aControl1, aEndPosition, tProgress);

Screenshot_2023-12-14_02-04-46.png

Breaking up text

If you don't need to squeeze every single drop of performance, doing things in JavaScript might be just enough for your use case. In this project, visualdata creates text geometry that is then broken with ThreeJS ConvexBufferGeometry. Then the resulting meshes are animated in the update loop.

Screenshot_2023-12-14_02-04-19.png

Model morph

In this model morph animation, they animate they animate the triangles to morph between models. To make sure triangles sort of stick together, you need to link them in some sort of way. My go-to approach is with the triangle's centroid. All three vertices share the same centroid, so we can use that for the timing:

Getting the centroid of a triangle is summing all the vertices and then diving by 3

centroid.copy(point0)
centroid.add(point1)
centroid.add(point2)
centroid.divide(3)

Screenshot_2023-12-14_02-04-03.png

Triangle transitions

Here's an old but gold example that uses the same centroid technique. This uses Three.BAS to be able to add new attributes while still being able to work with built-in materials.

Screenshot_2023-12-14_02-03-19.png

Instancing geometry morph

Another approach for vertex shader animation is using textures instead of buffers like Unseen Studio's Dala project. The animation data is either encoded in a texture, or the final animation is encoded into the texture, like in the case for gpgpu simulations. This texture is sampled in the shader to animate each instance or vertex.

Screenshot_2023-12-14_02-01-29.png

Further reading / Inspiration