You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

问题根源分析

  1. 三角面顶点缠绕顺序错误
    FDynamicMesh3默认用逆时针缠绕顺序定义面的正面(外部可见面),你的部分三角面顶点顺序是顺时针,导致面朝向反转,只能从内部看到。比如底部第二个面AppendTriangle(3, 1, 2),从外部看是顺时针,和第一个面的逆时针顺序冲突,这是半数面异常的核心原因。

  2. 法线Overlay绑定逻辑冗余且错误
    你为每个法线重复添加3个元素,但绑定三角面时,法线索引和顶点顺序不匹配。当三角面朝向反转时,法线方向没有同步调整,进一步导致显示异常。同时这种重复添加的方式完全没必要,平面的三个顶点法线方向一致,只需绑定同一个法线元素即可。

  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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 08:46:05