Zylann / godot_voxel

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

Occasionally faces won't properly illuminate or work with Godot's triplanar mapping #59

Closed TokisanGames closed 4 years ago

TokisanGames commented 5 years ago

Sometimes, some lower LODs have faces that are black when using a texture, or impossibly dark even with a white albedo color and the sun shining directly on it. I wonder if those faces have their normals reversed.

Present in master, though these pics are from the paged branch.

I've seen this a lot on the noise world and have even pointed it out in videos I've sent you. I thought it was my shader, because if triplanar map blending is too high, it will cause black sections. But no. This issue is there even when there's no triplanar mapping.

It's also not a lighting issue, or that the lower LODs have a mesh that is just causing more shadow.

Textured w/ triplannar mapping and very low blend (6). Shouldn't have black marks:

image

Blank SpatialMaterial. Sun is shining on both faces:

image

Bottom face has shifted to a new LOD. Shows how much light was there that did not reflect off the face in the picture above.

image

This is from master, sun shining on this pillar. Then I backed up to hit the right LOD from different angles, moving around it from left to right.:

image

Zylann commented 5 years ago

I've seen this occasionally as well, I honestly don't know where it comes from.

TokisanGames commented 5 years ago

Could those faces have their normals reversed? Isn't there a draw normals debug in Godot or VT? I've seen Reduz post pics of faces with normals poking out. Maybe that would tell us if this is the issue.

Zylann commented 5 years ago

It's likely normals are reversed yeah. For DMC terrains, normals come from the meshing algorithm itself, by deriving it from the SDF channel in voxels. Maybe one of the many marching cube cases has a flaw? I'm not aware of any normal debug tool like this in Godot. You could check this by changing your shader to unshaded and output normals in ALBEDO?

TokisanGames commented 5 years ago

Here I grabbed NORMAL from vertex(), assigned it to a varying, then assigned it to ALBEDO.

1, 3, 5 = LOD0 from left, middle, right side 2, 4, 6 = lower LOD with a black artifact image

I guess the vertex normals look ok, but the mesh has a strange shape. You can see it has a lip of green image

Here in fragment I just assigned ALBEDO=NORMAL

image

So maybe vertex normals are ok, but face normals are reversed?

Zylann commented 5 years ago

I'm unable to see those black spots with my shader in the location you found them image image image image

TokisanGames commented 5 years ago

After more testing, I decided a video will show some things better:

https://youtu.be/uNVG_49DGNo

Zylann commented 5 years ago

Black/dark areas: I don't know yet what these are. In some rare cases they are just the marching square skirts that sometimes reveal if looked at towards the sun, which would otherwise be a hole into the terrain. But I am aware that in some cases, geometry itself turns dark, and I suspect it's due to a corner case in the marching cubes implementation which would turn normals upside down. So far I don't know where the bug is, any help is appreciated. A restrained test case could help pinpoint the issue (like meshing a simple 8x8 buffer).

They can be occasionally found in noise terrain, precisely in places where corners are very sharp: image This one was at a block boundary (you can tell by putting your camera inside it, you'll see marching square skirts right inside). image

Holes in the mesh: These are supposed to be hidden by skirts most of the time, but they can still show up. Either because skirts are too short (but couldnt figure out a better sweetspot), or because adjacent octrees have slightly different outcomes in their subdivisions which makes them not line up as if they were one. Same here, I don't know where the bug is.

Nubs: This is due to https://github.com/Zylann/godot_voxel/issues/60 There are some kinds of shapes that won't ever work with the current meshing technique. Marching cubes are good for organic terrain, not geometric or thin shapes like those in the demo. I left them here specifically to test/illustrate that. Remember they were supposed to be perfectly square in the original demo xD To handle thin geometric shapes, both data storage and meshing techniques need to be changed.

TokisanGames commented 5 years ago

Should this issue include the mesh holes or should that be a separate issue?

Zylann commented 5 years ago

@tinmanjuggernaut which mesh holes? There is #57 during edition but for holes that remain despite marching square skirts that could be another issue yeah.

TokisanGames commented 5 years ago

My/Godot's triplanar mapping method produces black faces. But when it works, allows for triplanar mapped normals (and albedo, AO).

@Zylann 's triplanar mapping method and non-triplanar materials produce darker albedo faces (like the albedo is multiplied with black), but triplanar mapped normals isn't an option.

I've tried the ensure_correct_normals shader option and it did not help.

Now that VLT is paged and can reach far clip, this opens up the possibility for high speed, flight based games. This issue becomes more obvious as most of the terrain will be on a lower LOD.

Black faces all over when flying overhead: image

TokisanGames commented 4 years ago

This issue isn't limited to lower LODs. I just think they are more common there, and I use Godot's triplanar mapping method which turns the faces black so they are more obvious.

You've posted pics and I've seen other instances that have been on LOD0. On your pics you can see texture applied, they are just very dark.

Today I encountered it on a new shader that does not use triplanar mapping at all. The faces properly apply albedo and vertex displacement. My shader doesn't touch normals or anything else. However, I have a spotlight shining directly on them and they will not illuminate. They accept only ambient light.

https://youtu.be/fIm7B8KsmEg

So for these faces:

TokisanGames commented 4 years ago

I attempted the following unsuccessful tests with a light shining on a 2D plane:

All lit properly.

I also figured out how to properly color the terrain with normals (e.g. x=red, y=yellow, z=blue), unlike the garbage I showed above, and don't see oddities here.

For reference you can display normals by:

varrying vec3 vertex_normal;
void vertex() { 
    vertex_normal = NORMAL; 
}
void fragment() { 
    vec3 normal_color  = clamp(vertex_normal,0.,1.);
    ALBEDO = normal_color;
}

I don't believe it's a normal problem. I'm wondering if the face isn't being properly constructed and the engine is picking up the slack by displaying something, but the lighting code does not accept it. e.g. It's missing a vertex and the engine is auto-filling the face, or it has something to do with indices.

Zylann commented 4 years ago

I believe this really comes from the meshing process, just not sure which part exactly. To debug this we need a simple scene without terrain, only buffer and mesher, with extremely few voxels that reproduce the case.

TokisanGames commented 4 years ago

It's possible that you implemented DMC perfectly, but the nature of the algorithm is non-manifold.

Today I found an instance that looks like a non-manifold mesh problem. E.g. the vertices were on the same point, or the hermite data went through the voxel but was too narrow and didn't touch any edges or something: image

I just found a BSD licensed implementation of manifold DMC: https://github.com/dominikwodniok/dualmc

Unfortunately, under rare circumstances the original algorithm can create non-manifold meshes. See the remarks of the original paper on this problem. In chapter 3.3.5 of his book "Isosurfaces: Geometry, Topology, and Algorithms" Rephael Wenger proposed the manifold dual marching cubes algorithm as a possible solution, which is also included in this implementation.

// If a problematic C16 or C19 configuration shares the ambiguous face 
// with another C16 or C19 configuration we simply invert the cube code
// before looking up dual points. Doing this for these pairs ensures
// manifold meshes.

All the code is in /include: https://github.com/dominikwodniok/dualmc/blob/master/include/

Zylann commented 4 years ago

Note: the current implementation is defaulting as an equivalent to marching cubes, so I wonder how that affects the fix to do? It could be interesting to check the transvoxel mesher on that data, because even though it does not implement seams, its regular cell polygonization is entirely different from DMC (it still does a similar job but the code is completely different).

TokisanGames commented 4 years ago

Look at this lovely artifact. There are two nearby sections. Even the skirts are poking out. Here it's using godot's triplanar mapping so turns black.

https://youtu.be/FwOmOXHC_Go

Would it help if there was some debug function that could dump a section of the mesh to disk or console? If there was something like do_sphere, then when I encounter these, I could toss a bullet at it, which could write the mesh or the voxel buffer to disk or console. Then we could reload it as it's own scene and have an isolated test case.

Zylann commented 4 years ago

That's what I suggested before: make a scene without terrain, generate a very small VoxelBuffer and polygonize it directly with VoxelMesherDMC (like done in the test_dmc scene of the demo project). It may require a minimum size to work though (the mesher needs a margin of 2). It might not be strictly necessary to copy the mesh directly from a full terrain (that's quite cumbersome), only the voxel data should do.

TokisanGames commented 4 years ago

Here's a relocatable reference area for the future small area capture test.

Default 3d noise world settings (e.g. from my demo). Location: 1412.63, 122.4, 685.94

The problem faces are not caused by triplanar blend sharpness (which causes black faces in some cases, due to a lack of clamping I believe), nor by features the material can do.

image

Zylann commented 4 years ago

Something I don't get, is that the artifact appears so sharp. Vertices are supposed to be connected together, and then shading should be smooth. But if they look sharp, there can be two reasons:

I'm testing both the incomplete Transvoxel implementation and DMC in a test scene, so far I could not find the artifact. Also I found out both algorithms resulted in similar approximation errors when it comes to normals, which a generic mesh-based normal calculation doesn't have. Which makes sense, they are both derived from marching cubes.

Edit: found one. So it's not because of block boundary, and it only happens with DMC. Note: DMC uses a different table of marching cubes than the Transvoxel one, so I'm not surprised that the result is slightly different. Also with both algorithms I find many normals have a length below 0.9 or above 1.1. Some are very near zero.

DMC: image

DMC with recomputed normals: image

Transvoxel: image

Code used to generate voxel data in which it happens:

    var vb_size = 8
    var padding = 2
    _voxels.create(vb_size + padding * 2, vb_size + padding * 2, vb_size + padding * 2)

    _voxels.fill_f(1.0, VoxelBuffer.CHANNEL_ISOLEVEL)

    var osn = OpenSimplexNoise.new()
    osn.set_seed(12)
    osn.set_period(6.0)
    var vt = _voxels.get_voxel_tool()
    vt.set_channel(VoxelBuffer.CHANNEL_ISOLEVEL)

    for z in range(1, _voxels.get_size_z()):
        for x in range(1, _voxels.get_size_x()):
            for y in range(1, _voxels.get_size_y()):
                var v = osn.get_noise_3d(x + 22, y + 6, z + 26) + 0.2
                vt.set_voxel_f(Vector3(x, y, z), v)

And it turns out, the artifact happens precisely when normal is almost zero. I placed yellow cubes where normals are beyond 0.1 of error, and red when they are zero: image

Zylann commented 4 years ago

Normalizing seems to fix it. In 28cb74e9b11ce7de1e5e2f63d031f1a0910632cf

TokisanGames commented 4 years ago

Great work, @Zylann . I did some testing and think you've solved the issue for LOD0. Does this also normalize the mesh for lower LODS?

Here is my testing of the various symptoms brought up in this issue:

LOD0 Fixed

image

image

Questionable LOD0

The only oddities I was able to find on LOD0 in any of my scenes was this. Those dark spots have a light shining on them. But they are too small to be a mesh face. This shader is using vertex displacement, so I think the shader screwed them up. I'm only including this for completeness in case it comes up again elsewhere.

image

LOD1+ still has an issue

It seems meshes on lower LODs still show black or dark faces.

Our tower still shows a dark face. This one is difficult to test because on some demos the tower turns into a nub on lower LODs due to #60, and other demos it stays as a tower. I don't know what the difference is in the demos. I've used the same VT settings.

image

image

image

image

P.S. Issue #63 Holes in the mesh is unchanged as shown above.

Zylann commented 4 years ago

The fact it happens in far lods makes me think of one hypothesis: far lods have a higher isolever factor because they are bigger. On the heightmap example (which, remember, has a faulty stream), values between voxels differ so quickly and so much that they hit their quantified bounds (they are all either -1 or 1). So when computing the gradient between two voxels, the results are very likely to be zero, which means the normal also ends up as zero, normalized or not. Maybe you could verify this by checking in the shader?

TokisanGames commented 4 years ago

Vertices with non-normalized normals with length < 0.5 in red: image

Normal lengths < 0.1 are only small red dots: image

I can't see any red vertices when length(normal)==0.

However, the reason the faces turn black is due to this line in Godot's triplanar shader: uv1_power_normal/=dot(uv1_power_normal,vec3(1.0));

The texture sample turns to 0 so the dot product is probably returning zero.

If I put this at the beginning of vertex() it fixes all the black faces. (Incidentally, the red dots can show there are still some normal lengths between 0.3 and 0.5 even though they are normalized??) Anyway, this line fixes all the black faces:

NORMAL = normalize(NORMAL+vec3(0.,0.001,0.));

With this change using triplanar mapping, or a regular shader material, fog or no, everything looks fine. No artifacts in the fog. There might be some faces that are still a little dark, but they are far away and subtle enough that I can't be sure. Don't see anything on the tower.

If the shader uses world_vertex_coordinates, then ensure_correct_normals must also be used to get rid of the black faces. Nothing else I could do with the normal or albedo would fix it, except turning on that flag. And actually this is the first time I've ever seen that second flag actually do anything.

YAY! image

TokisanGames commented 4 years ago

@Zylann So we've effectively fixed this problem with a module fix and a shader work around. But there's still an issue with normals apparently. Do you want to keep this open until it is found in the engine? Or, I could just make a note about the shader fix in the docs and we could close it.

Zylann commented 4 years ago

I will probably come back to it when investigating terracing.

There could be a case like this:

gx = right - left
gy = top - bottom
gz = front - back
normal = Vector3(gx, gy, gz).normalized()

The normal will be zero if the values are equal, even though the middle voxel could be different. It probably happens more on sticks because if you take a slice of the map, they really are like a vertical bar of +1 in the middle of -1 values perfectly centered on the grid, which means calculating the normal of that will give the same values on the sides, across all axes. So the gradient is null.

The fix you did is basically equivalent to give a normal of (0,1,0) if it is null, but it won't really be correct. Maybe it should not have been null in the first place.

Anyways, I was able to fix one cause thanks to reproducing it the dmc_test scene. The best way to figure out why it happens in the other case is to do the same. If my theory confirms, perhaps normal calculation could fallback on a different formula taking the midpoint into account? Something like:

gx = right - middle
gy = top - middle
gz = front - middle

This one is biased too, but should not be null in such cases.

Zylann commented 4 years ago

With the Transvoxel merge, I also introduced a change to how normals are calculated. It seems to still cause darkening (or whitening), but maybe it won't go completely black this time?

TokisanGames commented 4 years ago

As mentioned in #2, faces still go black on distant LODs using triplanar mapping and still require a shader fix.

Zylann commented 4 years ago

I submitted a06de157bf633e417c1f3d73266c786c015516ed which changes again how normals are computed. Now they are normalized later in the process (after marching cubes interpolation) and if length is null it falls back on (0, 1, 0) instead of (0, 0, 0). So you might be able to remove your shader fix.

TokisanGames commented 4 years ago

I no longer see any faces showing black or unusually dark. The shader fix is no longer necessary. Great work!