Contents

UE4 SkeletalMesh的实时动态切割

技术概述

本系统提供了一套通用的针对SkeletalMesh的动态切割的解决方案,包括在UE4中动态的切割模型顶点,补充侧面顶点,增加切割后的截面,增加新增顶点的骨骼权重,动态改变模型的物理碰撞体。使得切割后的模型能够继续播放动画或者进行物理模拟。

问题背景

角色拥有特殊技能能够对场景中物体进行切割,并希望对可移动的敌人骨骼物体进行切割。由于需要多次切割并且要补充切割截面所以并不能使用shader方式去处理。目前市面上的主流解决方案是将SkeletalMesh变为可动态改变的ProceduralMesh,将当前pose下的顶点信息拷贝出来,转化为对ProceduralMesh进行切割。另一种是对整个人物做预处理,事先划分好不同的切割点和模块,切割时动态选择对应的切割点,播放相应的动画。第一种方案由于转化为没有关节的ProceduralMesh,无法继续播放动画或者进行真实的物理模拟,非常生硬;第二种方案对工作流影响太大,并且不支持多次对一个物体的切割。

https://papalqiblog.oss-cn-beijing.aliyuncs.com/blog/picture5fae24e68c5674b2aa0ae5c5oO3fqG6l01.gif

https://papalqiblog.oss-cn-beijing.aliyuncs.com/blog/picture5fae25b468d8647aa3b7f43fiYlN1y6b01.gif

因此,本方案支持直接对skeletalMesh进行切割,补充动态切割面截面,增加新增顶点的骨骼权重,动态改变模型的物理碰撞体。使得切割后的模型能够继续播放动画或者进行物理模拟。

技术思路

我们主要改变SkeletalMesh的两个部分,一个是它的渲染数据,另一个是它的物理数据。

  1. 对于渲染数据的更改,我们首先会将SkeletalMesh渲染数据拿到,由于拿到的顶点处于原始姿势,我们还要将顶点数据变换到pose下的数据,然后按照顶点索引组成的三角形为单位,根据顶点在平面两侧的位置判断,将数据分为两部分。如果三角形中的三个顶点同时处于一侧,我们将直接保留整个结果,如果三角形三个顶点隶属于平面的两侧,说明整个三角形处于边界,我们将增加新的顶点,并对三角形进行重新划分。之后我们将之前存储的重组面上的顶点进行三角剖分,得到我们的截面数据。
  2. 对于物理数据的更改,与渲染数据类似,将skeletalMesh身上的物理碰撞体与平面进行检测,如果完全处于平面一侧则完全保留,如果处于相交面上,我们则对它的大小进行简单的增减。

技术实现

顶点划分与侧边补面

我们所有的判断标准都是根据顶点位置是否存在于我的切割平面一侧,如果在我需要的一侧,我们将保留,如果在另一侧我们将丢弃掉这个顶点。当然由于我们是基于三角面的渲染,所以我们判断的准测应该是三角形而不是单一的顶点。所以触发丢弃,应该是整个三角形中所有的顶点都处于丢弃的一侧。对于顶点的所处位置判断其实并不困难。困难的主要原因是我们如何丢弃不需要的顶点。我们并不想在实时的情况下去真的从一整块内存中将其中的某些元素删除,因为这会造成 大量的内存移动的操作。

目前采用的方案是将整个三角形的Index都标记为0来实现最终的渲染隐藏的操作。

我们假设点为有顶点$P=(x_1,y_1,z_1)$,平面的法线为${N}=(A,B,C)$,平面上的点为$Q=(x_0,y_0,z_0)$所以平面的公式则可写为$A(x-x_0)+B(y-y_0) +C(z-z_0) = 0$,我们可以将公式变换成$Ax+By+Cz+D=0$的形式,其中$D=-Ax_0-By_0-Cz_0$,我们将点带入公式,如果大于0说明该点与法线同侧,如果小于0,则与法线异侧。

虽然我们前面进行了简单的丢弃,但是这并不完美,因为我们目前仅处理了完全在一侧的三角面,而对相交的三角形还没有做处理。我们需要的是一个平整的切割平面,而不是一个犬牙交错的切割面。

所以我们将进行切割面的补面,对于那些有些点在保留侧有些点在丢弃侧的那些三角形。我们拿到这个三角形,对其中每个边进行比较,如果其中的某条边上两个点都在同侧,我们添加两个到对应的堆栈中;如果某条边穿过了切割平面,我们将新增一个中间顶点。

我们假设处于Normal 的方向点的集合为A,反方向为B,我们假设三角形三个顶点为$a,b,c$三个点,并且他们的顺序也是abc。

  1. 遍历三角形,找到未处理的第一个点$p$,假设顶点$p$处于集合A/B,将$p$加入集合A/B。
  2. .对$p$点的下个顶点$p’$进行判断
    1. 如果$p$和$ p’$的集合相同则不做处理,跳转到第一步继续执行。
    2. 如果$p$和$ p’$的集合不相同,说明$p$和$ p’$间存在点$o$处于切割平面上。我们根据两点的位置方向适量,计算出新增点的位置。新增点为两侧功能拥有的点,所以我们将点分别加入A,B集合。

最后,每个三角形都划分为两个三角形,增加对应的Index。

增加截面

我们需要对切割的两部分中间进行补齐截面。我们将保存上一部分我们将采集到之前所有补面过程当中得到的新增点,由于在上一部分中,我们对于中间部分的每个三角形,都会生成两个新的点。这两个点也有机会成为最后截面的边,所以我们在记录的时候,是将两个新的点连接成为一个Edge来进行记录。这些点都处于切割平面上,所以我们可以顶点的三维数据进行投影到切割平面上,然后使用二维平面的算法去计算。

我们根据法线方向作为投影平面,将所有数据边进行投影。我们将寻找能够组成封闭多边形的集合。对于每个边,我们根据其中顶点位置,寻找最近的点作为它的临边,然后便利查找,知道回到自己的另一个顶点为止。至此,我们将得到一组多个多边形集合。 之后我们使用隔耳法进行三角剖分。将得到的顶点数据和index数据变换回3D空间。

由于需要对新增的点继续做动作,所以我们必须填充新增点和三角形的骨骼权重。对于新增顶点,我们直接取三角面中距离它最近的顶点的骨骼权重就可以了。

切割并且修改物理数据

我们将动态改变物理数据以保证物理模拟的近似准确。我们将对所有的bodyInstace进行甄别,看是否与我们的切割平面有交叉,并判断隶属于那一部分。对于交叉的物理数据,我们将根据质心与交叉面的切割距离,判断其应当属于那一部分。如果两方的差距不是很大处于中心位置,那么两边将都会保留这一物理碰撞体。

例如手臂,武器,我们如果不做特殊处理,将会导致物体悬空,我们会通过配置的方式,标记出每个skeletalMesh可能悬空的部分,我们在处理时识别到这一物理数据时,将对其骨骼的子节点物理进行解除物理约束,保证悬空部分的物理的正确。

应用/效果展示

https://papalqiblog.oss-cn-beijing.aliyuncs.com/blog/picture5fae120d8c5674b2cdb7dc59WK4FaMqF01.gif

https://papalqiblog.oss-cn-beijing.aliyuncs.com/blog/picture5fae219e8c5674b2c3e587c6syyvlw8501.gif