Optimizing performance with imported gltf models

Hi all!

I’m working on a project where I’m essentially building an editor to create tile based maps, sort of like a game board. With some helpful feedback from the forum, I’ve gotten to the point where I have an editor using InstancedMesh for each tile type and can build very large boards of tiles in a way that feels pretty performant.

I’m now working on adding character model to the scene, kind of like game pieces. Right now I’m working with some low poly gltf models. Before I get too far, however, I want to have a reasonable understanding of how I should be thinking of performance with respect to models like this. To clarify, I’m currently just using the robot model in this example as a test case: https://threejs.org/examples/?q=animation#webgl_animation_skinning_morph.

I have a general understanding of instancing from a simple mesh point of view, but it’s not too clear to me what a best practice for using multiple models like this in a scene would be. I’ve set up a service that will load the model, or, if it has already been loaded, clone it with SkeletonUtils.clone() based on this example https://threejs.org/examples/?q=animation#webgl_animation_multiple. This seems to be reusing the geometries and not creating new ones, as my geometry count stays the same, but allows them to be animated individually. However, draw calls go up with each model added and each model itself seems to require many draw calls. If I add ~15 or so, I start to drop frames.

So generally I’m wondering what best practices look like for importing models like this. I kind of have two high level questions:

  • Are models with animations generally expensive/require many draw calls each based on the model? Is there any similar concept of instancing like with instanced meshes that allows them to be animated individually?
  • Alternatively, is it possible to import the robot model and merge the meshes to a single mesh, retaining the materials and giving up the animations to drastically reduce the number of draw calls related to each model? This could potentially allow me to easily use InstancedMesh to implement multiple in the scene if I wanted to as well. Is this something that would generally happen after you import a model in Three.js, or would you export a model differently from Blender?

I’ve been searching for details around the above, and I’ve found a handful of posts on here that seem related, but I feel like I’m missing some of the general context around how this is generally handled in 3d projects to piece together an approach. I’m trying to keep performance as a fairly important aspect of this project, so I’d like to get a good handle on a holistic approach for and managing models moving forward.

Thanks for any advice!

It’s not yet possible to use THREE.InstancedMesh with animated models. You would have to develop something on your own, wait until this feature lands in the official repository or use an external third-party plugin/extension if available.

Unfortunately no, since an instance of SkinnedMesh can only manage the skeleton for a single model.

I’m not sure if the number of draw calls (in your case ~15) causes already the performance issue in your app. Note that the management of animations itself can causes quite a lot of overhead, depending of the complexity of the skeleton. It’s probably best to do a performance analysis with Chrome dev tools to exactly determine what piece of code causes the frame drop.

The overhead in the fragment shader could also be the root cause. Do you see a performance improvement if you shrink down the browser window? If you automatically resize the renderer, this will lower the effective resolution and thus lower the amount of work in the fragment shader.

1 Like

Cool, that makes sense about InstancedMesh. :+1:

Regarding performance, it seems like each robot model added to the scene adds about 19 draw calls. So adding 15 or so starts to add up quickly (though I’m not sure if 300 is actually a lot, or if it is contextual). Just so I’m having an appropriate mental model of loading a gltf file, does this sound accurate: Loading a gltf model adds a tree of nested objects and meshes and will add a draw call for every mesh included. So increasing the number of meshes in a model, even if the model has a low number of triangles, can also make it more expensive to use?

I haven’t done general profiling in chrome dev tools yet, I’ve been mostly focusing on the three.js stuff. Yeah, the renderer is being automatically resized and performance does not appear to change fullscreen v tiny. That’s a good point, I’ll dig in to chrome dev tools more generally to see if I can pinpoint the source of the lag more clearly.

Thanks for the feedback!

1 Like

Yes, that’s correct.

1 Like

Hm there are a couple ways to interpret this question:

  1. if you’re willing to lose animation support, then any number of models can be merged, assuming they share a single material or can be modified to do so (e.g. with texture atlases or vertex colors).
  2. if you’re hoping to merge separate animated models and keep the animation, then as @Mugen87 mentions, this wouldn’t work currently.
  3. it is possible to merge sub-meshes within a single glTF model while retaining animation. For example, the robot model could probably be reduced to 1-2 draw calls with a bit of work. gltfpack will do some optimizations of that type, but in this particular case doesn’t reduce draw calls. A full reduction would require more advanced optimization in Blender or another tool.

@donmccurdy - These extra options help to clarify the context I should be thinking about models in general and why the original question was ambiguous. Thanks for the extra context here!