Zylann / godot_voxel

Voxel module for Godot Engine
MIT License
2.6k stars 245 forks source link

How to improve performance with non cubic meshes #680

Open Asgatoril opened 1 month ago

Asgatoril commented 1 month ago

Hi,

I'm trying to generate a terrain of currently 64x64x64 blocks with some holes in it (3D Noise) and am running into some performance issues.

Since I'm trying to get a bite more comicy style like e.g. Dragon quest Builders (https://i.ytimg.com/vi/0q65qT-X8RA/maxresdefault.jpg), I don't use perfect cubes, but cubeish meshes.

I currently have 3 different blocks, each with its own texture (will look into texture atlases once I get a bit farther into the project) which have ~ 250 vertices each.

When I generated the terrain with this, it took around 1 minute to generate and took ~14 GB of RAM. The game ran smooth, once the generation was done, but the generation time and especially the RAM usage is quite high.

I then reduced the meshes to ~ 50 vertices each and tried again. This time the generation took around 30 seconds and still uses 6GB of RAM.

If I replace the meshes with simple cube meshes (8 Vertices), the generation takes around 2 seconds and uses < 1 GB of RAM.

So my question is: Is there something I can do to increase the performance when using non cubic meshes? Especially the RAM usage seems quite high for me, since I am using just 3 different meshes.

The VRAM usage on the other hand is very similar for all 3 tested variants.

Zylann commented 1 month ago

Am I guessing you are using the blocky mesher for this? You say you don't use cubic meshes, but it looks like you are using the blocky mesher still.

So I'm assuming the memory usage you get is because of two things:

I dont think your memory usage comes from voxels themselves, though of course generating a 3D sponge with lots of details across all space will require more memory than more conventional terrain.

Regarding collisions, in theory voxel games should ideally use implicit grid colliders, which would compute collisions on the fly without pre-computing all geometry. But Godot doesn't have that, so unless you use VoxelBlockyMover (which is a very simplistic version of it) you end up having to pay the geometry cost for every mesh collider. The more detailed your meshes are, the more memory they use too. And if you use double-precision builds, they will use twice as much memory because Godot decided to implement that by blanket-replacing all floats with doubles even when it's technically unnecessary.

Something to keep in mind about the blocky mesher is that it will cull geometry that lies on the sides of voxel cubes: if two cubes sit next to each other, the sides they share are removed. So if terrain is mostly made of cubes, only surfaces will produce geometry. But other geometry remains. If you used meshes that do not exploit this, your memory usage will blow up. For example if you add a bevel to your cubes with actual geometry, or holes/carvings, you will end up with them not getting culled on EVERY VOXEL IN THE TERRAIN, which in terms of volume can generate insane amounts of pointless geometry. Check the insides of your terrain and get rid of that if it's the case.

Having a quick look at your screenshot, I dont believe that game is using complex geometry for every block. It looks to me that they are using clever normalmapping, that's all. Maybe also with some displacement mapping or tessellation. Only a few blocks actually get extra geometry, while the majority are simple cubes.

If you still want detailed geometry for all cubes regardless with such a high view distance, AFAIK there is no easy way. The blocky mesher was tuned for simple models specifically. Some can be more detailed, but the most frequent ones should be simple. You would have to use some kind of LOD system that switches blocks to cubes beyond some distance. Right now, that means writing your own mesher that behaves the way you want. But the switching is not really possible to do this within the same LOD size at the moment. There are other options involving multimeshes instead, but that's a very different approach which requires even more customization and still wouldn't actually cull all faces. Finally if you use VoxelTerrain, that has no LOD handling at all. 64x64x64 blocks across is huge. That's 4 times more than the default setting of Minecraft in multiplayer in all directions. I would tune that down or use LOD maybe (though using blocky with LOD isn't very fleshed out at the moment).