|
Unreal动画混合权重(Weight)的原理分析
BlendSpace是unreal中动画二维混合工具,常见的应用有:主角战斗视角下速度和方向的locomotion动画混合,竖直方向和水平方向的lookat动画混合。
本文主要分析BlendSpace的实现原理,并不是使用教程。
编辑器Editor部分
基本原理
融合图中,所有白色的菱形点,都是策划拖动进来的动画(AnimSequence),这个在unreal引擎中叫BlendSamples,下文统一叫做采样点。
编辑器首先会对这些采样点从0开始进行编号,引擎后续对采样点的引用都用编号来表示;然后,会对这些采样点进行三角剖分,从而得到一系列的三角形。
在编辑器里,策划已经配置好了格子的行数和列数;编辑器会遍历所有格子点(GridElement),检查当前格子点所在的三角形,根据三角形的三个顶点,计算各自权重,融合得到格子点的动画信息。一个格子点的动画信息,就是三个采样点的编号和权重。
源码分析
编辑器的入口函数在:SAnimationBlendSpace.cpp SBlendSpaceEditor::ResampleData(),此函数的基本原理和我上述的描述类似,不再赘述。
下面重点介绍融合机制:就是如何从一个三角形的三个采样点,获取其中一个格子点的动画融合权重?
核心融合代码的调用关系为:
SBlendSpaceEditor::ResampleData SAnimationBlendSpace.cppFBlendSpaceGrid::GenerateGridElements AnimationBlendSpaceHelpers.cppFBlendSpaceGrid::FindTriangleThisPointBelongsTo AnimationBlendSpaceHelpers.cppFMath::GetBaryCentric2D UnrealMath.cpp
<pre class="brush:cpp;" style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">FVector FMath::GetBaryCentric2D(const FVector& Point, const FVector& A, const FVector& B, const FVector& C)
{
float a = ((B.Y-C.Y)(Point.X-C.X) + (C.X-B.X)(Point.Y-C.Y)) / ((B.Y-C.Y)(A.X-C.X) + (C.X-B.X)(A.Y-C.Y));
float b = ((C.Y-A.Y)(Point.X-C.X) + (A.X-C.X)(Point.Y-C.Y)) / ((B.Y-C.Y)(A.X-C.X) + (C.X-B.X)(A.Y-C.Y));
return FVector(a, b, 1.0f - a - b);
}</pre>
这里的数学知识可以温习下参考文献2
假设三角形为ABC,任意一格子点为P,A点对P的融合权重a == 三角形PBC的面积 / 三角形ABC的面积。
三角形PBC的面积 = PC X BC = (P.x - C.x) (B.y - C.y) - (B.x - C.x) * (P.y - C.y)
三角形ABC的面积 = AC X BC = (A.x - C.x) (B.y - C.y) - (B.x - C.x) * (A.y - C.y)
X表示向量叉乘运算,上面的简单推导证明了函数GetBaryCentric2D的正确性。
Runtime部分
基本原理
根据运行时输入的两个参数BlendInput,从而确定格子坐标GridIndex,找到所在格子,一个格子对应有四个格子点LeftTop、RightTop、LeftBottom、RightBottom。
引擎将每个格子,都Normalize为1 * 1的正方形,保证每个格子的总面积是 1。
BlendInput的点和LeftTop、RightTop、LeftBottom、RightBottom分别组成一个长方形,每个长方形的面积就是对应格子点的最终融合权重,基本原理和上面三角形融合类似。
源码分析
源码入口UBlendSpaceBase::TickAssetPlayer BlendSpaceBase.cpp
融合代码的调用堆栈:
UBlendSpaceBase::GetSamplesFromBlendInput BlendSpaceBase.cppUBlendSpace::GetRawSamplesFromBlendInput BlendSpace.cppUBlendSpace::GetGridSamplesFromBlendInput BlendSpace.cpp
Unreal的BlendSpace混合速率(timeScale)的原理分析
可以注意到,BlendSpace的每个Sample都有一个RateScale属性,表示当前Sample的播放速率。
BlendSpace允许出现两个Animation相同的Sample,而且这两个Sample的RateScale可以不同。
BlendSpace的最终播放速率的计算都在:Runtime阶段。
合并相同Animation的Sample
BlendSpace会把属性Animation相同的Samples,合并为一个Sample。
假设:Animation相同的Samples集合设为SameSamples,集合中第i个Sample用SameSamples表示,合并后的Sample设为MergedSample。
MergedSample.RateScale的计算,可以按如下方式理解:
先对SameSamples的Weights进行归一化(保证总和是1),设为NormalWeightsMergedSample.RateScale = Σ SameSamples.RateScale * NormalWeights
MergedSample.Weight == Σ SameSamples.Weight
这块的代码在:UBlendSpaceBase::GetSamplesFromBlendInput BlendSpaceBase.cpp
按照Weight最高的Sample的RateScale进行播放
这块的源码直接看:UBlendSpaceBase::TickAssetPlayer BlendSpaceBase.cpp
参考文档:
https://api.unrealengine.com/INT/API/Runtime/Core/Math/FMath/GetBaryCentric2D/index.html
http://mathworld.wolfram.com/BarycentricCoordinates.html
https://docs-origin.unrealengine.com/latest/CHN/Engine/Animation/Blendspaces/index.html
|
|