The game performance is terrible when there are a lot of meshes

How can I get better FPS when rendering a lot of meshes?

There are a lot of topics on optimization in this forum and in the docs:

Lately, thin instances have been added to support even faster instancing (docs will be available in https://doc.babylonjs.com/how_to/how_to_use_thininstances when validated).

5 Likes

Hi,

Few questions. How many is ‘a lot’ in this case? And are they all blocks/cubes?

They are all cubes, by saying cube I mean a box mesh, filled within a 16x128x16 area. (a cube’s size is 1x1)

This world is not a perfect place,we all have to compromise

compromise, what do you mean?

@EnderAdel I think it is a great use case now about those cube how many different materials are you using ??? cause you could use instances to batch the calls saving gpu.

At the cpu level if not all of them are pickable or moving and so on per frame you could freeze their matrices as well as their materials. This would tremendously help with perf.

Basically the closer we will know your use case the easier it would be to optimize :slight_smile:

Also for all the identical cubes next to each other with similar materials you could even think of merging them as with voxels ?

Ok, I’m going to try to give you guys more details

Ok, so I have 2 types of rendered blocks:
worldBottomBlock, worldGenerationBlock

And all of the meshes within the scene are boxes/cubes.

Here’s the script that defines all of the materials within a scene:

const standerdBlock = function(scene){
  return BABYLON.Mesh.CreateBox("Box", 1, scene);
};
const multiSidesBlock = function(scene){
  var faceUV = new Array(6);
  for(var i = 0; i < 6; i++){
    faceUV[i] = new BABYLON.Vector4(i / 6, 0, (i + 1) / 6, 1);
  }
  return BABYLON.MeshBuilder.CreateBox("Box", { faceUV: faceUV, wrap: true }, scene);
};
const defineMaterials = function(scene){
  var materials = [
    {
      "genBlock": new BABYLON.StandardMaterial("genBlock", scene),
      "struBlock": new BABYLON.StandardMaterial("struBlock", scene),
      "stone": new BABYLON.StandardMaterial("stone", scene),
      "glass": new BABYLON.StandardMaterial("glass", scene),
      "leaves": new BABYLON.StandardMaterial("leaves", scene),
      "worldBottom": new BABYLON.StandardMaterial("worldBottom", scene),//same as bedrock
      "dirt": new BABYLON.StandardMaterial("dirt", scene),
      "grass": new BABYLON.StandardMaterial("grass", scene),
      "mushroom": new BABYLON.StandardMaterial("mushroom", scene),
      "bush": new BABYLON.StandardMaterial("bush", scene),
      "water": new BABYLON.StandardMaterial("water", scene),
      "woodLog": new BABYLON.StandardMaterial("woodLog", scene)
    }
  ];
  //
  //materials.name.diffuseColor = new BABYLON.Color3(0, 0, 1);
  //materials.name.emissiveColor = new BABYLON.Color3(1, 0, 0);
  //materials.name.specularColor = new BABYLON.Color4(0.2, 0.2, 0.2, 0.01);
  //materials.name.alpha = 1;//use it to create a class material
  //materials.name.maxSimultaneousLights = 4;
  //materials.name.diffuseTexture = new BABYLON.Texture("./textures/block/stone.png", scene, true, true, BABYLON.Texture.NEAREST_SAMPLINGMODE);
  //
  materials = materials[0];
  //[START] World generation blocks
  materials.genBlock.diffuseColor = new BABYLON.Color3(0, 0, 1);
  materials.genBlock.disableLightning = true;
  //
  materials.struBlock.diffuseColor = new BABYLON.Color3(1, 0, 0);
  materials.struBlock.disableLightning = true;
  //
  materials.worldBottom.diffuseColor = new BABYLON.Color3(0, 1, 0);
  materials.worldBottom.disableLightning = true;
  //[END] World generation blocks


  //[START] Stone block
  materials.stone.specularColor = new BABYLON.Color4(0.2, 0.2, 0.2, 0.01);
  materials.stone.diffuseTexture = new BABYLON.Texture("./textures/block/stone.png", scene, true, true, BABYLON.Texture.NEAREST_SAMPLINGMODE);
  //[END] Stone block

  //[START] Glass block
  materials.glass.specularColor = new BABYLON.Color4(0.2, 0.2, 0.2, 0.01);
  materials.glass.useAlphaFromDiffuseTexture = true;
  materials.glass.diffuseTexture = new BABYLON.Texture("./textures/block/glass.png", scene, true, true, BABYLON.Texture.NEAREST_SAMPLINGMODE);
  materials.glass.opacityTexture = new BABYLON.Texture("./textures/block/glass.png", scene, true, true, BABYLON.Texture.NEAREST_SAMPLINGMODE);
  //[END] Glass block

  //[START] Leaves block
  materials.leaves.specularColor = new BABYLON.Color4(0.2, 0.2, 0.2, 0.01);
  materials.leaves.useAlphaFromDiffuseTexture = true;
  materials.leaves.diffuseTexture = new BABYLON.Texture("./textures/block/leaves_opaque.png", scene, true, true, BABYLON.Texture.NEAREST_SAMPLINGMODE);
  materials.leaves.opacityTexture = new BABYLON.Texture("./textures/block/leaves_opaque.png", scene, true, true, BABYLON.Texture.NEAREST_SAMPLINGMODE);
  //[END] Leaves block

  //[START] Dirt block
  materials.dirt.specularColor = new BABYLON.Color4(0.2, 0.2, 0.2, 0.01);
  materials.dirt.diffuseTexture = new BABYLON.Texture("./textures/block/dirt.png", scene, true, true, BABYLON.Texture.NEAREST_SAMPLINGMODE);
  //[END] Dirt block

  //[START] Grass block
  materials.grass.specularColor = new BABYLON.Color4(0.2, 0.2, 0.2, 0.01);
  materials.grass.diffuseTexture = new BABYLON.Texture("./textures/block/grass_block.png", scene, true, true, BABYLON.Texture.NEAREST_SAMPLINGMODE);
  //[END] Grass block

  //[START] Mushroom block
  materials.mushroom.specularColor = new BABYLON.Color4(0.2, 0.2, 0.2, 0.01);
  materials.mushroom.diffuseTexture = new BABYLON.Texture("./textures/block/brown_mushroom.png", scene, true, true, BABYLON.Texture.NEAREST_SAMPLINGMODE);
  //[END] Mushroom block

  //[START] Bush block
  materials.bush.specularColor = new BABYLON.Color4(0.2, 0.2, 0.2, 0.01);
  materials.bush.diffuseTexture = new BABYLON.Texture("./textures/block/bush.png", scene, true, true, BABYLON.Texture.NEAREST_SAMPLINGMODE);
  //[END] Bush block

  //[START] Water block
  materials.water.specularColor = new BABYLON.Color4(0.2, 0.2, 0.2, 0.01);
  materials.water.alpha = 0.8;
  materials.water.diffuseTexture = new BABYLON.Texture("./textures/block/water.png", scene, true, true, BABYLON.Texture.NEAREST_SAMPLINGMODE);
  //[END] Water block

  //[START] Wood log block
  materials.woodLog.specularColor = new BABYLON.Color4(0.2, 0.2, 0.2, 0.01);
  materials.woodLog.diffuseTexture = new BABYLON.Texture("./textures/block/wood_log.png", scene, true, true, BABYLON.Texture.NEAREST_SAMPLINGMODE);
  //[END] Wood log block


  /*
  box1.enableEdgesRendering();	
	box1.edgesWidth = 2.0;
	box1.edgesColor = new BABYLON.Color4(.6, .6, .6, 1);
  */

  return materials;
};

And the max limit that I want to allow placing blocks in is 256
And the lowest height that blocks can be places at is 0
NOTE: If you can tell me how can I optimize the scene to works as the intended behavior, that I’ve explained above, that would be very helpful as I think that this would reduce the lag.

And here’s the script that defines blocks within the scene:

const defindBlocks = function(scene){
  const materials = defineMaterials(scene);
  var blocks = [
    {
      "worldGenerationBlock": null,
      "structureBlock": null,
      "stoneBlock": null,
      "glassBlock": null,
      "leavesBlock": null,
      "worldBottomBlock": null,//same as bedrock
      "dirtBlock": null,
      "grassBlock": null,
      "mushroomBlock": null,
      "bushBlock": null,
      "waterBlock": null,
      "woodLogBlock": null
    }
  ];
  blocks = blocks[0];
  /*const FadeInAndOut = function(scene, mesh){
    scene.registerBeforeRender(function(){
      if(BABYLON.Vector3.Distance(scene.activeCamera.position, mesh.position) > 100){
        mesh.visibility = 0;
      }else{
        mesh.visibility = 1;
      }
     });
  };*/

  //.dispose()

  const checkForBlocks = function(mesh){
   /* scene.registerBeforeRender(function(){
      let meshPosition = mesh.position;
      if(scene.isThereMeshAtPosition(meshPosition["x"] + 1, meshPosition["y"], meshPosition["z"]) && scene.isThereMeshAtPosition(meshPosition["x"], meshPosition["y"] + 1, meshPosition["z"]) && scene.isThereMeshAtPosition(meshPosition["x"], meshPosition["y"], meshPosition["z"] + 1) && scene.isThereMeshAtPosition(meshPosition["x"] - 1, meshPosition["y"], meshPosition["z"]) && scene.isThereMeshAtPosition(meshPosition["x"], meshPosition["y"] - 1, meshPosition["z"]) && scene.isThereMeshAtPosition(meshPosition["x"], meshPosition["y"], meshPosition["z"] - 1)){
        mesh.visibility = 0;
      }else{
        mesh.visibility = 1;
      }
    });*/
  };

  blocks.worldGenerationBlock = function(){
    var block = standerdBlock(scene);
    block.material = materials.genBlock;
    block.updateFacetData();
    checkForBlocks(block);
    return block;
  };

  blocks.structureBlock = function(){
    var block = standerdBlock(scene);
    block.material = materials.struBlock;
    block.updateFacetData();
    checkForBlocks(block);
    return block;
  };

  blocks.worldBottomBlock = function(){
    var block = standerdBlock(scene);
    block.material = materials.worldBottom;
    block.updateFacetData();
    checkForBlocks(block);
    return block;
  };

  blocks.glassBlock = function(){
    var block = standerdBlock(scene);
    block.material = materials.glass;
    block.updateFacetData();
    checkForBlocks(block);
    return block;
  };

  blocks.leavesBlock = function(){
    var block = standerdBlock(scene);
    block.material = materials.leaves;
    block.updateFacetData();
    checkForBlocks(block);
    return block;
  };

  blocks.stoneBlock = function(){
    var block = standerdBlock(scene);
    block.material = materials.stone;
    block.updateFacetData();
    checkForBlocks(block);
    return block;
  };

  blocks.dirtBlock = function(){
    var block = standerdBlock(scene);
    block.material = materials.dirt;
    block.updateFacetData();
    checkForBlocks(block);
    return block;
  };

  blocks.grassBlock = function(){
    var block = multiSidesBlock(scene);
    block.material = materials.grass;
    block.updateFacetData();
    checkForBlocks(block);
    return block;
  };

  blocks.mushroomBlock = function(){
    var block = standerdBlock(scene);
    block.material = materials.mushroom;
    block.updateFacetData();
    checkForBlocks(block);
    return block;
  };

  blocks.bushBlock = function(){
    var block = standerdBlock(scene);
    block.material = materials.bush;
    block.updateFacetData();
    checkForBlocks(block);
    return block;
  };

  blocks.woodLogBlock = function(){
    var block = multiSidesBlock(scene);
    block.material = materials.woodLog;
    block.updateFacetData();
    checkForBlocks(block);
    return block;
  };

  blocks.waterBlock = function(){
    var block = standerdBlock(scene);
    block.material = materials.water;
    block.updateFacetData();
    checkForBlocks(block);
    return block;
  };

  return blocks;
};

It pretty mush looks like you are going for a Minecraft like game ???

In this case Voxels techniques are the recommended approach. Minecraft classic is relying on GitHub - andyhall/noa: Experimental voxel game engine. built on top of Babylon.js if you want to learn more about those techniques.

For a cheap workaround, you could try to use instances: Use Instances - Babylon.js Documentation and create instances grouped by materials.

1 Like