Zylann / godot_voxel

Voxel module for Godot Engine
MIT License
2.59k stars 244 forks source link

smooth terrain: 'lines' on 'connection' points #96

Closed blockspacer closed 2 years ago

blockspacer commented 4 years ago

image

White dots follows line pattern and can change on camera moment. They are more noticeable on shadowed surfaces. Light can pass through white dots.

Tested on Ubuntu 18.04, Nvidia, Godot voxel 1b19bbee8e2d17ee8f2c1f45435aaf1e1a6b79cf (Enable 3D noise on VoxelTerrain...), Godot 0587df4aa5f2977350cc80b1522cdc1e483c4515 (3.1.2-stable)

Project based on noise_smooth_lod.tscn from https://github.com/tinmanjuggernaut/voxelgame with CHANNEL_ISOLEVEL renamed to CHANNEL_SDF

Please respond if you can't reproduce the bug

image

Zylann commented 4 years ago

I have seen cases where there are a few pixels, or sometimes even a tiny line in some places, but that was extremely rare and not dependent on camera orientation. I don't know what the cause is. It could be a float precision error. Things like this have been seen even in Minecraft, or in 2D with tilemaps, where modular meshes are stitched next to each other.

blockspacer commented 4 years ago

@Zylann

Based on quote As a solution for visible seams or cracks along mesh edges, a method called Transition Cubes algorithm from paper Examining Automatic Texture Mapping of Arbitrary Terrains https://www.diva-portal.org/smash/get/diva2:422722/FULLTEXT01.pdf

I assume that seams or cracks along mesh edges may be possible without Transition Cubes algorithm. Are seams or cracks from paper along mesh edges may be like White dots?

blockspacer commented 4 years ago

@Zylann modular meshes are stitched next to each other sounds like Z-fighting https://www.reddit.com/r/Unity3D/comments/9ucvtf/how_to_fix_these_seams_between_modular_pieces/

Zylann commented 4 years ago

I assume that seams or cracks along mesh edges may be possible without Transition Cubes algorithm. Are seams or cracks from paper along mesh edges may be like White dots?

No they aren't. This module uses Transvoxel, which has transition meshes already (similar as the one seen in this paper).

modular meshes are stitched next to each other sounds like Z-fighting

This is not Z-fighting either. It may just be a float precision error, where one of the vertices is offset by an infinitesimal amount for some reason. Also, it's not as simple to fix as "just moving them together" like done with simple cubes, because this is an arbitrary terrain, and chunks are polygonized in parallel, without "knowing each other". They should have just lined up instead.

In order to work fixing this, I need a small reproduction project where this problem can immediately be seen, isolated and measured.

TokisanGames commented 4 years ago

@blockspacer You are using Godot 3.1.2, but you're using voxel master with all updates (plus the macro patches for 3.1?)

This looks like your own shader. Are you using the vertex shader here? If so, what if you remove it? https://github.com/Zylann/godot_voxel/issues/2#issuecomment-570013045

blockspacer commented 4 years ago

@Zylann @tinmanjuggernaut Bug remained after i switched to godot 3.1 513bfe496c10eb874d085d009742a568363413d3 godot_voxel master with support for godot 3.1 branch https://github.com/Zylann/godot_voxel/pull/102 tinmanjuggernaut/voxelgame ffe4aec31f5fa286aa17bcdd614eb72e877bbd09

your own shader - i use triplanar.shader (default) from tinmanjuggernaut/voxelgame ffe4aec31f5fa286aa17bcdd614eb72e877bbd09 BTW - I can't notice difference after un-commenting VERTEX = get_transvoxel_position(VERTEX, COLOR);

was extremely rare - white dots are much more visible with darker shadows. They can make smooth terrain almost unplayable when camera moves (critical for games that must have dark caves or day/night cycle). On cube terrain they still hardly noticeable, but can blink along connection lines when camera moves.

My shadows setup: in WorldEnvironment - procedural_environment Energy - 0.1 Sky Contribution - 0.1 in WorldEnvironment - DirectionalLight Bake mode: Disable (BTW, inderect produced same results. Why indirect is used on dynamic terrain in tinmanjuggernaut/voxelgame? Isn't indirect for static terrain?)

TokisanGames commented 4 years ago

Direct Light /Indirect Energy=1? That's the default. It shouldn't any any effect without GI.

I see it. So basically @Zylann, if you turn off the lights, turn down ambient energy, you'll see a ton of white speckles twinkle along the square seams. It is effected by camera angle. Provided vertex shader doesn't help. Occurs in heightmap or noise, VLT or VT.

Edit: And it happens on a plain white material also, so it's not my shader.

blockspacer commented 4 years ago

BTW, why in get_viewer_pos_and_direction line out_direction = -gt.basis.get_axis(Vector3::AXIS_Z) is not .normalized()?

Zylann commented 4 years ago

why in get_viewer_pos_and_direction line out_direction = -gt.basis.get_axis(Vector3::AXIS_Z) is not .normalized()?

Because the basis already is.

If you see those white dots even with a totally different mesher and it's affected by lighting, then it's even harder to find out if the problem comes from my module. I have no idea what the real cause would be or how to fix it efficiently. It could even be dependent on graphics driver.

Maybe you could workaround it with a dark sky? Or you could have an anti-firefly post-processing effect which would canceal those dots when they show up.

I see none of those. Note: the ones I mentionned in https://github.com/Zylann/godot_voxel/issues/96#issuecomment-573353029 were also not dependent on camera angles, must have been something else. image image

Out of curiosity, what are your camera settings?

When looking closer I sometimes see a few, only at very specific camera angles: image Still, no idea how to possibly fix that. Last time I saw something like this in a 3D game, it was Minecraft https://www.reddit.com/r/Minecraft/comments/ek5vtv/white_dots_appearing_in_between_blocks/ And it looks like they still haven't solved it https://bugs.mojang.com/browse/MC-1794

TokisanGames commented 4 years ago

I tried it on a November build with DMC and it doesn't appear. It's only on the new builds with Transvoxel. So we all see it, on presumably different cards and drivers, shaders and a blank material, VT and VLT, different data sources. Transvoxel seems to be the common denominator.

It's not affected by lighting. It's affected by camera angle, and even frame. They flicker even if the camera isn't moved. It's only visible in the dark though.

Here I took a clip of 334 frames, while gently moving the camera. I merged all frames together with a 'lighter color' blend mode so you can clearly see the pattern it makes. It would be on a very thin grid line, but it is sampled fatter due to my camera motion.

image

Still photo. The line of dots started lower right and curved up and over the rock, though on this frame the upper portion didn't illuminate.

image

Zylann commented 4 years ago

I tried it on a November build with DMC and it doesn't appear.

My guess is, they don't appear with DMC because marching square skirts do extend beyond the necessary range to fix LOD cracks. Which "luckily" hides the dots in the way. With Transvoxel, the geometry is supposed to perfectly connect without off-margins of any kind. Which unfortunately uncovers the dots.

They flicker even if the camera isn't moved.

The few I could find did not do that Oo

TokisanGames commented 4 years ago

With Transvoxel, the geometry is supposed to perfectly connect without off-margins of any kind. Which unfortunately uncovers the dots.

So maybe it's a Godot renderer bug?

I changed the background to custom color and was able to change the color and luminosity of the dots by changing the color, so it appears it's the sky peeking through.

Zylann commented 4 years ago

@tinmanjuggernaut Yeah it's the sky, that's why I suggested dark sky to mitigate the issue. An anti-firefly effect would also help, but your screenshot is madness. I'm not seeing that many dots anywhere.

If it even happens with blocky mesher that's even more of a bummer, because no floats are used in there. It's all integers until it gets to the vertex array...

I tried quantizing vertex positions with Math::stepify, it only made it worse.

Calinou commented 4 years ago

was extremely rare - white dots are much more visible with darker shadows. They can make smooth terrain almost unplayable when camera moves (critical for games that must have dark caves or day/night cycle). On cube terrain they still hardly noticeable, but can blink along connection lines when camera moves.

This could be worked around by darkening the sky when the player is detected as being in a cave. This is what Minetest does (it also suffers from this issue).

If you want to avoid affecting reflections, you could add a large transparent CubeMesh around the player that would effectively darken the sky (its cull mode will need to be set to Front for this to work).

blockspacer commented 4 years ago

Tested under Windows 10, Nvidia. Same behavior.

If you can't reproduce - please try to: 1) use white_dots_bug branch from https://github.com/blockspacer/voxelgame/tree/white_dots_bug 2) Disable "Antialiasing" in graphics driver settings as stated in https://bugs.mojang.com/browse/MC-1794

Reproduced with godot_voxel https://github.com/blockspacer/godot_voxel/tree/3_1_without_error_macros_h (same behavior in master branch).

@Calinou Thanks for respose, great ideas. Issue can be seen not only in caves. How Minetest handles shadowed cliffs or night/day cycle?

Zylann commented 4 years ago

I tried this anti-firefly shader on a fullscreen ColorRect, it cleared all fireflies I was able to reproduce (will only work on isolated pixels). Of course, at performance cost.

shader_type canvas_item;

uniform float u_threshold = 0.4;

void fragment() {
    vec3 col = texture(SCREEN_TEXTURE, SCREEN_UV).rgb;
    vec3 col_nx = texture(SCREEN_TEXTURE, SCREEN_UV - vec2(SCREEN_PIXEL_SIZE.x, 0.0)).rgb;
    vec3 col_ny = texture(SCREEN_TEXTURE, SCREEN_UV - vec2(0.0, SCREEN_PIXEL_SIZE.y)).rgb;
    vec3 col_px = texture(SCREEN_TEXTURE, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).rgb;
    vec3 col_py = texture(SCREEN_TEXTURE, SCREEN_UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).rgb;
    float d = min(
        min(distance(col, col_nx), distance(col, col_ny)), 
        min(distance(col, col_px), distance(col, col_py))
    );
    vec3 col_m = 0.25 * (col_nx + col_ny + col_px + col_py);
    //col_m = vec3(0.0, 1.0, 0.0); // DEBUG
    COLOR = vec4(mix(col_m, col, step(d, u_threshold)), 1.0);
}

Another thing to try would be to quantize world position of vertices in the vertex shader?

Calinou commented 4 years ago

How Minetest handles shadowed cliffs or night/day cycle?

It can also detect when you're looking at a large surface with a low average light level (though the algorithm isn't perfect).

blockspacer commented 4 years ago

@Zylann isolated pixels - bug may produce lines. Also it may produce small holes in shadow (very rarely) I tried FXAA shader from https://github.com/blockspacer/voxelgame/commit/614be8a190c968c39a5ef17a91661ed68eae702e and it made white lines hardly noticeable (as a temporary workaround).

quantize world position of vertices in the vertex shader - I'd like to try that approach, but IDK where to start.

Zylann commented 4 years ago

Isolated pixels is all I could reproduce, so that fixed it at least for me I guess.

To quantize in world space I guess you'd need to change the shader to calculate world space vertex positions itself (instead of Godot automatically doing it), I don't remember the keyword for that though. Then for XYZ, multiply by N, floor the result, and divide back by N (or something like that).

I also tried scaling up blocks by a tiny amount, or growing them a bit along normals, none of it worked. I hate floats with passion.

blockspacer commented 4 years ago

@Zylann

i tried render_mode skip_vertex_transform to make terrain look blocky by floor and N=1.0, issue still here. I'm not sure that understood you correctly.

void vertex() {
    mat4 inv_model_view_matrix = inverse(MODELVIEW_MATRIX);

    VERTEX = floor(VERTEX.xyz);
    VERTEX = (MODELVIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
    NORMAL = (MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz;

    vertex_normal = NORMAL;

    TANGENT = vec3(0.0,0.0,-1.0) * abs(NORMAL.x);
    TANGENT+= vec3(1.0,0.0,0.0) * abs(NORMAL.y);
    TANGENT+= vec3(1.0,0.0,0.0) * abs(NORMAL.z);
    TANGENT = normalize(TANGENT);
    BINORMAL = vec3(0.0,1.0,0.0) * abs(NORMAL.x);
    BINORMAL+= vec3(0.0,0.0,-1.0) * abs(NORMAL.y);
    BINORMAL+= vec3(0.0,1.0,0.0) * abs(NORMAL.z);
    BINORMAL = normalize(BINORMAL);

    A_uv_power_normal=pow(abs(NORMAL),vec3(A_tri_blend_sharpness));
    A_uv_power_normal/=dot(A_uv_power_normal,vec3(1.0));
    A_uv_triplanar_pos = (inv_model_view_matrix * vec4(VERTEX, 1.0)).xyz * float(A_uv_tiles) / (16.) + A_uv_offset;         //On VoxelTerrain 16 is 100% size, so uv_tile is multiples of 16. 
    A_uv_triplanar_pos *= vec3(1.0,-1.0, 1.0);

    B_uv_power_normal=pow(abs(NORMAL),vec3(B_tri_blend_sharpness));
    B_uv_power_normal/=dot(B_uv_power_normal,vec3(1.0));
    B_uv_triplanar_pos = (inv_model_view_matrix * vec4(VERTEX, 1.0)).xyz * float(B_uv_tiles) / (16.)  + B_uv_offset;
    B_uv_triplanar_pos *= vec3(1.0,-1.0, 1.0);

    // Get the distance from camera to VERTEX (VERTEX as if the camera is 0,0,0)
    vertex_distance = (PROJECTION_MATRIX * MODELVIEW_MATRIX * vec4(VERTEX, 1.0)).z;     
}
Zylann commented 4 years ago

@blockspacer when I mentionned "blocky mesher" I assumed this would reproduce with blocky terrain, however when re-reading messages I must have misread that. So we have not tested this with actual Blocky mesher?

If you still get the dots by flooring vertex positions which would still be lining up without holes then I don't know what could possibly be done. I tried it myself, both with just floor(VERTEX) and floor(VERTEX * 100) * 0.01, none of them was problem-free.

Seen here across blocks of same LOD, always at boundaries: image Note: I took this screenshot after tweaking Transvoxel to use integer math when interpolating positions (before it was done with floats). At this point, all my module does is putting integers into a float and scale that float. That did not change the problem.

TokisanGames commented 4 years ago

I filed an upstream bug with details from here.

blockspacer commented 4 years ago

Great articles about T-junction problems https://blackflux.wordpress.com/2014/02/23/meshing-in-voxel-engines-part-1/ https://blackflux.wordpress.com/2014/03/01/meshing-in-voxel-engines-part-2/ https://blackflux.wordpress.com/2014/03/02/meshing-in-voxel-engines-part-3/

Also interesting idea from https://www.reddit.com/r/VoxelGameDev/comments/3muuee/rendering_is_missing_pixels_possibly_t_junction/

It is sometimes a feasible approach not to clear the color buffer and so "smear" over those pixels (popularized by the quake engines).

How to disable clearing of color buffer in Godot? ClearMode? http://docs.godotengine.org/en/latest/classes/class_viewport.html?highlight=update()#numeric-constants

Zylann commented 4 years ago

AFAIK Transvoxel does not produce T-junctions, yet fireflies still happens in same LOD anyways.

How to disable clearing of color buffer in Godot? ClearMode?

Yes: http://docs.godotengine.org/en/latest/classes/class_viewport.html?highlight=update()#class-viewport-property-render-target-clear-mode But I guess you won't see the sky then, and I think you can't choose "only color".

blockspacer commented 4 years ago

I tried to add to https://github.com/tinmanjuggernaut/voxelgame/blob/master/project/fps_demo/scripts/Player.gd

func _ready():
    $CamNode.get_viewport().set_clear_mode(Viewport.CLEAR_MODE_NEVER)
    $CamNode/Camera.get_viewport().set_clear_mode(Viewport.CLEAR_MODE_NEVER)
    print('Player ready')

Nothing changed. Sky is visible. 'Player ready' printed to console

Zylann commented 4 years ago

@blockspacer I don't know, maybe the sky acts as a clear, or maybe it doesn't work for the main viewport

Calinou commented 4 years ago

@blockspacer Try changing the background mode to Keep in the Environment resource attached to the WorldEnvironment node. If it works, you should see an "hall of mirrors" effect when you move the camera in the 3D viewport.

(PS: This is also the fastest background mode, so it should be preferred when working on a scene that's purely indoors.)

blockspacer commented 4 years ago

Keep mode in the Environment resource fixes issue both with or without set_clear_mode(Viewport.CLEAR_MODE_NEVER)

TokisanGames commented 2 years ago

A workaround has been found. https://github.com/godotengine/godot/issues/35067#issuecomment-1058617216

Adding this to the shader will fix the fireflies along the seams of the voxel terrain meshes (using my old 3.2.2 voxel terrain build, haven't worked in the newer versions.)

shader_type spatial;
render_mode  skip_vertex_transform;

void vertex() {
    VERTEX = (WORLD_MATRIX * vec4(VERTEX, 1.0)).xyz;
    VERTEX = (INV_CAMERA_MATRIX * vec4(VERTEX, 1.0)).xyz;
}

However, since we're all using triplanar-mapping, specifying skip_vertex_transform messes up NORMAL, so when you move the camera around the terrain gets lighter and darker.

This page says how the NORMAL projection (and binormal/tangent) can be done manually, but this works for VERTEX, but not NORMAL. https://docs.godotengine.org/en/3.4/tutorials/shaders/shader_reference/spatial_shader.html?highlight=skip_vertex_transform#render-modes

Users can disable the built-in modelview transform (projection will still happen later) and do it manually with the following code:

shader_type spatial;
render_mode skip_vertex_transform;

void vertex() {
    VERTEX = (MODELVIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
    NORMAL = normalize((MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz);
    // same as above for binormal and tangent, if normal mapping is used
}

We have two triplanar mapping algorithms, one made by zylann in his dmc_terrain.shader and one made by me, adapting the one built into Godot triplanar.shader. Both are negatively affected by skip_vertex_transform. I'm not yet sure how to fix the projection on NORMAL so it works for triplanar mapping.

TokisanGames commented 2 years ago

Looks like it does work if done correctly. The key is the new lines must go at the end, not at the top.

Here is the working vertex shader for dmc_terrain.shader using zylann's triplanar method. The three bottom lines are the additions, plus the render mode.

render_mode  skip_vertex_transform;

void vertex() {
    v_world_pos = VERTEX;
    v_world_normal = NORMAL;

    VERTEX = (WORLD_MATRIX * vec4(VERTEX, 1.0)).xyz;
    VERTEX = (INV_CAMERA_MATRIX * vec4(VERTEX, 1.0)).xyz;
    NORMAL = (MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz;
}

And these go at the very end of my shader vertex function in triplanar.shader:

render_mode  skip_vertex_transform;

void vertex() {
    ....
    VERTEX = (WORLD_MATRIX * vec4(VERTEX, 1.0)).xyz;
    VERTEX = (INV_CAMERA_MATRIX * vec4(VERTEX, 1.0)).xyz;
    NORMAL = (MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz;
    TANGENT = (MODELVIEW_MATRIX * vec4(TANGENT, 0.0)).xyz;
    BINORMAL = (MODELVIEW_MATRIX * vec4(BINORMAL, 0.0)).xyz;
}

I believe this ticket can now be closed after over 2 years!

blockspacer commented 2 years ago

@tinmanjuggernaut Thank you!