/images/avatar.png

Papalqi

UE4 SkeletalMesh的切割

技术概述 本系统提供了一套通用的针对SkeletalMesh的动态切割的解决方案,包括在UE4中动态的切割模型顶点,补充侧面顶点,增加切割后的截面,增加新增顶点的骨骼权重,动态改变模型的物理碰撞体。使得切割后的模型能够继续播放动画或者进行物理模拟。 问题背景 在DM138中,角色拥有特殊技能能够对场景中物体进行切割,并希望对可移动的敌人骨骼物体进行切割。由于需要多次切割并且要补充切割截面所以并不能使用shader方式去处理。目前市面上的主流解决方案是将SkeletalMesh变为可动态改变的ProceduralMesh,将当前pose下的顶点信息拷贝出来,转化为对ProceduralMesh进行切割。另一种是对整个人物做预处理,事先划分好不同的切割点和模块,切割时动态选择对应的切割点,播放相应的动画。第一种方案由于转化为没有关节的ProceduralMesh,无法继续播放动画或者进行真实的物理模拟,非常生硬;第二种方案对工作流影响太大,并且不支持多次对一个物体的切割。 因此,本方案支持直接对skeletalMesh进行切割,补充动态切割面截面,增加新增顶点的骨骼权重,动态改变模型的物理碰撞体。使得切割后的模型能够继续播放动画或者进行物理模拟。 技术思路 我们主要改变SkeletalMesh的两个部分,一个是它的渲染数据,另一个是它的物理数据。 对于渲染数据的更改,我们首先会将SkeletalMesh渲染数据拿到,由于拿到的顶点处于原始姿势,我们还要将顶点数据变换到pose下的数据,然后按照顶点索引组成的三角形为单位,根据顶点在平面两侧的位置判断,将数据分为两部分。如果三角形中的三个顶点同时处于一侧,我们将直接保留整个结果,如果三角形三个顶点隶属于平面的两侧,说明整个三角形处于边界,我们将增加新的顶点,并对三角形进行重新划分。之后我们将之前存储的重组面上的顶点进行三角剖分,得到我们的截面数据。 对于物理数据的更改,与渲染数据类似,将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。 遍历三角形,找到未处理的第一个点$p$,假设顶点$p$处于集合A/B,将$p$加入集合A/B。 .对$p$点的下个顶点$p'$进行判断 如果$p$和$ p'$的集合相同则不做处理,跳转到第一步继续执行。 如果$p$和$ p'$的集合不相同,说明$p$和$ p'$间存在点$o$处于切割平面上。我们根据两点的位置方向适量,计算出新增点的位置。新增点为两侧功能拥有的点,所以我们将点分别加入A,B集合。 最后,每个三角形都划分为两个三角形,增加对应的Index。 增加截面 我们需要对切割的两部分中间进行补齐截面。我们将保存上一部分我们将采集到之前所有补面过程当中得到的新增点,由于在上一部分中,我们对于中间部分的每个三角形,都会生成两个新的点。这两个点也有机会成为最后截面的边,所以我们在记录的时候,是将两个新的点连接成为一个Edge来进行记录。这些点都处于切割平面上,所以我们可以顶点的三维数据进行投影到切割平面上,然后使用二维平面的算法去计算。 我们根据法线方向作为投影平面,将所有数据边进行投影。我们将寻找能够组成封闭多边形的集合。对于每个边,我们根据其中顶点位置,寻找最近的点作为它的临边,然后便利查找,知道回到自己的另一个顶点为止。至此,我们将得到一组多个多边形集合。 之后我们使用隔耳法进行三角剖分。将得到的顶点数据和index数据变换回3D空间。 由于需要对新增的点继续做动作,所以我们必须填充新增点和三角形的骨骼权重。对于新增顶点,我们直接取三角面中距离它最近的顶点的骨骼权重就可以了。 切割并且修改物理数据 我们将动态改变物理数据以保证物理模拟的近似准确。我们将对所有的bodyInstace进行甄别,看是否与我们的切割平面有交叉,并判断隶属于那一部分。对于交叉的物理数据,我们将根据质心与交叉面的切割距离,判断其应当属于那一部分。如果两方的差距不是很大处于中心位置,那么两边将都会保留这一物理碰撞体。 例如手臂,武器,我们如果不做特殊处理,将会导致物体悬空,我们会通过配置的方式,标记出每个skeletalMesh可能悬空的部分,我们在处理时识别到这一物理数据时,将对其骨骼的子节点物理进行解除物理约束,保证悬空部分的物理的正确。 应用/效果展示

UE4 物理系统实现

虚幻引擎4使用 PhysX 3.3 物理引擎来模拟物理效果。所有物理运动(坠落或受力的物理形体)以及碰撞(物理形体的相互作用)都由 PhysX 管理。 一.Physx 1.1Physx简介 UE4.21前的版本采用的是NVIDIA的PhysX做为其默认的物理引擎,用于计算3D世界的碰撞查询与物理模拟。自4.21版本开始重构了代码调用,兼容使用Chaos物理系统,4.26才会实装,如要使用的话是需要自己构建的。 由于Epic 和NVIDIA的PY交易。Epic为UE4开发者们提供PhysX 3.3.3的基于CPU实现的二进制代码访问权,而且还包括其C++源代码访问权,以及布料库和可破坏物体库。 现在除了可以获得虚幻引擎4的完整C++源代码外,还可以查看和修改此PhysX代码。 参见:https://ue4community.wiki/legacy/physx-integrating-physx-code-into-your-project-ryzw4tj3 Physx文档:https://gameworksdocs.nvidia.com/PhysX/3.3/PhysXGuide/Index.html 1.1.1使用debug 模式检测物理 UE4 使用NVIDIA 的PhysX 3.3物理引擎来模拟物理效果,使用 APEX 模拟 destruction 和 clothing。对于PhyX,它对于UE4来说就是一个提供输入然后获取输出的黑 盒,不过可以通过NVIDIA提供的PhysX Visual Debugger(PVD)来进行可视化调试在编辑器运行游戏 输入pvd connect我们就可以得到实时的物理调试结果。 整个debug软件是比较重的,功能众多,如果大家在项目中遇到了一下物理上的性能问题,强烈建议可以打开pvd看下场景里的动态rigidbody情况,碰撞穿插解算的复杂度,一般严重的性能问题都是在这方面的。 1.1.2基本数据结构 PxPhysics:用来创建Physx各种实用组件,像场景,Shape,Rigidbody,布料,粒子系统等。 PxScene:物理场景,是碰撞盒组件的模拟环境,它的创建是根据PxSceneDesc属性来的。一个大型系统可以有多个场景,不同场景的组件相互不影响,比如UE4,使用了2个场景,用来模拟同步和异步的物理模拟功能。 PxSceneDesc:有如下属性: gravity 重力大小和方向, PxSimulationEventCallback 模拟事件回调, PxContactModifyCallback 碰撞解算修改回调, PxCCDContactModifyCallback CCD的碰撞解算修改回调, filterShader 全局的碰撞分类处理函数, cpuDispatcher Cpu线程分配器 PxCooking:碰撞盒得有形状,我们知道物理引擎里碰撞盒不仅仅是只有胶囊体,长方形,球形,还有凸包体,复杂的静态地表碰撞盒,三角形面片构成的碰撞盒。对于比较复杂的碰撞盒,Phyx支持用PxCooking类接受面片数据,然后create出可以使用的碰撞mesh。 Rigidbody: 它首先是Actor,作为Scene的基础实体,然后可以分成静态的和动态的,静态的如房子,各种场景部件,有自带的预处理功能,处理碰撞等运算性能高很多,动态的像人,车,可以运动,但性能会差一些,动态Rigidbody里用的比较多的是PxRigidDynamic,而Articulation是专门给类似布娃娃这样的系统设计的。 PxShape:SimulationFilterData 这个是做碰撞模拟类别划分的,这边设定好SimulationFilterData,在场景下的filterShader进行区分,可以控制哪些碰撞shape之间是可以相互进行碰撞计算的。 PxMaterial:DynamicFriction,StaticFriction动摩擦,静摩擦,Restitution弹性系数,FrictionCombineMode 摩擦力计算的方式,比如两个物体碰撞了,摩擦力双方不同,可以选择取最小的,最大的,或者平均一下,RestitutionCombineMode,弹力计算模式,摩擦力的一样。 1.1.3几何体 UE4 里面使用的几何体完全匹配于Physx的类型。 球 胶囊体 box 平面 凸包 三角形Mesh 需要注意的问题是,凸包的在Physx里面限定的个数是255.

UE4RenderingDependencyGraph

什么是RDG 渲染依赖性图表(Rendering Dependency Graph) 也被称为RDG或渲染图表,是一个基于图表的调度系统,用于执行渲染管线的整帧优化。它利用DirectX12这样的现代API,使用自动异步计算调度以及更高效的内存管理和屏障管理来提高性能。 RDG的概念如下:在GPU上并非立即执行通道,而是延迟到整个帧已记录到依赖性图表数据结构之中后再执行。当完成了对所有通道的收集之后,则按照依赖性的排序顺序对图表进行编译和执行。这主要是因为DirectX 12、Vulkan和Metal2之类的现代图形API选择将低级GPU管理的负担转移至应用程序本身。这提供了一个机会,可以利用渲染管线的高级情境来驱动调度,从而提高性能并且简化渲染堆栈。 我们今天的RDG有什么好处,就要看之前的方式有多菜。来看一下传统的下图分别是07年和17年的渲染系统的复杂程度 可以参考: http://www.frostbite.com/2007/04/frostbite-rendering-architecture-and-real-time-procedural-shading-texturing-techniques 为什么需要RDG 这里当然不是非常的具体,不过我们都能看出这里非常的复杂。 当然,我们可以进行一些简单的简化,如下图 战地四的pass数(Feature) 面临的挑战: 显示的代码驱动的即时渲染模式 显示的资源管理 手动的ESRAM管理 每一个不同的游戏团队有着不同的实现方式 不同的渲染系统有紧密的耦合性 对于管线定制的扩展性差 游戏团队必须对这些定制fork/diverge 代码数飞速增长,将产生很多重复性的代码 所以,我们需要将整个的渲染结构进行一个更高层次的抽象,让其拥有更高的扩展性,模块化,更好的可视化和debug,资源的自动管理,解耦模块并使其可自由组合。 这就是新的结构: RDG设计理念 我们将抛弃即时的渲染模式;将渲染代码分离到pass中,大致分为三个阶段 1.Setup phase 2.Compile phase 3.Execute phase Setup phase 我们将定义这个pass是一个render pass, 还是compute pass我们将定义每一个resource,对于输入和输出对于renderpass ,我们要定义资源的使用其是read,write,还是create。当然对于长期贮存的资源我们是要imported进RDG中,例如History buffer for TAA,Backbuffer Compile phase 剔除没有使用的资源和pass 计算资源的生命周期 根据使用情况分配实际的GPU资源

AR Kit sample分析

AR Kit sample分析 arkit动画模型规范 关卡蓝图流程 Live Link Plugin 使用Live Link Plugin驱动面部追踪数据,包括当前的面部表情和头部旋转,作为一个运动捕获设备来puppeteer一个onscreen charater. 概览 对比51个独立的面部位姿。这些位姿在ARKit SDK内部,每一个位姿对应面部的一个特定部分。比如左眼,右眼,嘴巴等。位姿的值在[0.0, 1.0]间变化。举例来说,如果一个用户闭上左眼,那么LeftEyeBlink会在[0.0, 1.0]之间变化。 也就是说,用户的面部自然移动时,所有的51个位姿会被SDK评估并分配一个值。UE4 ARKit集成了所有的51个位姿,通过Live Link Plugin导入到UE4中。这51个值能够驱动实时的任务面部表情。 需要做的是捕获并驱动人物的头部来确保角色内容能够使用这51个数据。这51个反馈数据在[0.0, 1.0]之间,因而十分适合驱动人物角色表情。 详情请参见: arkit动画模型规范 设置 角色设置 创建一个blend shape角色模型基于面部动画,包括51个blend shapes.理想的,这些blend shapes的命名也与Apple ARKit一致。 导入到UE4,确保导入了Blend Shapes 在DefaultEngine.ini文件中开启面部追踪。 打开项目,在项目设置中设置AR为On. 创建并应用一个Data Asset来进行面部追踪。 内容浏览器,Miscellaneous > Data Asset 选择ARSessionConfig 双击 new Asset 设置 World Alignment: Camera Session Type: Face Horizontal Plane Detecion: Off Vertical Plane Detection: Off Enable Auto Focus: Off Light Estimation Mode: Off Enable Automatic Camera Overlay\Trakcing :Off 在关卡蓝图中Begin Play后,带动Start AR Session,并设置ARSessionConfig数据。 创建一个动画蓝图,使用LiveLinkPose节点,名称为FaceAR.

arkit动画模型规范

arkit动画模型规范 Neutral-静态的pose 一.创建Blendshape 曲线 苹果通过ARKit面部捕捉技术,提供开发者使用iphoneX以上的机器的前置摄像头,实现面部捕捉。blendshape名称是写死的,总共51个blendshape我们需要去实现,不过我们可以实现一些而不是全部。强烈建议的是,命名和功能需要保持一致。所有的值是在0-1之间. eyeBlinkLeft eyeLookDownLeft eyeLookInLeft eyeLookOutLeft eyeLookUpLeft eyeSquintLeft eyeWideLeft eyeBlinkRight eyeLookDownRight eyeLookInRight eyeLookOutRight eyeLookUpRight eyeSquintRight eyeWideRight jawForward jawRight jawOpen mouthClose mouthFunnel mouthPucker mouthLeft mouthRight mouthSmileLeft mouthSmileRight mouthFrownLeft mouthFrownRight mouthDimpleLeft mouthDimpleRight mouthStretchLeft mouthStretchRight mouthRollLower mouthRollUpper mouthShrugLower mouthShrugUpper mouthPressLeft mouthPressRight mouthLowerDownLeft mouthLowerDownRight mouthUpperUpLeft mouthUpperUpRight browDownLeft browDownRight browInnerUp browOuterUpLeft cheekSquintRight noseSneerLeft noseSneerRight eyeBlinkLeft ;eyeLookDownLeft ;eyeLookInLeft eyeLookOutLeft eyeLookUpLeft eyeSquintLeft eyeWideLeft eyeBlinkRight eyeLookDownRight eyeLookInRight eyeLookOutRight eyeLookUpRight eyeSquintRight eyeWideRight jawForward jawLeft jawRight jawOpen mouthClose mouthFunnel mouthPucker mouthLeft mouthRight mouthSmileLeft mouthSmileRight mouthFrownLeft mouthFrownRight mouthDimpleLeft mouthDimpleRight mouthStretchLeft mouthStretchRight mouthRollLower mouthRollUpper mouthShrugLower mouthShrugUpper mouthPressLeft mouthPressRight mouthLowerDownLeft mouthLowerDownRight mouthUpperUpLeft mouthUpperUpRight browDownLeft browDownRight browInnerUp browOuterUpLeft browOuterUpRight cheekPuff cheekSquintLeft cheekSquintRight noseSneerLeft noseSneerRight 二.

UE4动画系统源码剖析

动画基础概念 3D模型动画的基本原理是让模型中各顶点的位置随时间变化。主要种类有Morph(变形)动画,关节动画和骨骼蒙皮动画(SkinnedMesh)。 (自己搭的blog被黑了,只有本地备份,太伤了,所以文章会有一些格式问题) 从动画数据的角度来说,三者一般都采用关键帧技术,即只给出关键帧的数据,其他帧的数据使用插值得到。但由于这三种技术的不同,关键帧的数据是不一样的。 Morph(渐变,变形)动画是直接指定动画每一帧的顶点位置,其动画关键中存储的是Mesh所有顶点在关键帧对应时刻的位置。 关节动画的模型不是一个整体的Mesh,而是分成很多部分(Mesh),通过一个父子层次结构将这些分散的Mesh组织在一起,父Mesh带动其下子Mesh的运动,各Mesh中的顶点坐标定义在自己的坐标系中,这样各个Mesh是作为一个整体参与运动的。动画帧中设置各子Mesh相对于其父Mesh的变换(主要是旋转,当然也可包括移动和缩放),通过子到父,一级级的变换累加(当然从技术上,如果是矩阵操作是累乘)得到该Mesh在整个动画模型所在的坐标空间中的变换(从本文的视角来说就是世界坐标系了,下同),从而确定每个Mesh在世界坐标系中的位置和方向,然后以Mesh为单位渲染即可。关节动画的问题是,各部分Mesh中的顶点是固定在其Mesh坐标系中的,这样在两个Mesh结合处就可能产生裂缝。 第三类就是骨骼蒙皮动画即SkinnedMesh了,骨骼蒙皮动画的出现解决了关节动画的裂缝问题,而且效果非常酷,发明这个算法的人一定是个天才,因为SkinnedMesh的原理简单的难以置信,而效果却那么好。骨骼动画的基本原理可概括为:在骨骼控制下,通过顶点混合动态计算蒙皮网格的顶点,而骨骼的运动相对于其父骨骼,并由动画关键帧数据驱动。一个骨骼动画通常包括骨骼层次结构数据,网格(Mesh)数据,网格蒙皮数据(skin info)和骨骼的动画(关键帧)数据。下面将具体分析。 SkinnedMesh中文一般称作骨骼蒙皮动画,正如其名,这种动画中包含骨骼(Bone)和蒙皮(Skinned Mesh)两个部分,Bone的层次结构和关节动画类似,Mesh则和关节动画不同:关节动画中是使用多个分散的Mesh,而Skinned Mesh中Mesh是一个整体,也就是说只有一个Mesh,实际上如果没有骨骼让Mesh运动变形,Mesh就和静态模型一样了。 Skinned Mesh技术的精华在于蒙皮,所谓的皮并不是模型的贴图(也许会有人这么想过吧),而是Mesh本身,蒙皮是指将Mesh中的顶点附着(绑定)在骨骼之上,而且每个顶点可以被多个骨骼所控制,这样在关节处的顶点由于同时受到父子骨骼的拉扯而改变位置就消除了裂缝。 Skinned Mesh这个词从字面上理解似乎是有皮的模型,哦,如果贴图是皮,那么普通静态模型不也都有吗?所以我觉得应该理解为具有蒙皮信息的Mesh或可当做皮肤用的Mesh,这个皮肤就是Mesh。 而为了有皮肤功能,Mesh还需要蒙皮信息,即Skin数据,没有Skin数据就是一个普通的静态Mesh了。 Skin数据决定顶点如何绑定到骨骼上。顶点的Skin数据包括顶点受哪些骨骼影响以及这些骨骼影响该顶点时的权重(weight),另外对于每块骨骼还需要骨骼偏移矩阵(BoneOffsetMatrix)用来将顶点从Mesh空间变换到骨骼空间。在本文中,提到骨骼动画中的Mesh特指这个皮肤Mesh,提到模型是指骨骼动画模型整体。骨骼控制蒙皮运动,而骨骼本身的运动呢?当然是动画数据了。 每个关键帧中包含时间和骨骼运动信息,运动信息可以用一个矩阵直接表示骨骼新的变换,也可用四元数表示骨骼的旋转,也可以随便自己定义什么只要能让骨骼动就行。除了使用编辑设定好的动画帧数据,也可以使用物理计算对骨骼进行实时控制。 线性混合蒙皮算法 在骨骼动画的蒙皮算法中,出现最早、最经典,也是应用最为广泛的算法是线性混合蒙皮算法。 根据骨骼动画的基本原理,动画模型之所以能够运动,是由于其骨骼带动了蒙在骨骼之上的皮肤一同动作,实现了动画效果。因此,因首先设置好模型骨架以及各骨骼之间的关联性,当运动数据到来时,计算皮肤顶点的新位置,就可以完成模型的运动。 黑色与白色的皮肤顶点分别与其相同颜色的骨骼相绑定。 方框里的皮肤顶点离两个骨骼关节最近,它们同时受到两个骨骼关节的影响。当骨架运动的时候,对于这些受多个骨骼共同影响的皮肤顶点,我们要计算它们变换后的位置信息,即找到皮肤网格自动变形后的方法,传统一般采用线性混合蒙皮算法。线性混合蒙皮算法是由 Lander 最早提出并实现的一种柔性绑定算法。Lander 利用线性混合蒙皮算法实现了人体上臂的动画,解决了之前的刚性绑定算法在关节处的失真问题。该算法的基本原理可以用下列公式表示 V表示顶点变换前的世界坐标系中的位置,V’表示顶点变换后的位置,i 表示同时影响该顶点的骨骼数量,一般取 2-4 之间的值。W_i表示第 i 个骨骼对该顶点的施加的影响权重,取 0-1 之间的值,M_i表示在模型初始参考姿势下,与顶点相关的第 i 个骨骼由本地坐标转换为世界坐标的转换矩阵(即骨骼变换的绝对矩阵),通过矩阵M_i能将骨骼 i 从初始位置转换到动画数据来到时的新位置上. 综上所述,线性混合蒙皮算法即是求得一个顶点在每个骨骼影响下的一系列新的位置,然后对这些位置数据进行加权平均计算得到最后的结果。 在线性混合蒙皮算法中,顶点的新位置 V′是通过其初始位置V 乘以一个矩阵 C 得到,这个矩阵被称为变换矩阵。  我们可以使用 OFFSET(偏移)的 3 个量来表示子关节相对父关节的偏移量;用 CHANNELS 来表示关节旋转通道数量和旋转顺序,其中根关节有6个通道,其他关节有3个通道,与根关节相比少了XYZ的位置(position)信息,这是因为其他关节都可以根据相对其于父关节的偏移量计算坐标位置。 运动数据对应的是骨架信息中各关节点的层次数据,即CHANNELS 中 Zrotation Xrotation Yrotation 顺序的数据。对于子关节来说,平移信息存储在骨架信息的 OFFSET 中,旋转信息则来自于运动数据部分;对于根关节来说,平移量是 OFFSET 和运动数据部分中定义的平移量之和。要得到蒙皮所需的绝对变换矩阵,首先需要根据 BVH 文件中的旋转数据分别创建三个方向轴(Y 轴,X 轴,Z 轴)对应的旋转矩阵,然后将它们按顺序相乘得到矩阵R (也称相对矩阵): 绝对变换矩阵是由关节的相对矩阵乘上它的父关节的绝对矩阵得到的,其中,根关节的绝对变换矩阵就是它的相对矩阵。因此,根据骨架各关节之间的关系,可以计算出每一个关节的绝对变换矩阵,用来将关节的本地坐标变换为世界坐标。