UE4源码阅读_骨骼模型和动画系统_Mesh
写在前面
1. Mesh数据
SkeletalMeshComponent中和骨骼模型相关的代码主要封装在USkeletalMesh类。
USkeletalMesh包含两部分数据:骨骼数据,和模型网格数据。拥有蒙皮绑定和应用动画变形网格的功能。
其中骨骼相关数据都封装在USkeleton类,其中包括了骨架数据、虚拟骨骼、插槽、重定向等逻辑,以及一个对外的FReferenceSkeleton类,存储骨骼姿势数据。
而模型网格数据及流程主要封装在USkeletalMesh类,本篇仅简述该部分。
1.1 几何数据
- FbxSkeletalMeshImport中的FbxImporter类负责导入FBX资源,并生成FSkeletalMeshModel对象,该对象储存着模型网格的几何数据。
- 在USkeletalMesh对象中持有几何数据的引用,但该数据不会在Runtime时使用,Runtime时使用的是由该几何数据初始化的FSkeletalMeshRenderData对象。
TSharedPtr<FSkeletalMeshModel> ImportedModel;
1.2 运行时数据
在运行期间,USkeletalMesh的Mesh相关数据存储在FSkeletalMeshRenderData中
RenderData的数据进一步存在FSkeletalMeshLODRenderData
LODRenderData:
- 含有当前LOD中所有的Mesh顶点数据,例如index,position,UV,Normal,Tangent等,还有每个顶点的影响骨骼和权重。
- 并且还有一个划分不同section的数据结构,其中存储着某个section在各个buffer里的连续分布情况
初始化RenderData
由ImportedModel初始化SkelMesh的RenderData:
调用FSkeletalMeshRenderData::Cache进行初始化,该接口负责遍历ImportedModel的LODModel(FSkeletalMeshLODModel)数据,逐一创建对应的LODRenderData对象,用LODModel数据初始化该对象,初始化结束后将LODRenderData加入RenderData的LODRender列表,供后续使用。
LODRenderData的初始化数据和流程:BuildFromLODModel
- 根据ImportModel的LOD数据中的Section数据,初始化LODRenderData中的Section列表
- 向StaticVertexBuffers赋值所有顶点数据:坐标,切线,UV数据
- 向SkinWeightVertexBuffer初始化蒙皮权重
- 如果有Cloth数据,会初始化ClothVertexBuffer
- 赋值ActiveBoneIndices和RequiredBones
ActiveBoneIndices:这个LOD激活的骨骼集合
RequiredBones:渲染这个LOD时需要被更新的骨骼集合,可能包括一些不会被渲染的骨骼。这里需要保存从Root到被渲染骨骼的骨骼链,且要严格的按照骨骼顺序存储,方便合并。
1.3 创建渲染对象
FSkeletalMeshObject:渲染对象的基类,通过该对象从Game线程往渲染线程传递数据。
1. CreateRenderState_Concurrent
创建USkinnedMeshComponent组件需要的渲染线程的信息
其中最主要的功能是根据渲染设置:静态渲染、CPU蒙皮或GPU蒙皮,创建对应的子类对象:
- FSkeletalMeshObjectStatic
- FSkeletalMeshObjectCPUSkin
- FSkeletalMeshObjectGPUSkin
这三个类都继承自FSkeletalMeshObject,UE4默认是GPU蒙皮
因为SkinnedMeshComp继承自ActorComponent,该接口在ActorComp初始化时被调用。
2. FSkeletalMeshObjectGPUSkin
FSkeletalMeshObjectGPUSkin::InitResources
- 由RenderData(LODRenderData)对FSkeletalMeshObjectLOD列表进行数据初始化
- FSkeletalMeshObjectLOD::InitResources
○ 初始化顶点蒙皮权重缓冲:MeshObjectWeightBuffer
○ 初始化顶点颜色缓冲:MeshObjectColorBuffer
○ 获取RenderLODData中的顶点数据,存到中间变量FVertexFactoryBuffers(纯数据对象)
○ 用Buffers初始化MeshObjectLOD成员变量GPUSkinVertexFactories(FVertexFactoryData对象)
○ 如果有布料数据,则拿FVertexFactoryBuffers也初始化ClothVertexFactories
FSkeletalMeshObjectGPUSkin::Update
由游戏线程向渲染线程的队列添加渲染指令和数据
调用流程:
- UWorld::SendAllEndOfFrameUpdates
for (UActorComponent* Component : ComponentsThatNeedEndOfFrameUpdate)
for (UActorComponent* Component : ComponentsThatNeedEndOfFrameUpdate_OnGameThread)
Comp->DoDeferredRenderUpdates_Concurrent
-
ActorComponent::DoDeferredRenderUpdates_Concurrent
两个标志位:bRenderTransformDirty、bRenderDynamicDataDirty
当帧是否有更新(rotation、transform、scale改变),或者DynamicData的数据变化。
数据有变动的话,则分别调用SendRenderTransform_Concurrent、SendRenderDynamicData_Concurrent渲染新数据 -
USkinnedMeshComponent::SendRenderDynamicData_Concurrent
调MeshObject->Update -
FSkeletalMeshObjectGPUSkin::Update
○ 用RenderData创建一个渲染线程使用的数据对象DynamiacData(FDynamicSkelMeshObjectDataGPUSkin)
○ 把相关的渲染数据放到指令列表,后面由渲染线程调用。
FSkeletalMeshObjectGPUSkin* MeshObject = this;
ENQUEUE_RENDER_COMMAND(SkelMeshObjectUpdateDataCommand)(
[MeshObject, FrameNumberToPrepare, RevisionNumber, NewDynamicData, GPUSkinCache](FRHICommandListImmediate& RHICmdList)
{
FScopeCycleCounter Context(MeshObject->GetStatId());
MeshObject->UpdateDynamicData_RenderThread(GPUSkinCache, RHICmdList, NewDynamicData, nullptr, FrameNumberToPrepare, RevisionNumber);
}
2. 渲染
2.1 渲染数据
DynamicData:FDynamicSkelMeshObjectDataGPUSkin
储存GPU顶点蒙皮需要的矩阵数据,由游戏线程创建并发送到渲染线程进行更新
# Vertex数据
//蒙皮矩阵
TArray<FMatrix> ReferenceToLocal;
TArray<FMatrix> PreviousReferenceToLocal;
TArray<FTransform> MeshComponentSpaceTransforms;
# Morph数据
TArray<FActiveMorphTarget> ActiveMorphTargets;
TArray<float> MorphTargetWeights;
TArray<int32> SectionIdsUseByActiveMorphTargets;
int32 NumWeightedActiveMorphTargets;
# Cloth数据
TMap<int32, FClothSimulData> ClothingSimData;
FMatrix ClothObjectLocalToWorld;
float ClothBlendWeight;
1. InitDynamicSkelMeshObjectDataGPUSkin
用RenderData和SkeletalMeshComp初始化DynamicData
调用UpdateRefToLocalMatrices。
2. UpdateRefToLocalMatrices:计算蒙皮矩阵
const TArray<FBoneIndexType>* RequiredBoneSets[3] = { &LOD.ActiveBoneIndices, ExtraRequiredBoneIndices, NULL };
for (int32 RequiredBoneSetIndex = 0; RequiredBoneSets[RequiredBoneSetIndex] != NULL; RequiredBoneSetIndex )
const TArray<FBoneIndexType>& RequiredBoneIndices = *RequiredBoneSets[RequiredBoneSetIndex];
for (int32 BoneIndex = 0; BoneIndex < RequiredBoneIndices.Num(); BoneIndex )
//...
//更新骨骼全局姿势
ReferenceToLocal[ThisBoneIndex] = ComponentTransform[ThisBoneIndex].ToMatrixWithScale();
//...
//乘上绑定姿势逆矩阵,得到最终的蒙皮矩阵
for (int32 ThisBoneIndex = 0; ThisBoneIndex < ReferenceToLocal.Num(); ThisBoneIndex)
ReferenceToLocal[ThisBoneIndex] = (*RefBasesInvMatrix)[ThisBoneIndex] * ReferenceToLocal[ThisBoneIndex];
该函数遍历骨骼列表,从SkeletalMeshComp获取前期Animation计算完后的骨骼全局姿势数据,初始化ReferenceToLocal矩阵列表。
然后遍历ReferenceToLocal,为每个骨骼全局姿势再乘上绑定姿势逆矩阵,得到最终的蒙皮矩阵。
绑定姿势矩阵:每骨骼的全局绑定姿势矩阵,用于将顶点从某骨骼空间变换到模型空间。
绑定姿势逆矩阵:用于将顶点从模型空间变换到骨骼空间。
蒙皮矩阵的作用是负责将顶点从模型空间下的绑定姿势,变换到模型空间下的当前动画姿势。所以需要先将顶点变换到骨骼空间,再应用骨骼全局姿势矩阵。
3. USkeletalMesh::CalculateInvRefMatrices
预计算绑定姿势逆矩阵,由USkeletalMesh的PostLoad在资源初始化时调用一次。存在USkeletalMesh的RefBasesInvMatrix中。
由于绑定姿势是常量,所以只需计算一次即可。
该函数负责逐骨骼计算:
- 获取每根骨骼T-Pose姿态下的矩阵,暂存在CachedComposedRefPoseMatrices
- 对每根骨骼,找到其父骨骼的T-Pose矩阵,应用在子骨骼上,暂存在CachedComposedRefPoseMatrices
- 对每根骨骼,取CachedComposedRefPoseMatrices矩阵数据的逆矩阵,存到RefBasesInvMatrix
2.2 处理渲染数据
1. 渲染线程入口
FSkeletalMeshObjectGPUSkin::UpdateDynamicData_RenderThread
- 获取游戏线程传递的DynamicData
- 调ProcessUpdatedDynamicData处理数据
2. FSkeletalMeshObjectGPUSkin::ProcessUpdatedDynamicData
- 用DynamicData.LODIndex索引MeshObjectLOD和存在SkeletalMeshComp中的Render数据(LODRenderData和RenderSection列表)
- 从MeshObjectLOD获取顶点数据:FVertexFactoryData
- 遍历RenderSection,用SectionIdx索引,顶点数据中获取对应的顶点工厂对象(FGPUBaseSkinVertexFactory),调用其UpdateBoneData接口将骨骼变换矩阵数据存入到 GBoneUniformStruct,再写入UniformBuffer,后续在 shader 中使用。
for (int32 SectionIdx = 0; SectionIdx < Sections.Num(); SectionIdx )
{
const FSkelMeshRenderSection& Section = Sections[SectionIdx];
...
FGPUBaseSkinVertexFactory* VertexFactory;
{
VertexFactory = VertexFactoryData.VertexFactories[SectionIdx].Get();
}
...
TArray<FMatrix>& ReferenceToLocalMatrices = DynamicData->ReferenceToLocal;
bool bNeedFence = ShaderData.UpdateBoneData(RHICmdList, ReferenceToLocalMatrices, Section.BoneMap, RevisionNumber, false, FeatureLevel, bUseSkinCache);
3. 顶点工厂
FGPUBaseSkinVertexFactory
FGPUBaseSkinVertexFactory::FShaderDataType
4. Shader
FGPUBaseSkinVertexFactory定义的宏:
IMPLEMENT_GPUSKINNING_VERTEX_FACTORY_TYPE:将GPU蒙皮的VertexFactory绑定到对应的Shader(GpuSkinVertexFactory.ush)
#define IMPLEMENT_GPUSKINNING_VERTEX_FACTORY_TYPE(FactoryClass, ShaderFilename,bUsedWithMaterials,bSupportsStaticLighting,bSupportsDynamicLighting,bPrecisePrevWorldPos,bSupportsPositionOnly)
IMPLEMENT_GPUSKINNING_VERTEX_FACTORY_TYPE(TGPUSkinVertexFactory, "/Engine/Private/GpuSkinVertexFactory.ush", true, false, true, false, false);
2.3 GpuSkinVertexFactory.ush
负责GPU蒙皮的顶点shader
ush是UE4中Shader的头文件,usf是Shader的源文件
1.FVertexFactoryInput:
C (CPU)的输入数据,封装顶点坐标(Position)、切线(Tangent)、影响蒙皮的骨骼索引和权重(BlendIndices、blendWeights),以及其他渲染需要的数据,如纹理。
2.GetVertexFactoryIntermediates
返回值FVertexFactoryIntermediates
Intermediates.UnpackedPosition = UnpackedPosition(Input);
Intermediates.BlendMatrix = CalcBoneMatrix( Input );
Intermediates.TangentToLocal = SkinTangents(Input, Intermediates);
Intermediates.Color = Input.Color FCOLOR_COMPONENT_SWIZZLE;
该函数根据参数顶点蒙皮的若干骨骼,及其骨骼变换矩阵,计算蒙皮矩阵。以及计算切线矩阵。
蒙皮矩阵的计算在CalcBoneMatrix
FBoneMatrix BoneMatrix = Input.BlendWeights.x * GetBoneMatrix(Input.BlendIndices.x);
BoneMatrix = Input.BlendWeights.y * GetBoneMatrix(Input.BlendIndices.y);
#if !GPUSKIN_LIMIT_2BONE_INFLUENCES
BoneMatrix = Input.BlendWeights.z * GetBoneMatrix(Input.BlendIndices.z);
BoneMatrix = Input.BlendWeights.w * GetBoneMatrix(Input.BlendIndices.w);
#if GPUSKIN_USE_EXTRA_INFLUENCES
BoneMatrix = Input.BlendWeightsExtra.x * GetBoneMatrix(Input.BlendIndicesExtra.x);
BoneMatrix = Input.BlendWeightsExtra.y * GetBoneMatrix(Input.BlendIndicesExtra.y);
BoneMatrix = Input.BlendWeightsExtra.z * GetBoneMatrix(Input.BlendIndicesExtra.z);
BoneMatrix = Input.BlendWeightsExtra.w * GetBoneMatrix(Input.BlendIndicesExtra.w);
支持4骨骼或8骨骼蒙皮
线性蒙皮,将所有对该顶点有影响的骨骼变换矩阵,乘上权重再累加起来,得到一个完整的蒙皮矩阵。
2.4 FPrimitiveSceneProxy
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhibhcjj
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
微信运动停用后别人还能看到步数吗
PHP中文网 07-22 -
excel打印预览压线压字怎么办
PHP中文网 06-22