UE C++创建FDynamicMesh3立方体半数面法线异常求助
UE中手动创建FDynamicMesh3立方体的法线异常问题排查
问题描述
在Unreal Engine中用C++手动创建FDynamicMesh3立方体时遇到以下问题:
- 手动定义顶点、面和法线后,半数面的法线异常,只能从立方体内部看到
- 调用
mesh.CheckValidity()返回false,说明网格存在错误 - 参照Blender导出OBJ的逻辑,执行
ReverseOrientation和InitializeOverlayToPerTriangleNormals操作后,立方体显示正常,法线无异常
关键代码
UE::Geometry::FDynamicMesh3 AMyActor::RegenerateMeshByHand() { UE::Geometry::FDynamicMesh3 mesh; mesh.EnableAttributes(); UE::Geometry::FDynamicMeshNormalOverlay *normalsOverlay = mesh.Attributes()->PrimaryNormals(); const std::array vertices{FVector3d(-1, -1, -1), FVector3d(1, -1, -1), FVector3d(-1, -1, 1), FVector3d(1, -1, 1), FVector3d(-1, 1, -1), FVector3d(1, 1, -1), FVector3d(-1, 1, 1), FVector3d(1, 1, 1)}; const std::array normals{FVector3f(0, -1, 0), FVector3f(0, 1, 0), FVector3f(-1, 0, 0), FVector3f(1, 0, 0), FVector3f(0, 0, -1), FVector3f(0, 0, 1)}; int32 id; UE::Geometry::EMeshResult result; for (auto vertex : vertices) { mesh.AppendVertex(vertex * 100); } for (auto normal : normals) { normalsOverlay->AppendElement(normal); normalsOverlay->AppendElement(normal); normalsOverlay->AppendElement(normal); } // Face 0, 1. Normal 0 id = mesh.AppendTriangle(0, 1, 2); result = normalsOverlay->SetTriangle(id, {0, 1, 2}); id = mesh.AppendTriangle(3, 1, 2); result = normalsOverlay->SetTriangle(id, {0, 1, 2}); // Face 2, 3. Normal 1 id = mesh.AppendTriangle(4, 5, 6); result = normalsOverlay->SetTriangle(id, {1 * 3, 1 * 3 + 1, 1 * 3 + 2}); id = mesh.AppendTriangle(7, 5, 6); result = normalsOverlay->SetTriangle(id, {1 * 3, 1 * 3 + 1, 1 * 3 + 2}); // Face 4, 5. Normal 2 id = mesh.AppendTriangle(4, 6, 0); result = normalsOverlay->SetTriangle(id, {2 * 3, 2 * 3 + 1, 2 * 3 + 2}); id = mesh.AppendTriangle(2, 6, 0); result = normalsOverlay->SetTriangle(id, {2 * 3, 2 * 3 + 1, 2 * 3 + 2}); // Face 6, 7. Normal 3 id = mesh.AppendTriangle(1, 5, 3); result = normalsOverlay->SetTriangle(id, {3 * 3, 3 * 3 + 1, 3 * 3 + 2}); id = mesh.AppendTriangle(7, 5, 3); result = normalsOverlay->SetTriangle(id, {3 * 3, 3 * 3 + 1, 3 * 3 + 2}); // Face 8, 9. Normal 4 id = mesh.AppendTriangle(0, 1, 4); result = normalsOverlay->SetTriangle(id, {4 * 3, 4 * 3 + 1, 4 * 3 + 2}); id = mesh.AppendTriangle(5, 1, 4); result = normalsOverlay->SetTriangle(id, {4 * 3, 4 * 3 + 1, 4 * 3 + 2}); // Face 10, 11. Normal 5 id = mesh.AppendTriangle(2, 3, 6); result = normalsOverlay->SetTriangle(id, {5 * 3, 5 * 3 + 1, 5 * 3 + 2}); id = mesh.AppendTriangle(7, 3, 6); result = normalsOverlay->SetTriangle(id, {5 * 3, 5 * 3 + 1, 5 * 3 + 2}); // Why is this false? const auto validityResult = mesh.CheckValidity({}, UE::Geometry::EValidityCheckFailMode::ReturnOnly); return mesh; }
问题根源分析
三角面顶点缠绕顺序错误
FDynamicMesh3默认用逆时针缠绕顺序定义面的正面(外部可见面),你的部分三角面顶点顺序是顺时针,导致面朝向反转,只能从内部看到。比如底部第二个面AppendTriangle(3, 1, 2),从外部看是顺时针,和第一个面的逆时针顺序冲突,这是半数面异常的核心原因。法线Overlay绑定逻辑冗余且错误
你为每个法线重复添加3个元素,但绑定三角面时,法线索引和顶点顺序不匹配。当三角面朝向反转时,法线方向没有同步调整,进一步导致显示异常。同时这种重复添加的方式完全没必要,平面的三个顶点法线方向一致,只需绑定同一个法线元素即可。网格有效性检查失败的原因
CheckValidity()返回false,本质是部分三角面的法线方向与几何朝向不匹配,FDynamicMesh3会检测这种不一致并标记网格无效。
修复方案
1. 统一三角面的逆时针缠绕顺序
调整所有方向错误的三角面顶点顺序,确保从立方体外部看是逆时针:
- 底部第二个面改为
mesh.AppendTriangle(2, 1, 3) - 顶部第二个面改为
mesh.AppendTriangle(6, 5, 7) - 左侧第二个面改为
mesh.AppendTriangle(0, 6, 2) - 右侧第二个面改为
mesh.AppendTriangle(3, 5, 7) - 近面第二个面改为
mesh.AppendTriangle(4, 1, 5) - 远面第二个面改为
mesh.AppendTriangle(6, 3, 7)
2. 简化法线Overlay的绑定逻辑
不需要重复添加法线元素,每个法线只添加一次,绑定三角面时直接使用对应法线索引:
// 每个法线只添加一次 for (auto normal : normals) { normalsOverlay->AppendElement(normal); } // 底部两个面绑定法线0 id = mesh.AppendTriangle(0, 1, 2); result = normalsOverlay->SetTriangle(id, {0, 0, 0}); id = mesh.AppendTriangle(2, 1, 3); result = normalsOverlay->SetTriangle(id, {0, 0, 0});
3. 验证修复效果
修改后调用mesh.CheckValidity()应返回true,所有面都能从外部正常显示。如果仍有问题,可以调用mesh.ReverseOrientation()一次性反转所有面的朝向,再重新绑定法线。
内容的提问来源于stack exchange,提问作者stryku
相关产品推荐
相关产品推荐

