Zylann / godot_voxel

Voxel module for Godot Engine
MIT License
2.49k stars 233 forks source link

Configurability for LOD range(s) #213

Open ChristianB84 opened 3 years ago

ChristianB84 commented 3 years ago

I noticed that there are quite frequently obvious "popping" artifacts (https://en.wikipedia.org/wiki/Popping_(computer_graphics) when moving around in the LOD terrain. I'd like the LOD changes to happen much farther away, so that they are less noticable. I couldn't find a way to configure the distances at which those changes occur. Is there some way which I don't see or is there a workaround?

Of course, support for other approaches to the problem (like those mentioned in the Wikipedia article that I linked above), would also be nice, but I guess they'd be quite hard to implement.

Zylann commented 3 years ago

The value that controls this is lod_split_scale. The higher it is, the further away LOD transitions will occur, but the lower performance will be due to the amount of data to process.

This value relates to the LOD octree. Such octree subdivides space into blocks of variable size, by powers of two. A split_scale value of 2 means that if a viewer is closer than 2 * size of block, such block will subdivide into 8 smaller blocks of higher detail. Conversely, if a viewer is further away than this distance, the block will merge back into a larger one with fewer detail. Increasing the value to 4 will double the distances, however it will multiply processing time by 8 (in the worst case if your world is a sponge) because the volume to process is cubically larger. Depending on the power of your computer there will be a point where the terrain will be too slow to update, or even to render because of the amount of detail, requiring to move slower as well in order to keep up.

I don't have plans for smooth LOD swaps at the moment. I already tried one approach, which is used in No Man's Sky, where swaps are done with a shader that blends meshes by progressively discarding pixels of the previous mesh and replacing them with pixels of the next meshes. Unfortunately there are some challenges to overcome with it (such as popping of the shadow which Godot won't let me customize), and I also hit a bug: https://github.com/godotengine/godot/issues/34966 So I havent continued this and don't plan to for now, as there are other things I'd like to work on before.

Finally there is also some work to do in the mesher to have better quality meshes in the distance, such that popping would be less noticeable. This is still in research but It's probably going to be a combination of 16-bit voxels and deep sampling https://github.com/Zylann/godot_voxel/issues/60.

ChristianB84 commented 3 years ago

Hi, thanks for answering. I already played around a bit with that setting, but the replacement still happened too close in my opinion. So I tried to tweak the scale, which improved the distance issue, but of course increasing the scale means that huge blocks are being replaced at once.

Is it necessary to always double the resolution (in every dimension) or could we maybe increase the LOD a bit less "aggressively"? Then the changes wouldn't be that apparent, even when closer. Could I do that in a customized version of voxel_generator_noise.cpp or would that lead to problems elsewhere?

Would it be an option to "morph" the replaced mesh(es) into the one(s) replacing it/them? I implemented something similar some time ago in a different (but similar) context and it more or less worked. I essentially tried to map the higher LOD vertices to the lower LOD ones and stored both positions in the mesh, then blended the positions in the shader. Of course, there was still some popping when geometry existed only in the block with the higher detail, but that didn't happen too often. However, my solution was very restricted and required special code in the shader.

ChristianB84 commented 3 years ago

I thought about this "softer" LOD increase a bit more. I guess by customizing voxel_generator_noise.cpp, I could reduce the noise resolution (if there aren't any issues with that that I don't see right now), so the changes would be less apparent. However, the mesh resolution would still double - for less than double the details. Would it be possible to replace the 16x16x16 blocks with 8 blocks of (e.g.) 12x12x12?

Zylann commented 3 years ago

Is it necessary to always double the resolution

Yes. This the quickest way to get this working that I know of. Allows to use an octree, with cheap bit-shift, and allows to use nearest-neighbor downscaling. Using a different ratio would require to rewrite the entire LOD system with a slower algorithm.

There is not much you can do with a custom generator, apart from generating lower-frequency voxels (leading to smoother surfaces, so less popping to see).

The size of blocks is not really hardcoded, but you still need to change a constant within the code to choose another one: https://github.com/Zylann/godot_voxel/blob/117802c80766d040ae4d549319d90636512e3d8e/terrain/voxel_map.cpp#L10

It only expects powers of two as well. I never tested different values using Transvoxel but if all goes well, it should just work. Note: if you have saved data using different block size, they will need conversion.