Repository: TokisanGames/Terrain3D

Issue #185: Normal artifact along region boundaries
Created: 2023-08-16T11:14:52Z
This artifact appears on regions borders. Along a region border make multiple heights at 40, 20, 0, -40 then start smoothing them together and it will appear. The thickness of this artifact appears to be 2 vertices, and is offset. So on X it goes from 0.5 to -1.5.
The artifact is in the normals. It occurs in the fragment() normal calculation. It has to do with using texture() with filter_linear, as it linearly blends the edge of the height map in it's normal calculation. If we switch to filter_nearest or texelfetch() it does resolve, however the normals are blocky.
Possible solutions:

use texelfetch but bilinearly interpolate between the lookups to smooth them, rather than letting the card do it

We can't do it only on the vertices (looked up in vertex()) because they spread out on lower lods, but
We can use the same point grid and bilinear interpolation that the material lookups use.


generate normal map and pass it in to the shader

perhaps this can be done upon mouse release for the section that was just modified, then blit in to the normal map
when GPU painting works, could be generated by the compute shader on the fly, but will it have the same issue?





Testing notes:

offset collision half a texel instead of shader - no it's the shader that's off due to filtering/interpolation
vertex, calculate 9 heights around, say in varying, then using textureGather
set map to nearest & use texel lookup
fragment linear interpolate normals (or heights) from vertex neighbors, lerped based on position
use texel lookup per pixel, but lerp it
if on edge of region use texel otherwise texture?

I tried clamping to the region so it wouldn't sample across boundaries and just average, that reduced the size but didn't help
	vec2 region_min = floor(uv/_region_size); //1023/1024->0, 4095/4096->0
	vec2 region_max = region_min + vec2(_region_size);
	float left = get_height(clamp(uv + vec2(-_region_texel_size, 0), region_min, region_max));
	float right = get_height(clamp(uv + vec2(_region_texel_size, 0), region_min, region_max));
	float back = get_height(clamp(uv + vec2(0, -_region_texel_size), region_min, region_max));
	float front = get_height(clamp(uv + vec2(0, _region_texel_size), region_min, region_max));

Duplicate issue #284: Colormap artifact in mipmaps along region boundaries
Created: 2023-12-20T16:54:12Z
Terrain3D version
v0.9.0
System information
Godot v4.1.3.stable - Windows 10.0.22621 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 3070 Laptop GPU (NVIDIA; 31.0.15.4633) - 12th Gen Intel(R) Core(TM) i9-12900H (20 Threads)
Is the issue reproducable in the demo?
Yes
Issue description
Mipmaps were generated on the colormap array in 7e4f9eb to avoid distant noise, which was very obvious when using a full colormap such as in GIS applications w/ satellite photos (farther below). However, this has introduced another artifact on the region boundary. Enabling renormalize on generate_mipmaps() has no affect.
ALBEDO=colormap*vec3(.1)

Regular shader

Though mipmaps should be generated on the whole map at once, it appears this tiling is not a problem and the individual mipmaps blend together fine. It's actually surprising that they blend at all, given how different from each other the lower lods look.

The only artifact is the small grid line that shows up when up close on mipmap0 shown at the start, and also far away shown here.

Upon further testing, I've found that indeed it is not the mipmap lods, it is actually the system that determines which LOD to use that is the problem.

These lines are the borders of the individual textures in the arrays and show up when using texture().
I have found I can clamp the lod based on distance.

I could go up to 2048 or 3172 to basically eliminate the grid at all distances. However in practice with either a full GIS colormap, or ground textures up close, 1024 gives me the best results of texture mipmaping to eliminate noise at far distances, and fix this line at close distances.

I'll close this issue with this workaround, though I believe this to be an engine bug as textureQueryLod() should not show a grid and should not bleed into my renders using texture().
textureLod(tex, 0) does not include this grid artifact.
Basically these two lookup blocks provide very similar results. It's as if when texture() is used on texture arrays, it adds the results of textureQueryLod() to the result.
# Texture Lookup
	color_map = texture(_color_maps, region_uv);

# TextureLod + textureQueryLod artifically added
	vec2 v = textureQueryLod(_color_maps, region_uv.xy);
	color_map.rgb = textureLod(_color_maps, region_uv, v.y).rgb + vec3(v.y*.005);

ALBEDO = albedo_height.rgb * color_map.rgb;

Upstream issues
godotengine/godot#48462
godotengine/godot#27837 (comment)
godotengine/godot#33519

Closing comment (2023-12-24T09:02:45Z): I reopened this because I think this is an upstream bug in texture and textureQueryLod.  7860ecf is a workaround, but I don't think it should be there at all. @clayjohn @Calinou can you guys take a look at this please, given your comments on the referenced tickets?
In short, I've encountered an artifact on mipmapped texture array lookups. The problem exists with texture() but not with any level of textureLod(). The problem also exists in textureQueryLod() and it appears that texture() adds the result of textureQueryLod() to the output. @clayjohn In godotengine/godot#48462, you suggested meshes need UVs to properly work with texture array mipmaps, but how is that supposed to work on UV-less terrains? Start at the beginning for full context.
To test it:

Download the latest release of terrain3d
Import the project it and restart twice
Open the demo
Hide the Borders node
Enable the Material/Shader override enabled and paste this minimal_shader.txt in it.

At the bottom of the shader you'll see four sections:

the problem with textureQueryLod showing the texture array borders
the problem with texture seemingly combining the above values into the results
the near visual equivalent of 2, by actually adding textureQueryLod to textureLod
the current hacky workaround which is not as good as if it were working properly.

With (2) enabled, you'll see the artifact along the path at the region borders, shown in the red artifact below. The green artifact is unrelated, tracked in #185.

Here is where mipmaps are created on the colormap texture array:

  
    
      Terrain3D/src/terrain_3d_storage.cpp
    
    
         Line 239
      in
      7860ecf
    
  
  
    

        
          
           map->generate_mipmaps();