前言:

​ 体素化是Recast生成导航网格的5个步骤中的第一步,作用是将集合数据转成3D体素组成的高度场,简而言之就是将复杂的几何地形数据,转换成统一简易的数据结构,便于后续处理。同时不可行走的空间会在这个阶段被标识。

写这篇随笔的目的:

  1. 说明下体素化的过程的大致流程。
  2. UE5是怎么做的,相关代码函数应该去哪找,之后可以当作一本UE体素化说明书来查找相关内容。
  3. 分析具体的算法是如何实现的,什么因素可能会影响效率,以规范后续的开发修改。

主体内容:

  1. 我们还是从最开始构建导航开始串,串到体素化的部分在细说。
  2. 最外层当然是在NavigationSystem::Build()中,它会检查它的NavData数据是否完成构建。而NavData会绑定一个NavMeshGenerator,有没有构建好和构建都找它。
  3. RecastNavMeshGenerator::EnsureBuildCompletion()中创建若干个异步任务。每个任务以Tile为单位,创建其Tile的NavMesh数据。
  4. RecastTileGeneratorWrapper这个就是任务,它只干一件事,通知它身上的TileGenerator去干活。
  5. RecastTileGenerator才是开始干活的地方,而第一步就收集碰撞数据GatherGeometryFromSources(),碰撞数据细节这里就不讲了,这里是体素化的流程。
  6. 还是在RecastTileGenerator,收集完碰撞数据以后就要利用碰撞去生成Tile了。
  7. 现在我们拿到的只是一堆碰撞数据,很难想象一堆碰撞数据是怎么直接变成导航数据的,具体哪些地方是可以走的,哪些地方太窄或太矮,人挤不进去,感觉用这么抽象的数据是解决不了的。所以要先对数据进行处理,用recast的话说就是为Tile生成多层的layer,GenerateCompressedLayers()。
  8. 生成多层layer的第一步:体素化(终于到体素化了,前面没搞清楚的,现在再仔细看还来的及)。
  9. 体素化第一步,构建高度场:CreateHeightField()
  10. 构建高度前时,首先把Tile的边界给填清楚了,高啊,宽啊,包围盒啊什么的。
  11. 随后,便根据物理碰撞来处理高度场,如果没有碰撞数据的话,高度场甚至直接不生成了,我们来看看生成的情况,rcCreateHeightfield()。
  12. CreateHeightfield中没什么特别的,只是单纯的生成空间和初始化,那处理高度场的应该在体素化第二步,格栅化图元:RasterizeGeometry()。
  13. RasterizeGeometry()中首先是rcMarkWalkableTriangles(),标记可行走。根据三角形的倾斜角度判断这个三角形是否可以行走。
  14. 随后进行rcRasterizeTriangles(),很吓人,UE这里全是计算却占了500多行的代码。大致流程是遍历所有三角形进行体素化,在rasterizeTri内进行addspan操作,span就是高度合并后的体素。

具体部分分析:

  1. rcMarkWalkableTriangles(标记可行走)

    1. 大致能看出,for循环遍历了Tile中所有的三角形,并执行了calcTriNormal(),然后拿计算出来的norm与walkableSlopeCos(估计是生成可配置的角度的cos值)比较,如果大的话就可行走(90°以下cos越大,角度越小,说明可行走的区域要保证小于这个角度)
    2. calcTriNormal中,先是取了两次三角形的两点进行相减,得到两个向量,再拿两个向量进行了叉积运算,很明显这是在求三角形的法向量。
    3. 所以当三角形的法向量cos值大于walkableSlopeCos的话说明可以行走。

    标记可行走1

    标记可行走2

  1. rcRasterizeTriangles(格栅化三角形:待补充,太多了看不下去)
最后修改:2024 年 01 月 08 日
如果觉得我的文章对你有用,请随意赞赏