
Improve your Threejs performances with BufferGeometryUtils
In this tutorial we will see how to improve our Threejs performances. We assume we already know Webgl and Threejs.
Threejs optimizations
Hello, today we are going to see how to optimize our Threejs performances on scenes with multiple objects.
To resume, performances in Threejs are based on the amount of draw/frame, the complexity and the size of the geometries, the type of materials used, the lights, antialias, and the amount of matrix manipulations. (Try to avoid using post render effects, most of the time you can avoid it. I mean, render the scene on the screen multiples times/frame, seriously ?).
Luigi De Rosa made an interesting article about rendering the whole scene in a texture, then rendering it in the screen with a custom mesh, shader and antialias : Simple postprocessing in three.js.
I love his approach, when I have more time I will benchmark the performances to know how cool his logic is.
As you can see there are many ways to improve your performances, in this article we will see the first one : reduce the amount of draw per frame.
We want to create a cool 3D particles background for our website. Typically, we would add 10 000 Mesh in our scene and animate all of them.
Then animate them this way :
We can access to the stress test code on this link but I won’t embed it on this tutorial for obvious reason. ← Your computer would die unless you got an Nvidia GeForce RTX 2080 Ti, like 95% of the “web creative developer” on internet it seems :winky face: :
https://jsfiddle.net/onirenaud/2mgjbc1q/
With this code we are drawing 9000+ times per frame on our canvas. We can debug this value easily using renderer.info.render.calls in our update method.
First of all, we need to know that Geometry is user-friendly and easy to read, but not performances friendly. We would always prefer BufferGeometry when we dev. I couldn’t say it better than the threejs definition, so here it is : “BufferGeometry is an efficient representation of mesh, line, or point geometry. Includes vertex positions, face indices, normals, colors, UVs, and custom attributes within buffers, reducing the cost of passing all this data to the GPU.”
But do not worry, converting Geometry into BufferGeometry is that easy :
Now that we got our Geometry converted into a Buffergeometry we can start to do cool stuffs.
BufferGeometryUtils class
The BufferGeometryUtils class contains multiples utilities functions for BufferGeometry instances. What we need for this tutorial is the mergeBufferGeometries method.
MergeBufferGeometries put every Buffergeometries with the sames attributes together. Basically, if we use the same material twice in our scene we need to use it.
Here is the code explaining how to apply this method on our example :
Now we just got 1 mesh to update :
As a final result :
We did it, 10 000 elements, animated at 60 fps.
Now lets talk about the only weird point with this logic.
A translation on an Object3D moves the position, A translation on a Geometry moves the origin
As we saw in the first example we didn’t apply the translation on the mesh, but on the geometry. This is the only complex stuff to remember in this logic. If we rotate our geometry it will rotate around the origin 0.0.0 of the initially cloned Geometry and not on itself. I never had to face the case where I want to rotate every object on themselves but with some Math it shouldn’t be difficult but I don’t want to write huge articles uselessly. If this is requested I might do it.
As I said in the intro, this is only a small tip on how to optimize your ThreeJS performances, there are many more ways. If you are interested about the others optimizations, hit me on twitter @onirenaud.