Open Calinou opened 1 month ago
What do the UV maps look like?
Primitive meshes UV each side individually with spacing between each cube, could be that blender tries to keep seams together resulting in less pixels wasted to create spacing and thus more resolution for each face.
What do the UV maps look like?
Using Godot's View UV2 function in the Mesh menu at the top of the 3D editor viewport (note that aspect ratio isn't preserved):
Primitive BoxMesh | Single Blender cube from Multiple Imported Scenes example | Single imported scene[^1] |
---|---|---|
[^1]: This contains UVs from all cubes in that scene, including the larger ground mesh.
Comparison between Godot and Blender UV2 (with Godot's layout rotated by 90 degrees in GIMP for easier comparison):
Notice the lines missing at the top/sides for Godot; it's due to the UV2 preview in the editor being cropped. While this is a bug in the UV2 preview, it highlights the lack of padding around the edges of the UV layout (along with the reduced padding between faces).
@Calinou if I remember correctly we indeed don't have padding on the upper and left edge of the texture. It would be fairly straight forward to change that.
I've tried to increase padding in generated BoxMesh UV2 so that it doesn't touch the edges, but this doesn't resolve the light leaking issue after rebaking lightmaps:
diff --git a/scene/resources/3d/primitive_meshes.cpp b/scene/resources/3d/primitive_meshes.cpp
index ceeb73d0ef..7c45ac44ec 100644
--- a/scene/resources/3d/primitive_meshes.cpp
+++ b/scene/resources/3d/primitive_meshes.cpp
@@ -663,11 +663,11 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int
// Only used if we calculate UV2
// TODO this could be improved by changing the order depending on which side is the longest (basically the below works best if size.y is the longest)
- float total_h = (size.x + size.z + (2.0 * p_uv2_padding));
+ float total_h = (size.x + size.z + (4.0 * p_uv2_padding));
float padding_h = p_uv2_padding / total_h;
float width_h = size.x / total_h;
float depth_h = size.z / total_h;
- float total_v = (size.y + size.y + MAX(size.x, size.z) + (3.0 * p_uv2_padding));
+ float total_v = (size.y + size.y + MAX(size.x, size.z) + (6.0 * p_uv2_padding));
float padding_v = p_uv2_padding / total_v;
float width_v = size.x / total_v;
float height_v = size.y / total_v;
@@ -712,7 +712,7 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int
ADD_TANGENT(1.0, 0.0, 0.0, 1.0);
uvs.push_back(Vector2(u, v));
if (p_add_uv2) {
- uv2s.push_back(Vector2(u2 * width_h, v2 * height_v));
+ uv2s.push_back(Vector2(u2 * width_h + padding_h, v2 * height_v + padding_v));
}
point++;
@@ -722,7 +722,7 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int
ADD_TANGENT(-1.0, 0.0, 0.0, 1.0);
uvs.push_back(Vector2(twothirds + u, v));
if (p_add_uv2) {
- uv2s.push_back(Vector2(u2 * width_h, height_v + padding_v + (v2 * height_v)));
+ uv2s.push_back(Vector2(u2 * width_h + padding_h, height_v + 2.0 * padding_v + (v2 * height_v)));
}
point++;
@@ -775,7 +775,7 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int
ADD_TANGENT(0.0, 0.0, -1.0, 1.0);
uvs.push_back(Vector2(onethird + u, v));
if (p_add_uv2) {
- uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), v2 * height_v));
+ uv2s.push_back(Vector2(width_h + padding_h * 2.0 + (u2 * depth_h), v2 * height_v + padding_v));
}
point++;
@@ -785,7 +785,7 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int
ADD_TANGENT(0.0, 0.0, 1.0, 1.0);
uvs.push_back(Vector2(u, 0.5 + v));
if (p_add_uv2) {
- uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), height_v + padding_v + (v2 * height_v)));
+ uv2s.push_back(Vector2(width_h + padding_h * 2.0 + (u2 * depth_h), height_v + 2.0 * padding_v + (v2 * height_v)));
}
point++;
@@ -838,7 +838,7 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int
ADD_TANGENT(-1.0, 0.0, 0.0, 1.0);
uvs.push_back(Vector2(onethird + u, 0.5 + v));
if (p_add_uv2) {
- uv2s.push_back(Vector2(u2 * width_h, ((height_v + padding_v) * 2.0) + (v2 * depth_v)));
+ uv2s.push_back(Vector2(u2 * width_h + padding_h, ((height_v + 2.0 * padding_v) * 2.0) + (v2 * depth_v)));
}
point++;
@@ -848,7 +848,7 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int
ADD_TANGENT(1.0, 0.0, 0.0, 1.0);
uvs.push_back(Vector2(twothirds + u, 0.5 + v));
if (p_add_uv2) {
- uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), ((height_v + padding_v) * 2.0) + (v2 * width_v)));
+ uv2s.push_back(Vector2(width_h + 2.0 * padding_h + (u2 * depth_h), ((height_v + 2.0 * padding_v) * 2.0) + (v2 * width_v)));
}
point++;
I've tested all PrimitiveMeshes and PrismMesh is also affected by this issue. Strangely enough, PlaneMesh/QuadMesh are also affected, but its code says that it uses the fallback UV2 generation method instead of custom code.
@Calinou I'm just theorising but I don't think the problem is the padding size, it's that we don't have padding on the left and top edges for the texture. So I think we need to add an offset to all UVs of half the size of the padding to 'center' the UV map on the texture.
I'm just theorising but I don't think the problem is the padding size, it's that we don't have padding on the left and top edges for the texture.
I added padding to the top and left edges, as demonstrated by the top/left lines now being visible in the UV2 preview (they are no longer clipped by its clip_contents
property).
I added padding to the top and left edges, as demonstrated by the top/left lines now being visible in the UV2 preview (they are no longer clipped by its
clip_contents
property).
Ah, then I misread your code because I didn't see the offset being introduced, But it still doesn't resolve the issue? I don't know what the difference is then between the imported meshes and the generated meshes. Very strange.
Tested versions
System information
Godot v4.4.dev (7187c251d) - Fedora Linux 41 (KDE Plasma) on X11 - X11 display driver, Multi-window, 1 monitor - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 4090 (nvidia; 560.35.03) - 13th Gen Intel(R) Core(TM) i9-13900K (32 threads)
Issue description
LightmapGI leaks on box PrimitiveMeshes due to poor UV2 generation quality. This does not occur when using scenes imported from glTF files (created using Blender), regardless of whether multiple cubes were assembled together in Godot or the scene design was done in Blender entirely as a single object. This occurs even at Ultra quality with the denoiser disabled, and bicubic sampling disabled in the Project Settings.
The issue is less noticeable at higher texel scales, since this is resolution-dependent.
The geometry on all scenes is 100% identical, down to the triangulation order for the Multiple Imported Scenes example. The Single Imported Scene has a different triangulation order for its boxes, but that shouldn't affect UV2 generation quality noticeably.
I haven't tested whether this occurs with other PrimitiveMeshes, but BoxMesh is likely the most prominent example.
cc @BastiaanOlij
Scene assembled using box PrimitiveMeshes
Notice the light leaking on boxes' edges, or overly dark edges.
Scene assembled using boxes exported from Blender
Notice the lack of light leaking or overly dark edges.
Steps to reproduce
Minimal reproduction project (MRP)
test_lightmap_uv2.zip