Creating thousands of animated entities in Babylon.js

RTS games were one of my favorite types of games growing up, I really enjoyed been able to control large amounts of units and thinking about strategies in real time with my friends. The idea of being able to control and entire fantasy army on an epic battle like the ones described in Lord of the Rings was always somethings that sounded very epic in my head.

Now that I’m part of the Babylon.js team, one of my first side projects was to try to use the engine to build those types of games. Of course, building an RTS is a lot of work, but by doing it in baby steps I was also able to learn some very useful things that I would like to share with you guys that might be interesting in doing something similar.

Optimizing the rendering

There are multiple ways of doing instancing in Babylon.js. With each approach one might get more performance benefits in exchange for finite control over the rendering of the entity.

No Instance: Without using any type of instancing Babylon.js will need to setup rendering from scratch during the drawing of each mesh, this means that the binding of the index and vertex data, as well as material settings may need to happen for each mesh, resulting in much more calls to the rendering API. Things like WebGPU and internal optimizations of the Babylon.js engine will try to minimize this as much as possible, but the room for optimizations is much smaller when we are not able to make any assumption on how the mesh is setup.

1000 cube meshes using mesh.instance, running at 144fps

Instances: Instances are the first level of optimization we can do when rendering multiple meshes. Instances can be created from any mesh using the createInstance method:

var newInstance = mesh.createInstance("instanceName");

Instances will have the same material as the root mesh (this is the tradeoff where the performance is coming from), but it will have their own transforms and collisions events. The following properties can be set individually for each mesh instance:

  • position
  • rotation
  • rotationQuaternion
  • setPivotMatrix
  • scaling

Due to the restriction on all meshes using the same material, instances allow us to skip many steps when rendering the meshes, it also gives the engine the clue that those meshes should be rendered together.

For more information about Instances please visit the official Babylon.js documentation: Instances | Babylon.js Documentation (babylonjs.com)

Thin Instances: Thin instances are the next level of performance improvements that can be used when rendering multiple meshes. It is way more restrictive than normal instances and we don’t even have access to a individual “ThinInstance object” to manipulate. Thin instances allow us to specify an array of properties to set for a mesh object, and an instance of that mesh will be rendered for each property on that array. This will allow the shaders to use each different value property when rendering each instance.

See more at : https://doc.babylonjs.com/features/featuresDeepDive/mesh/copies/thinInstances

343,000 thin instances of a cube running at 70fps.

The underling shader will then be able to use those property values when rendering each instance. The main buffer used when rendering thin instances is the “matrix” buffer. This buffer will define the world matrix for each instance. We also have buffers like “color” to set more properties on how those instances will be rendered.

Adding movement

Since we are using thin instances for our rendered meshes, the navmesh agents will not be directly related to our mesh. We will be manually updating the world matrix of each thin instance with the values from the nav mesh agents (See the “UpdateThinInstanceBuffers” function on the playground sample).

For more information on Babylon Mesh Navigation system please see the link bellow:

Creating A Navigation Mesh | Babylon.js Documentation (babylonjs.com)

Using navigation mesh to move thousands of thin instances.

Animation

We will Baked Texture animations to store our animation data into a texture and use the “bakedVertexAnimationSettingsInstanced” thin instance buffer to set which animation should each instance be playing.

For a more in depth explanation about Baked Texture Animation please see the following link:

Baked Texture Animations | Babylon.js Documentation (babylonjs.com)

1000 animated meshes using thin instances.

Final Result

As can be seen in the demo, 1000+ animated entities can be rendered simultaneously. This shows how the proper usage of optimization techniques available on Babylon.js can really make a difference depending on your target.

I hope you all have enjoyed our little performance adventure! If you have any questions or comments, please reach out on the Babylon.JS forum!

Sergio Ricardo Zerbetto Masson — Babylon.js Team

(14) Sergio Ricardo Zerbetto Masson (@ZerbettoMasson) / Twitter

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store