d3d 骨骼动画1.docx
- 文档编号:3525063
- 上传时间:2022-11-23
- 格式:DOCX
- 页数:50
- 大小:262.35KB
d3d 骨骼动画1.docx
《d3d 骨骼动画1.docx》由会员分享,可在线阅读,更多相关《d3d 骨骼动画1.docx(50页珍藏版)》请在冰豆网上搜索。
d3d骨骼动画1
第14讲骨骼动画
1、模型动画信息
骨骼动画模型中的骨骼是以树状层次结构组织起来的,整个骨骼结构中有一块根骨骼(Root),其他的骨骼都直接或间接连接到根骨骼上,从而形成角色模型的整个骨骼框架,如图所示。
不含动画的普通X文件,有一个Mesh单元,保存了各顶点信息、各三角面的索引信息、材质种类及定义等。
而动画X文件,则在这个单元中增加了“各骨骼蒙皮信息”、“骨骼层次及结构信息”、“各时刻骨骼矩阵信息”等。
(1)在X文件中,骨骼信息是有框架(Frame)组成的,由于Frame模板是开放模板类型,所以在其中可以包含其他的Frame。
框架中通过嵌套定义也就形成了父子框架,而并列定义也就形成了兄弟框架,从而形成一个层次结构。
同时每块骨骼都包含两个矩阵:
初始变换矩阵LocalTransformMatrix和组合变换矩阵CombinedTransformMatrix,其中初始变换矩阵用于表示骨骼的初始位置,而组合变换矩阵用于对骨骼进行各种变换。
在Directx中,用一个D3DXFRAME结构或者X文件中的Frametemplate来表示帧对象。
下面看一下Frametemplate和D3DXFRAME结构的定义:
templateFrame
{
<3D82AB46-62DA-11cf-AB39-0020AF71E433>
FrameTransformMatrixframeTransformMatrix;
//骨骼相对于父节点的坐标变换矩阵,就是一个matrix
Meshmesh;
//骨骼的Mesh
}
typedefstruct_D3DXFRAME
{
LPSTR Name; //骨骼名称
D3DXMATRIX TransformationMatrix; //相对与父节点的坐标变换矩阵
LPD3DXMESHCONTAINER pMeshContainer; //LPD3DXMESHCONTAINER对象, 用来加载MESH,
struct_D3DXFRAME *pFrameSibling; //兄弟节点指针
struct_D3DXFRAME *pFrameFirstChild; //子节点指针
}D3DXFRAME,*LPD3DXFRAME;
(2)为了使一个X文件产生动画,必须至少提供一个动画集,每个动画集都应具有一个对某个框架的引用。
在X文件中,动画集由AnimationSet模板定义,其中由多个开放性模板Animation模板组成。
AnimationSet模板及Animation模板的定义如下:
templateAnimationSet
{
<3D82AB50-62DA-11cf-AB39-0020AF71E433>
[Animation<3D82AB4F-62DA-11cf-AB39-0020AF71E433>]
}
templateAnimation
{
<3D82AB4F-62DA-11cf-AB39-0020AF71E433>
[...]
}
AnimationSet模板只存储Animation对象,而Animation通常是由AnimationKey模板进行填充。
这个模板用于存储动画关键帧码,这个模板的每一个实例都包含帧码的类型(位置,缩放,转动,矩阵)和帧码数组。
数组中的每一个元素包含帧码的值和一个DWORD型的值指出帧的持续时间。
AnimationKey模板的定义如下:
templateAnimationKey//骨骼的各个动画帧时刻的动作数据模板
{
<10DD46A8-775B-11CF-8F52-0040333594A3>
DWORDkeyType;
DWORDnKeys;
arrayTimedFloatKeyskeys[nKeys];
}
templateTimedFloatKeys
{
DWORDtime;
FloatKeystfkeys;
}
其中,AnimationKey模板中的keyType表示当前关键帧的类型。
若该值为0,则表示旋转键,表示将使用四个分量w、x、y、z存储模型的旋转值。
其值为1表示缩放键,它表示分别对应x、y、z轴的模型缩放值。
而对于平移键,其值将为2。
该值为4,则表示变换矩阵键,此时关键帧的变换数组将使用16个浮点数实现该模型的各种变换。
例如:
AnimationSetwalk{
Animation{
AnimationKey{
4;//变换矩阵键
2;//2个动作
//第一个动作
0;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,
0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000;;,
//第二个动作
4960;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,
0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000;;;
}
{Scene_Root}
}
…
}
(3)皮肤属性由XSkinMeshHeader模板定义,其中包含了影响皮肤网格顶点的最大变换数和骨骼数。
皮肤权重由SkinWeights模板定义,其中包含了一个具体的骨骼对于相应顶点的影响权重。
这两个模板的定义如下:
templateXSkinMeshHeader
{
<3CF169CE-FF7C-44ab-93C0-F78F62D172E2>
WORDnMaxSkinWeightsPerVertex;
//一个顶点可以受到骨骼影响的最大骨骼数,可用于计算共同作用时减少遍历次数
WORDnMaxSkinWeightsPerFace;
//一个三角面可以受到骨骼影响的最大骨骼数。
这个数字对硬件顶点混合计算提出了基本要求。
WORDnBones;//当前Mesh的骨骼总数
}
由于每个骨骼的蒙皮信息都需要用SkinWeights结构去描述,所以有多少块骨骼,在Mesh中就有多少个SkinWeights对象。
注意,一般把SkinWeights视作Mesh的一部分。
这种Mesh又称SkinnedMesh(蒙皮网格),SkinWeights结构如下:
templateSkinWeights
{
<6F0D123B-BAD2-4167-A0D0-80224F25FABB>
STRINGtransformNodeName;//骨骼名
DWORDnWeights;//权重数组的元素个数,即该骨骼相关的顶点个数
arrayDWORDvertexIndices[nWeights];
//受该骨骼控制的顶点索引,实际上定义了该骨骼的蒙皮
arrayfloatweights[nWeights];//蒙皮各顶点的受本骨骼影响的权值
Matrix4x4matrixOffset;
//骨骼偏移矩阵,用来从初始Mesh坐标,反向计算顶点在子骨骼坐标系中的初始坐标。
}
在有的书中,把上面的matrixOffset叫骨骼权重矩阵,是不恰当的。
我们应该称为骨骼偏移矩阵比较合适。
2、加载骨骼动画数据
保存从X文件中读取的骨骼动画数据,Direct3D定义了D3DXFRAME和D3DXMESHCONTAINER两个结构。
其中,D3DXFRAME结构用于保存X文件中的骨骼信息(框架通常用来描述骨骼,所以也可以将框架看作骨骼),而D3DXMESHCONTAINER结构则用于保存网格蒙皮信息。
这两个结构体的定义。
typedefstruct_D3DXFRAME//保存框架骨骼信息
{
LPSTRName;//骨骼的名称
D3DXMATRIXTransformationMatrix;//本骨骼的转换矩阵
LPD3DXMESHCONTAINERpMeshContainer;//本骨骼所对应Mesh数据
struct_D3DXFRAME*pFrameSibling;//兄弟骨骼
struct_D3DXFRAME*pFrameFirstChild;//子骨骼
}D3DXFRAME,*LPD3DXFRAME;
typedefstruct_D3DXMESHCONTAINER//网格容器保存网格蒙皮信息
{
LPSTRName;//Mesh名称
D3DXMESHDATAMeshData;//Mesh数据
LPD3DXMATERIALpMaterials;//材质数组
LPD3DXEFFECTINSTANCEpEffects;//效果
DWORDNumMaterials;//材质数
DWORD*pAdjacency;//邻接三角形数组
LPD3DXSKININFOpSkinInfo;//蒙皮信息
struct_D3DXMESHCONTAINER*pNextMeshContainer;
}D3DXMESHCONTAINER,*LPD3DXMESHCONTAINER;
在实现动画网格模型的绘制前,不仅要得到每个框架的初始变换矩阵,同时还要得到从该框架的所有父节点到本级框架的组合变换矩阵。
这是因为任何一个父框架的位置改变都会影响该框架自身位置的变化,所以在此将结构D3DXFRAME扩展为D3DXFRAME_DERIVED。
例如:
structD3DXFRAME_DERIVED:
publicD3DXFRAME
{
D3DXMATRIXA16TransformationMatrix;
D3DXMATRIXA16CombinedTransformationMatrix;//组合转换矩阵
};
由于在D3DXMESHCONTAINER结构中并没有记录网格模型的纹理、蒙皮等信息,所以在应用程序中还需要将该结构扩展为D3DXMESHCONTAINER_DERIVED。
例如:
structD3DXMESHCONTAINER_DERIVED:
publicD3DXMESHCONTAINER
{
LPDIRECT3DTEXTURE9*ppTextures;//纹理数组
LPD3DXMESHpOrigMesh;//原始网格
LPD3DXATTRIBUTERANGEpAttributeTable;//网格的属性表
DWORDNumAttributeGroups;//属性组数量,即子网格数量
DWORDNumInfl;//每个顶点最多受多少骨骼的影响
LPD3DXBUFFERpBoneCombinationBuf;//骨骼结合表
D3DXMATRIX**ppBoneMatrixPtrs;//存放骨骼的组合变换矩阵
D3DXMATRIX*pBoneOffsetMatrices;//存放骨骼的初始变换矩阵
DWORDNumPaletteEntries;//骨骼数量上限
boolUseSoftwareVP;//标志是否使用软件顶点处理
};
其中,pBoneCombBuffer指向骨骼结合表,骨骼结合表中的数据按属性组结构体D3DXBONECOMBINATION组织起来。
该结构体定义如下:
typedefstructD3DXBONECOMBINATION{
DWORDAttribId;//属性ID
DWORDFaceStart;//起始的三角面
DWORDFaceCount;//三角面面数
DWORDVertexStart;//起始的顶点
DWORDVertexCount;//顶点数
DWORD*BoneId;//在每次绘制时所用到的骨骼矩阵
}D3DXBONECOMBINATION,*LPD3DXBONECOMBINATION;
Direct3D提供了多种骨骼数据的加载方式,然而标准的加载方式是通过调用D3DX库中D3DXLoadMeshHierarchyFromX函数读取Mesh层次信息。
该函数的声明如下:
HRESULTD3DXLoadMeshHierarchyFromX(//网格层次结构模型
LPCSTRFilename,//X文件名
DWORDMeshOptions,//加载选项,通常为D3DXMESH_MANAGED
LPDIRECT3DDEVICE9pDevice,
LPD3DXALLOCATEHIERARCHYpAlloc,//自定义数据容器
LPD3DXLOADUSERDATApUserDataLoader,//用户数据加载器,通常设为NULL
LPD3DXFRAME*ppFrameHierarchy,//框架层次结构的根框架
LPD3DXANIMATIONCONTROLLER*ppAnimController//动画控制器
);
(1)自定义数据容器类:
CAllocateHierarchy类自ID3DXAllocateHierarchy接口继承,该类分别重载了ID3DXAllocateHierarchy接口中四个方法,用于实现动画网格模型数据的加载和释放。
该类的定义如下:
//这里是Directx9.0a的
classCAllocateHierarchy:
publicID3DXAllocateHierarchy
{
public:
STDMETHOD(CreateFrame)(THIS_LPCTSTRName,LPD3DXFRAME*ppNewFrame);
#if((D3D_SDK_VERSION&0xFF)==31)//这里是Directx9.0b的
STDMETHOD(CreateMeshContainer)(THIS_
LPCTSTRName,
LPD3DXMESHDATApMeshData,
LPD3DXMATERIALpMaterials,
LPD3DXEFFECTINSTANCEpEffectInstances,
DWORDNumMaterials,
DWORD*pAdjacency,
LPD3DXSKININFOpSkinInfo,
LPD3DXMESHCONTAINER*ppNewMeshContainer);
#else//这里是Directx9.0c的
STDMETHOD(CreateMeshContainer)(THIS_
LPCSTRName,
CONSTD3DXMESHDATA*pMeshData,
CONSTD3DXMATERIAL*pMaterials,
CONSTD3DXEFFECTINSTANCE*pEffectInstances,
DWORDNumMaterials,
CONSTDWORD*pAdjacency,
LPD3DXSKININFOpSkinInfo,
LPD3DXMESHCONTAINER*ppNewMeshContainer);
#endif
STDMETHOD(DestroyFrame)(THIS_LPD3DXFRAMEpFrameToFree);
STDMETHOD(DestroyMeshContainer)(THIS_LPD3DXMESHCONTAINERpMeshContainerBase);
CAllocateHierarchy(CSkinMesh*pSkinMesh):
m_pSkinMesh(pSkinMesh){}
public:
CSkinMesh*m_pSkinMesh;
};
其中,CreateFrame函数仅仅用来创建一个D3DXFRAME对象,并按照指定的参数为该框架命名。
HRESULTCAllocateHierarchy:
:
CreateFrame(LPCTSTRName,LPD3DXFRAME*ppNewFrame)
{
HRESULThr=S_OK;
D3DXFRAME_DERIVED*pFrame;
*ppNewFrame=NULL;
pFrame=newD3DXFRAME_DERIVED;//为框架指定名称,创建框架结构对象
if(pFrame==NULL)
{
hr=E_OUTOFMEMORY;
gotoe_Exit;
}
hr=AllocateName(Name,&pFrame->Name);
if(FAILED(hr))
gotoe_Exit;
//初始化frame其他成员变量
D3DXMatrixIdentity(&pFrame->TransformationMatrix);
D3DXMatrixIdentity(&pFrame->CombinedTransformationMatrix);
pFrame->pMeshContainer=NULL;
pFrame->pFrameSibling=NULL;
pFrame->pFrameFirstChild=NULL;
*ppNewFrame=pFrame;
pFrame=NULL;
e_Exit:
deletepFrame;
returnhr;
}
AllocateName函数用于为一个LPSTR类型字符串分配内存,并用LPCSTR类型的字符串对它进行赋值。
HRESULTAllocateName(LPCTSTRName,LPTSTR*pNewName)
{
UINTcbLength;
if(Name!
=NULL)
{
cbLength=lstrlen(Name)+1;
*pNewName=newTCHAR[cbLength];
if(*pNewName==NULL)
returnE_OUTOFMEMORY;
memcpy(*pNewName,Name,cbLength*sizeof(TCHAR));
}
else
{
*pNewName=NULL;
}
returnS_OK;
}
DestroyFrame函数比较简单,它只有一个LPD3DXFRAME类型参数,并指向准备释放的框架对象。
该函数首先通过SAFE_DELETE_ARRAY宏实现框架对象的命名,然后通过SAFE_DELETE宏释放该对象。
该函数的实现代码如下
HRESULTCAllocateHierarchy:
:
DestroyFrame(LPD3DXFRAMEpFrameToFree)
{
SAFE_DELETE_ARRAY(pFrameToFree->Name);
SAFE_DELETE(pFrameToFree);
returnS_OK;
}
其中:
#defineSAFE_DELETE(p){if(p){delete(p);(p)=NULL;}}
#defineSAFE_DELETE_ARRAY(p){if(p){delete[](p);(p)=NULL;}}
#defineSAFE_RELEASE(p){if(p){(p)->Release();(p)=NULL;}}
从CreateMeshContainer函数的声明中可以看出,传入该函数的参数包括pMeshData、pMaterials、pEffectInstances、NumMaterials、pAjacency和pSkinInfo等。
因此,在该函数体内需要将这些数据保存到一个D3DXMESHCONTAINER对象中。
并且,其中所有数组所需的空间都需要在全局堆中进行分配。
例如:
D3DXMESHCONTAINER_DERIVED*pMeshContainer=NULL;
pMeshContainer=newD3DXMESHCONTAINER_DERIVED;
………………………
pMeshContainer->NumMaterials=max(1,NumMaterials);
pMeshContainer->pMaterials=newD3DXMATERIAL[pMeshContainer->NumMaterials];
pMeshContainer->ppTextures=newLPDIRECT3DTEXTURE9[pMeshContainer->NumMaterials];
MeshContainer->pAdjacency=newDWORD[NumFaces*3];
通过memcpy函数分别将这些对象复制到pMeshContainer对象中。
由于在绘制过程中并不需要pEffectInstanes,所以就不需要对它进行保存。
如果材质中未包含纹理贴图,那么将使用默认的材质属性。
判断pSkinInfo是否包含蒙皮信息,如果pSkinInfo不为NULL,那么就需要通过该信息中的蒙皮信息创建蒙皮网格对象。
首先,将原网格对象保存到pMeshContainer对象的pOrigMesh中,然后将保存在pSkinInfo中的各骨骼的偏移矩阵复制到pMeshContainer对象的pBoneOffsetMatrices中。
CSkinMesh类中的GenerateSkinnedMesh函数用来生成蒙皮网格数据,其中调用ID3DXSkinInfo接口的ConvertToBlendedMesh或ConvertToIndexedBlendedMesh方法生成已经过顶点混合或索引顶点混合后的蒙皮网格对象。
ConvertToBlendedMesh与ConvertToIndexedBlendMesh方法的区别在于,前者将采用无索引的顶点混合生成蒙皮网格对象,而后者则采用含有索引的顶点混合生成蒙皮网格对象。
HRESULTConvertToIBlendedMesh(
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- d3d 骨骼动画1 骨骼 动画