Zylann / godot_voxel

Voxel module for Godot Engine
MIT License
2.72k stars 251 forks source link

SDF terrain artifacts near chunk edges #603

Closed spiderworm closed 9 months ago

spiderworm commented 9 months ago

Describe the bug I'm not sure how to technically describe this issue, but it seems that SDF terrains generated by the Transvoxel mesher have unwanted artifacts. These artifacts appear to happen at the edges of chunks.

To Reproduce Steps to reproduce the behavior:

  1. Add VoxelTerrain
  2. Set min bounds of (-64, 0, -64) and max of (64, 32, 64)
  3. Chunk size 16
  4. VoxelMesherTransvoxel
  5. VoxelGenerator @tool script with the following code for _generate_block:

    func _generate_block(buffer : VoxelBuffer, origin : Vector3i, lod : int) -> void:
    var block_size := int(buffer.get_size().x)
    
    for xo in block_size:
        var x = origin.x + xo
        for yo in block_size:
            var y = origin.y + yo
            for zo in block_size:
                var z = origin.z + zo
    
                var v: float = clamp(y - 9.5, -1, 1)
                buffer.set_voxel_f(v, xo, yo, zo, 1)
  6. Ensure Run Stream in Editor is enabled
  7. Terrain > Regenerate
  8. Wait for mesh to build, artifacts should be visible on some of the sides of the mesh

Expected behavior A flat terrain will generate without odd artifacts on the sides

Screenshots

Screencast from 02-20-2024 03:19:16 PM.webm

Environment

Zylann commented 9 months ago

There is a built-in VoxelGeneratorFlat for testing, which doesn't produce the same artifacts, but when setting height 9.5 there is one:

image

Note, it seems some exceptional logic gets hit when I use your generator: the buffer passed to the generator is not cubic, but you assumed it was so it throws tons of errors (which are sadly poorly reported for some reason Oo not sure what's going on with Godot's error system but half of the message is surgically cut off from the rest). That also makes Godot freeze because it can't handle many error messages, yay.

So basically: meshing requires voxel buffers that are slightly bigger than regular chunks, to include neighbor voxels on all sides. Therefore, meshing tasks are sent the block of voxels to mesh, and all its neighbors too for sides to be meshed properly. When meshing occurs near a border, it finds no block on one side. The current behavior when a block is not provided is to fallback on the generator to produce a thin layer of voxels, to complete the side of the buffer that will be passed to the mesher. Hence the non-cubic size. I'm not sure it should always do that, the behavior stems more from how VoxelLodTerrain works, but technically it is correct.

I haven't figured yet the artifact though.

I fixed the error in your script and now I see no artifacts. Can you confirm?

@tool
extends VoxelGeneratorScript

func _get_used_channels_mask() -> int:
    return (1 << VoxelBuffer.CHANNEL_SDF)

func _generate_block(buffer : VoxelBuffer, origin : Vector3i, lod : int) -> void:
    # var block_size := int(buffer.get_size().x)
    var block_size := buffer.get_size()

    for xo in block_size.x:
        var x = origin.x + xo
        for yo in block_size.y:
            var y = origin.y + yo
            for zo in block_size.z:
                var z = origin.z + zo

                var v: float = clamp(y - 9.5, -1, 1)
                buffer.set_voxel_f(v, xo, yo, zo, VoxelBuffer.CHANNEL_SDF)
spiderworm commented 9 months ago

Thank you for finding that, yes, I believe my error is fixed and I didn't fully understand how to correctly use the buffer.

Zylann commented 9 months ago

Found the cause of the artifact in 44c35df6bccf0b99fa6ec61d665858e2ba26861f