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:
- Question (or a lot of questions) about performance and optimisations
- Optimizing the scene for a huge amount of simple meshes
- Optimization : Freeze active meshes, Merging, Instances
- Optimization - merge instances
- My experience in Babylon performance optimization and problems encountered
- Optimize your scene - Babylon.js Documentation
- etc
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).
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
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.