Recast 学习记录之一:三角形光栅化
序
本文根据 Sample_SoloMesh::handleBuild 中的 Step 2. Rasterize input polygon soup 进行源码解析。
rcCreateHeightfield 创建高度场
HeightField 和 span 的概念可以看看 CritterAI 对高度场的介绍 。
其实就是将模型按照包围盒确定的大小,划分为 cell 网格,每一个网格都使用链表保存垂直方向上的所有 cell。y 轴上连续的 cell 被合并为一个 span,称为 solid span。
rcMarkWalkableTriangles 标记可行走三角形
参数 walkableSlopeAngle 用于指定倾斜角度小于多少的平面才是可以行走的,RecastDemo 中使用的是 45°。
代码中 walkableThr 由 cosf(walkableSlopeAngle/180.0f*RC_PI) 计算得到,然后计算出三角形的单位法线向量 norm,最后用 if (norm[1] > walkableThr) 判断三角形面是否可以行走。推导过程如下:
已知三角形逆时针顺序三个点
又设三角形平面与 x-z 轴平面的夹角为
然后我们就能够算出一个平面的倾斜角度是否小于 walkableSlopeAngle 了。
显然当
又因为三角形的顶点是有顺序的,所以一个给定的三角形面可以根据其法线向量的 y 轴分量正负值确定其是正面朝上还是朝下。对于面朝下的那些三角形,计算得出的法线向量 y 轴分量会小于 0,此时其三角形表面也是不可行走的。
至此,就用三角形单位法线向量的 y 轴分量和
图画的不太好,将就看吧。。。
rcRasterizeTriangles 将三角形进行光栅化处理
将三角形做了行走标记处理后,下一步就要将三角形进行光栅化处理了。
这一步的核心是要判断三维空间里,一个三角形面与哪些格子相交。如果三角形面与一个格子有任意相交的地方,则需要将这个格子进行体素化处理。
Recast 中使用 rcRasterizeTriangles 函数来处理三角形的光栅化,函数本身只是遍历所有的三角形,其重点是 rasterizeTri(单个三角形的光栅化)及其调用的 dividePoly(将多边形沿轴线切割为两个多边形)这两个函数。
rasterizeTri 中通过两层 for 循环调用 dividePoly 实现先 z 轴后 x 轴的两方向切割,逐步在 x-z 轴上将三角形切割成一个一个 cellSize 大小的块。
第一次外层 for 循环在 z 轴上进行切割后如图,这时候我们得到了一个 z 轴上最大长度为 cellSize 的多边形
然后内层 for 循环在 x 轴上继续切割
然后我们就可以遍历
接着继续切割,直到整个三角形被完全切割完毕:
dividePoly 将一个凸多边形,按轴线切割为两个凸多边形
dividePoly 的参数 axis 用于指明要从哪个轴上进行切割,x 为轴上要进行切割的坐标。
函数进行切割的步骤如下:
- 遍历多边形的每一条边 j-i;
- 判断边的两个顶点与切割线的位置关系;
- 如果两个顶点一左一右或者其中一个在切割线上,那么在与切割线的交点处生成一个新的顶点,将其同时添加到 out1 和 out2 两个多边形中。对于 i 点,根据其在切割线左右两种情况,将其添加到 out1 或者 out2 中;
- 如果两个顶点都在切割线的同一边,那么将点 i 添加到其与切割线左右关系对应的多边形中;
- 如果两个顶点都在切割线上,那么将点 i 同时添加到 out1 和 out2 两个多边形中。
举个栗子,有如下的三角形
首先遍历边 AB,其两个端点在切割线的两边。这种情况增加点 D,同时添加到 out1 和 out2 中,然后将 B 添加到 out1 中,此时 out1{DB}, out2{D}。
然后遍历边 BC,其两个端点同样在切割线的两边。增加点 E,同时添加到 out1 和 out2 中,然后将 C 添加到 out2 中,此时 out1{DBE}, out2{DEC}。
最后遍历 CA,因为两个端点都在切割线的右边,所以此时将 A 添加到 out2 中,得到 out1{DBE}, out2{DECA}。遍历完毕,两个多边形都切割了出来。
addSpan solid span 的添加与合并
addSpan 用于将新添加的 solid span 根据其高度插入到有序链表中的合适位置,逻辑很直白。
需要注意的是,如果新添加的 span 与老的 span 存在重叠,将老的 span 合并到新 span 里:
如果两个 span 的顶部高度差在合并范围
flagMergeThr以内,则使用两个 span 中 area 较大的值(其实就是RC_WALKABLE_AREA了)。如果两个 span 的顶部高度差在合并范围
flagMergeThr以外,则使用新增 span 的 area 值。
上述的流程结束后,Recast 就将由三角形面构成的模型转换为了由 solid span 构成的体素空间——高度场,并且每一个 span 上都标记了其表面是否可供行走。如果止于这一步,再依靠 walkableClimb 进行格子的邻接判断,那么我们就已经得到了一个基于格子的导航网格。
- Title: Recast 学习记录之一:三角形光栅化
- Author: cybroxtu
- Created at: 2021-04-02 00:00:00
- Updated at: 2023-06-11 13:05:55
- Link: https://cybroxtu.pro/2021/04/02/pathfinding/recast_learning_1_rasterize_triangle/
- License: This work is licensed under CC BY-NC-SA 4.0.