UE4——SliceProceduralMesh 函数解析及BUG修复

写在前面

近日在项目中需要实现模型剖切的相关功能,总的来说目前实现思路分两种:

  1. 利用材质达到部分模型不渲染
  2. 切割模型

同时,切割模型的实现又分为两种,一个是使用UE4提供的ProceduralMeshComponent插件,其中附带模型切割函数,另一种则是使用模型处理库,对模型进行布尔运算,当然这种也是基于ProceduralMeshComponent。出于方便,尝试使用引擎自带的函数,在使用中出现剖切面缝合问题,主要表现为无法识别有厚度的物体(例如圆环),在缝合过程中会将剖面完全封闭,不能达到理想效果。因此,对SliceProceduralMesh进行了简单的学习与更改。

SliceProceduralMesh 函数位置

//.h
Engine\Plugins\Runtime\ProceduralMeshComponent\Source\ProceduralMeshComponent\Public\KismetProceduralMeshLibrary.h

//.cpp
Engine\Plugins\Runtime\ProceduralMeshComponent\Source\ProceduralMeshComponent\Private\KismetProceduralMeshLibrary.cpp

函数原型

/** 
	 *	Slice the ProceduralMeshComponent (including simple convex collision) using a plane. Optionally create 'cap' geometry. 
	 *	@param	InProcMesh				ProceduralMeshComponent to slice
	 *	@param	PlanePosition			Point on the plane to use for slicing, in world space
	 *	@param	PlaneNormal				Normal of plane used for slicing. Geometry on the positive side of the plane will be kept.
	 *	@param	bCreateOtherHalf		If true, an additional ProceduralMeshComponent (OutOtherHalfProcMesh) will be created using the other half of the sliced geometry
	 *	@param	OutOtherHalfProcMesh	If bCreateOtherHalf is set, this is the new component created. Its owner will be the same as the supplied InProcMesh.
	 *	@param	CapOption				If and how to create 'cap' geometry on the slicing plane
	 *	@param	CapMaterial				If creating a new section for the cap, assign this material to that section
	 */
	static void SliceProceduralMesh(UProceduralMeshComponent* InProcMesh, FVector PlanePosition, FVector PlaneNormal, bool bCreateOtherHalf, UProceduralMeshComponent*& OutOtherHalfProcMesh, EProcMeshSliceCapOption CapOption, UMaterialInterface* CapMaterial);

函数相关参数的注释在代码中已经标注,此处不过多赘述。下面便是这篇文章的重点,函数是如何完成剖切并缝合的,我们又应该如何修改。

问题探究

由于函数较为庞大,此处就不进行摘录,有兴趣的读者可以自行查看源码

简单地说,切割的步骤大致如下:

  1. 遍历所有Section,找出被切割面切分的Section。
  2. 依据切割面将应该切割的Section切分,并得到切割边。
  3. 利用切割边构建切割面
  4. 三角化

显然,问题出在依据切割边构建切割平面时,该函数为:

FGeomTools::Buid2DPolysFromEdges()

其原理如下:

  1. 找出所有封闭的多边形。
  2. 构建多边形并修正法线方向。

也就是说,之所以出现缝合问题,主要是封闭多边形存在环状结构,也即是说,对于圆环来说,形成了两个封面,一大一小,因此缝合出现问题。

问题修复

在此简单提出一种修复算法。对于存在环状结构的切面,我使用的方式是将切面切割为多个“无孔”的多边形,其过程如下:

  1. 遍历所有封闭多边形,判断其外存在的封闭多边形数量,为奇数个是认定其为内边,并将其设置为上一层多边形的内边。为偶数个时,认定其为外边。
  2. 遍历所有外边,没有内边的多边形放入最终输出的多边形队列,将有内边的多边形压入堆栈。
  3. 出栈一个多边形,任取一内边,判断切割该内边是否影响其他内边。如无影响,则切割。笔者切割方式如下,任取内边上一点,找出内边上距其最远点,分别计算两点到外圈多边形各个边距离,找出距离最小的边,过点做垂线,得到的两条垂线则为切割线。判定切割线是否影响其他内边的方式为计算切割线与其他内边是否相交。如不满足,则重新再内边上取点,如已遍历内边上所有点,则重新取一内边。(极少数情况下会出现无法切割的问题,此时使用备用算法,从之前的任取一点,另一点为其最远点的方式,变为任取内边上两点)
  4. 记切割后多边形分别为A,B。遍历原多边形内边(已切割除外),判断其位于A或B中,并将其设置为A或B的内边。
  5. 判断A,B是否含有内边,将无内边的多边形放入最终输出多边形队列,将有内边的多边形压入栈。
  6. 重复3-5步,直到含内边的多边形栈为空。
  7. 输出无内边的多边形队列。

使用上述方式可以简单完成含孔多边形切割,不过编写代码时需要注意切割后多边形法线问题。

你可能感兴趣的:(UE4)