• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

UE4源码阅读_骨骼模型和动画系统_Mesh

武飞扬头像
_lazycat
帮助1

写在前面

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

由游戏线程向渲染线程的队列添加渲染指令和数据
学新通
调用流程:

  1. UWorld::SendAllEndOfFrameUpdates
	for (UActorComponent* Component : ComponentsThatNeedEndOfFrameUpdate)
	for (UActorComponent* Component : ComponentsThatNeedEndOfFrameUpdate_OnGameThread)
		Comp->DoDeferredRenderUpdates_Concurrent
  1. ActorComponent::DoDeferredRenderUpdates_Concurrent
    两个标志位:bRenderTransformDirty、bRenderDynamicDataDirty
    当帧是否有更新(rotation、transform、scale改变),或者DynamicData的数据变化。
    数据有变动的话,则分别调用SendRenderTransform_Concurrent、SendRenderDynamicData_Concurrent渲染新数据

  2. USkinnedMeshComponent::SendRenderDynamicData_Concurrent
    调MeshObject->Update

  3. 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
系列文章
更多 icon
同类精品
更多 icon
继续加载