Zylann / godot_voxel

Voxel module for Godot Engine
MIT License
2.62k stars 246 forks source link

Visible seams between blocks with MSAA enabled #510

Open kumikumi opened 1 year ago

kumikumi commented 1 year ago

This is likely a known issue, but couldn't find an existing ticket for it, so reporting it here.

Steps to reproduce:

  1. Download and run voxelgame demo, observe that there are no visible seams between blocks. godot-voxel-no-aa
  2. Enable MSAA in project settings (I used MSAA 2D -> 8X and MSAA 3D -> 8X)
  3. Run the same demo again, observe that there are now seams between blocks.

godot-voxel-msaa

Attached 2 screenshots to showcase the issue, the first one with MSAA disabled (default) and another with 8x MSAA enabled.

While this is a problem that many games have (for example it also affects Minecraft if you force-enable MSAA in that game if I recall correctly), there are ways to fix this issue, as not every game has visible seams in world geometry with MSAA enabled.

Zylann commented 1 year ago

I wonder what should be done to fix this. I would not have imagined that it would happen in the first place Oo I already knew using linear filtering in textures can be problematic due to atlasing, but this problem with MSAA is something new to me... it's like you just can't have any sort of wall with not-shared vertices inside it... (which is necessary if textures or vertex colors have to be different in some areas, or just have terrain chunks)

Zylann commented 1 year ago

There is an issue open: https://github.com/godotengine/godot/issues/16337

Working around this sounds very annoying... according to the post, every face voxel models contain (either on sides or internal to the cell, basically anywhere vertices don't connect, which is basically everywhere in Minecraft-like models) have to be slightly scaled somehow (it cannot be naively scaled, it has to expand along the surfaces), but that would still have side-effects like being off at close range or requiring multiple polygonizations if mesh colliders are required. I wonder if scaling in the mesh would even suffice or if it has to depend on distance to the camera... Minecraft doesn't even have AA built-in probably for that reason.

Maybe it's possible to hack something in the fragment shader, but not sure what. There was a similar topic discussed here: https://github.com/godotengine/godot/issues/35067 but the solution might not be applicable here.

Alternatively perhaps another AA technique can be used. FXAA does not have this issue. There is TAA too but I havent tried it.

A lot of games indeed don't have that issue, but that's because their geometry is vastly different, sharing vertices and using smooth surfaces a lot more than Minecraft where every face of almost everything is a separate quad. UV-mapped textures also aren't as often atlased the same way.


Looking into it a bit more, that doesn't seem to occur because of vertex positions, but texture coordinates? In which case the issue would occur because of the fact the demo uses atlasing to reduce the amount of different materials to use on the terrain (and therefore reducing draw calls), so the only way to fix it would be to modify texture coordinates in models to be slightly inset, or modify textures such that there is a margin of pixels between tiles/quads used by faces. That would be a similar workaround to when mipmaps are enabled... which is equally annoying to do, but also cannot be fixed by the module because the demo uses meshes made in Blender, and I don't think there is a straightforward way to automatically modify textures/models to have those margins.

Unfortunately it seems both problems are happening (gaps between geometry faces, AND gaps between textures)... I wouldnt have thought MSAA could be so bad at this...

Anyways, I only quickly tested centroid scaling of sides (inside faces such as with stairs or more complex shapes requires a lot more work). It appears to help hiding geometry gaps, but as described earlier it also creates weird borders when looking at close range, and overall it barely makes a difference because the problem is also about texturing. WIP for that workaround: https://github.com/Zylann/godot_voxel/commit/2ba568ae99741072e5b2d4216abd18f7024c46d7 I have no good fix for this planned at the moment.

Important note: this module does not assume every model is a cube and allows for any mesh to be used.

kumikumi commented 1 year ago

Thanks for looking into it. Today I played with your more simple example, the blocky_terrain demo, and noticed that the seams were also visible there, but a lot less prominent.

blocky_terrain_aa

I removed the atlasing (replaced the texture atlas with just the sand texture) and all the seams are now gone. I also experimented with linear filtering, and it doesn't cause visible seams either.

blocky_terrain_linear_filtering

Finally I replaced the CubeMesh with a CustomMesh (dirt cube from the other demo), and I'm fairly sure that this doesn't cause seams either (other than the texture being a bit wonky there)

blocky_terrain_custom_mesh

Perhaps there is something about the blocky_game demo that makes it worse for MSAA somehow, but I'm not yet sure what it is.

By the way, what's the reason for using a cube made in blender instead of just the default cube geometry? When should one use one vs the other?

Zylann commented 1 year ago

Thanks for looking into it. Today I played with your more simple example, the blocky_terrain demo, and noticed that the seams were also visible there, but a lot less prominent.

That's because the texture is different and it uses a much smaller atlas, which "luckily" doesn't have borders with big color differences.

Finally I replaced the CubeMesh with a CustomMesh (dirt cube from the other demo), and I'm fairly sure that this doesn't cause seams either (other than the texture being a bit wonky there)

Not sure what you mean here, but if you changed the model to use GEOMETRY_MESH instead of GEOMETRY_CUBE, I dont think that's supposed to have much of an effect, since the end result after baking will be the same.

Perhaps there is something about the blocky_game demo that makes it worse for MSAA somehow, but I'm not yet sure what it is.

As described earlier, the blocky game demo contains a lot more different blocks and textures, and is using a bigger atlas to reduce the number of materials (increasing FPS due to less draw calls). Unfortunately that setup makes MSAA a very bad option for anti-aliasing. Textures would need pixel padding or would have to be changed into TextureArrays with a special shader, however automating this requires significant work and an extra "import" step to bake/update generated assets.

By the way, what's the reason for using a cube made in blender instead of just the default cube geometry? When should one use one vs the other?

Just convenience. I used Blender in the blocky game demo because I wanted to design many blocks, some of which were not cubes, and it was convenient to make them all in the same Blender file. I also was able to give every face the textures I wanted in the atlas (custom UV-mapping). The cube geometry mode is only a shortcut in case you want a quick cube model without using Blender. The end result is the same internally.