Too many or high quality meshes can cause your scene to render very slowly, especially on computers without a strong GPU. Let's see what can be done to solve those issues.
Too many
This is the usual cause of slow rendering. We tend to think that since there is no limitation to the number of meshes we can add to a scene, it wouldn't degrade the user's experience. What's another box? Shouldn't be a problem.
Wrong. This is a problem. With each polygon added, its information must be sent to the GPU for rendering. Check this example out - http://www.babylonjs-playground.com/#1YNQHJ . Zooming out will show you how the performance degrades while more and more objects are added to the camera's frustum (or, "becoming active"). And those are relatively simple meshes. With complex meshes the scene will have a much harder time to render.
Solutions
The first and simplest solution is to reuse a mesh's geometry and properties, if possible. Simply put, doing that with identical meshes will reduce the amount of information sent to the GPU for rendering (reducing the magical number draw calls). Using this solution will allow you to change the transformation (position, rotation, scaling) of any mesh, while reusing its resources. This is, of course, only helpful if you can find a mesh that can be duplicated. The best example is a large number of spheres. Those can be duplicated, because a sphere is a sphere is a sphere.
There are two ways of doing that - mesh cloning and instances. The main difference between them is that an instance is an exact copy of the original mesh. The returned mesh is an instance of the InstanceMesh
class, which is very limited in its functionality. You will not be able, for example, to change the new mesh's material. This is not even a copy of the mesh. It is a "slave" mesh with a new transformation. Cloning a mesh returns a Mesh
object that you will be able to manipulate further. The main thing that is cloned here is the geometry - the vertex data of the mesh is reused across all meshes. The material, for example, can be changed.
Using both methods is very simple. For instances:
var originalMesh = getOriginalMesh();
for (var i= 0; i< 100; ++i) {
var newInstance = originalMesh.createInstance("index: " + index);
newInstance.position.y =+ i*2;
}
This will create 100 exact copies of the original mesh, and will change their position. Rotation and scaling can be changed the same way.
To clone an object, the following code applies:
var originalMesh = getOriginalMesh();
for (var i= 0; i< 100; ++i) {
var newClone = originalMesh.clone("index: " + index);
newClone.position.y =+ i*2;
newClone.material = ...
}
This will create 100 clones of the original mesh, also changing its material.
Choosing between cloning and instancing is your task. If all meshes are completely the same, including material, use instances. If the vertex data of the new meshes is the only thing that should be identical to the original mesh, use cloning. Check this instace demo from babylonjs.com - http://www.babylonjs.com/?INSTANCES
The second solution involves combining many meshes to one. The upside here is that the number of draw calls to render all of those meshes will be reduces to one. The downside is that the single meshes cannot be manipulated. from now on, changing the meshes properties will change all of them together (as they are a single mesh).
The best use-case for combining meshes is when you have a lot of meshes sharing the same material. This is a wonderful case - the texture coordinates stay the same, so the texture will not change its position on each of the original meshes. Multi materials can also be used, but this is more complex.
To combine many meshes, Babylon offers a simple static function to merge meshes:
var arrayOfMeshes = [mesh1, mesh2, ...]
var newMesh = BABYLON.Mesh.MergeMeshes(arrayOfMeshes);
newMesh
is a brand new Mesh
object that contains the vertex data of all of the meshes in the array.
High quality meshes
A mesh with a very large number of polygons will reduce the FPS tremendously. A compromise between the quality of the mesh and the quality of experience should always be found.
The first tip would be - use only low-poly meshes when creating a WebGL game for everyone. This is a tip that suits everything written in this post - If you don't want to target high-level GPU-powered systems only, compromise.
If you do have a high-poly mesh, you will need to simplify it. There are many methods of simplifications. Your 3D editor is usually the first place to look at.MeshLab, an open source 3D editor, has many types of simplifications, the best being the one based on quadratic edge collapse. Check this tutorial - http://www.shapeways.com/tutorials/polygonreductionwith_meshlab.
You will also be able to create many versions of the same mesh and add them as LOD (Level Of Detail) objects - so when the camera is relatively far away from the object
Babylon.js offers an in-browser simplification - the mesh simplifies in the user's browser and reduces bandwidth usage (no need for many simplified objects to get the LOD effect). You can read the tutorial here - In-Browser Mesh Simplification. This simplification uses the quadratic edge collapse process that MeshLab offers, with a few differences - the most significant being that MeshLab can use the texture to simplify as well, providing better simplified meshes. The reason it wasn't implemented is to keep the simplification process relatively fast. Using the textures will add complexity to the process, that will increase the time needed to simplify an object.
End of part 1
These were a few suggestions as to how a Babylon.js scene can be optimized by the user. I have covered meshes. In the next post I will cover materials/textures and other features.