From ee578cc738beba4a9caa315250ea30b79966a3db Mon Sep 17 00:00:00 2001 From: Weizhen Huang Date: Mon, 9 Jun 2025 13:52:00 +0200 Subject: [PATCH] Fix #139753: Discontinuity in mesh tangent without UV map when there is no uv, we call the function `map_to_sphere()` to create temporary uv for computing the tangent. It could happen that a triangle has vertices with the u coordinates going across the line where u wraps from 1 to 0. In this case, just computing the difference of the u coordinates results in the wrong triangle area. To fix this problem, we compute distance in toroidal (wrap around) space. This is safe for coordinates generated by `map_to_sphere()` function, because it is not supposed to map the positions of a triangle to u coordinates that span larger than 0.5. Pull Request: https://projects.blender.org/blender/blender/pulls/139880 --- intern/cycles/scene/mesh.cpp | 7 ++++++- intern/mikktspace/mikktspace.hh | 16 ++++++++++++++-- .../blenkernel/intern/editmesh_tangent.cc | 7 ++++++- source/blender/blenkernel/intern/mesh_tangent.cc | 12 +++++++++++- .../render/mesh/cycles_renders/tangent_no_uv.png | 4 ++-- .../mesh/eevee_next_renders/tangent_no_uv.png | 4 ++-- tests/python/eevee_render_tests.py | 2 -- 7 files changed, 41 insertions(+), 11 deletions(-) diff --git a/intern/cycles/scene/mesh.cpp b/intern/cycles/scene/mesh.cpp index 1f890b75f4e..56c4301c114 100644 --- a/intern/cycles/scene/mesh.cpp +++ b/intern/cycles/scene/mesh.cpp @@ -67,7 +67,7 @@ struct MikkMeshWrapper { { /* TODO: Check whether introducing a template boolean in order to * turn this into a constexpr is worth it. */ - if (uv != nullptr) { + if (has_uv()) { const int corner_index = CornerIndex(face_num, vert_num); const float2 tfuv = uv[corner_index]; return mikk::float3(tfuv.x, tfuv.y, 1.0f); @@ -101,6 +101,11 @@ struct MikkMeshWrapper { } } + bool has_uv() const + { + return uv != nullptr; + } + const Mesh *mesh; const float3 *normal; diff --git a/intern/mikktspace/mikktspace.hh b/intern/mikktspace/mikktspace.hh index d722695c5a6..dcd75d93a15 100644 --- a/intern/mikktspace/mikktspace.hh +++ b/intern/mikktspace/mikktspace.hh @@ -253,6 +253,11 @@ template class Mikktspace { return mesh.GetTexCoord(f, v); } + bool has_uv() const + { + return mesh.has_uv(); + } + /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -522,9 +527,16 @@ template class Mikktspace { const float3 t2 = getTexCoord(triangle.vertices[1]); const float3 t3 = getTexCoord(triangle.vertices[2]); - const float t21x = t2.x - t1.x; + float t21x = t2.x - t1.x; + float t31x = t3.x - t1.x; + if (!has_uv()) { + /* Compute edge length in toroidal space, since the u generated by `map_to_sphere()` might + * go cross the seam. */ + t21x -= floorf(t21x + 0.5f); + t31x -= floorf(t31x + 0.5f); + } + const float t21y = t2.y - t1.y; - const float t31x = t3.x - t1.x; const float t31y = t3.y - t1.y; const float3 d1 = v2 - v1, d2 = v3 - v1; diff --git a/source/blender/blenkernel/intern/editmesh_tangent.cc b/source/blender/blenkernel/intern/editmesh_tangent.cc index d7930711e8f..d725a2dbb73 100644 --- a/source/blender/blenkernel/intern/editmesh_tangent.cc +++ b/source/blender/blenkernel/intern/editmesh_tangent.cc @@ -95,7 +95,7 @@ struct SGLSLEditMeshToTangent { mikk::float3 GetTexCoord(const uint face_num, const uint vert_index) { const BMLoop *l = GetLoop(face_num, vert_index); - if (cd_loop_uv_offset != -1) { + if (has_uv()) { const float *uv = (const float *)BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); return mikk::float3(uv[0], uv[1], 1.0f); } @@ -130,6 +130,11 @@ struct SGLSLEditMeshToTangent { copy_v4_fl4(p_res, T.x, T.y, T.z, orientation ? 1.0f : -1.0f); } + bool has_uv() + { + return cd_loop_uv_offset != -1; + } + Span face_normals; Span corner_normals; Span> looptris; diff --git a/source/blender/blenkernel/intern/mesh_tangent.cc b/source/blender/blenkernel/intern/mesh_tangent.cc index 3ae61b84e87..90bc9b5e356 100644 --- a/source/blender/blenkernel/intern/mesh_tangent.cc +++ b/source/blender/blenkernel/intern/mesh_tangent.cc @@ -70,6 +70,11 @@ struct BKEMeshToTangent { copy_v4_fl4(p_res, T.x, T.y, T.z, orientation ? 1.0f : -1.0f); } + bool has_uv() const + { + return true; + } + OffsetIndices faces; /* faces */ const int *corner_verts; /* faces vertices */ const float (*positions)[3]; /* vertices */ @@ -227,7 +232,7 @@ struct SGLSLMeshToTangent { int3 tri; int face_index; uint loop_index = GetLoop(face_num, vert_num, tri, face_index); - if (mloopuv != nullptr) { + if (has_uv()) { const float2 &uv = mloopuv[loop_index]; return mikk::float3(uv[0], uv[1], 1.0f); } @@ -281,6 +286,11 @@ struct SGLSLMeshToTangent { copy_v4_fl4(tangent[loop_index], T.x, T.y, T.z, orientation ? 1.0f : -1.0f); } + bool has_uv() const + { + return mloopuv != nullptr; + } + Span face_normals; Span corner_normals; const int3 *corner_tris; diff --git a/tests/files/render/mesh/cycles_renders/tangent_no_uv.png b/tests/files/render/mesh/cycles_renders/tangent_no_uv.png index cc3988d78f8..c0002b7eec7 100644 --- a/tests/files/render/mesh/cycles_renders/tangent_no_uv.png +++ b/tests/files/render/mesh/cycles_renders/tangent_no_uv.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd07487f8bb71a4199fb15cf994599317670ca806757fdae24c00c85a26d3be2 -size 28659 +oid sha256:7fa3ab720f94fa70de22f70f9e87bd74b8cc06bff20a82352b53ee24d02e14d8 +size 28195 diff --git a/tests/files/render/mesh/eevee_next_renders/tangent_no_uv.png b/tests/files/render/mesh/eevee_next_renders/tangent_no_uv.png index 3cb0486b8e1..1b72de67ab6 100644 --- a/tests/files/render/mesh/eevee_next_renders/tangent_no_uv.png +++ b/tests/files/render/mesh/eevee_next_renders/tangent_no_uv.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4a3a786cd629cbd7074a40c5ece486d6dccd031e8cafdf41ad5bd62f9c1afb1a -size 20981 +oid sha256:e69131fc75964401791a7a13a29d50a44e04699f29f8ff23d827ddbd75899d3a +size 20489 diff --git a/tests/python/eevee_render_tests.py b/tests/python/eevee_render_tests.py index a1cbf360cc0..d07fd94bde1 100644 --- a/tests/python/eevee_render_tests.py +++ b/tests/python/eevee_render_tests.py @@ -43,8 +43,6 @@ BLOCKLIST = [ ] BLOCKLIST_METAL = [ - # Blocked due to difference in tangent space calculation (to be fixed). - "tangent_no_uv.blend", # Blocked due to difference in volume lightprobe bakes (to be fixed). "clamp_.*.blend", "shadow_all_max_bounces.blend",