D3D中的骨骼蒙皮动画


骨骼蒙皮动画

      这是去年(2014-12-15 17:48)写的一篇笔记,鉴于此博客文章寥寥,故移植至此!

    之前在游戏中虽然也用骨骼动画,但是一切基于 GameBryo 引擎,动作的注册,回调和控制都比较简单,使用也比较方便,但是对于骨骼动画详细的原理不甚了解,项目的 bug 修改和上线准备都让我没有时间好好了解一番。好在最近得空,可以看下骨骼动画,尽管新项目不再是端游,但是万变不离其宗,好好了解一下 DX 的骨骼动画,对于使用 U3D 引擎也会有帮助,于是结合网上的资料,《精通 DirectX 3D 图形与动画程序设计》 《 DirectX 3D HLSL 高级实例讲解》以及微软 DirectX SDK 中的 Sample ,大致的步骤和原理总结如下

.x文件结构

1.模型结构: 包含顶点、纹理、面的索引,每个骨骼中各顶点受影响数

2.骨骼结构: 包含各骨骼层级关系和描述此继承关系的偏移量

3.动画数据: 包含多个动画的数据

 

template XSkinMeshHeader {

 <3cf169ce-ff7c-44ab-93c0-f78f62d172e2>

 WORD nMaxSkinWeightsPerVertex; //影响顶点的最大骨骼数(矩阵数)

 WORD nMaxSkinWeightsPerFace; //影响面的最大骨骼数(矩阵数)

 WORD nBones; //骨骼数量

}

 

template SkinWeights {

 <6f0d123b-bad2-4167-a0d0-80224f25fabb>

 STRING transformNodeName; //骨骼的字符串

 DWORD nWeights; //有多少顶点受此骨骼影响

 array DWORD vertexIndices[nWeights]; //受此骨骼影响的顶点

 array FLOAT weights[nWeights]; //受此骨骼影响的顶点的权值

 Matrix4x4 matrixOffset; //模型空间相对骨骼坐标空间的偏移

}

 

AnimationSet {

 Animation {

  AnimationKey {

   4; //关键帧的类型(0,1,2,3表示  旋转,缩放,位置,矩阵)

   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 }

 }

 

 Animation {

  

 

  AnimationKey {

   4;

   2;

   0;16;1.278853,0.000000,-0.000000,0.000000,0.000000,0.000000,1.123165,0.000000,0.000000,-1.470235,0.000000,0.000000,0.135977,2.027985,133.967667,1.000000;;,

   4960;16;1.278853,0.000000,-0.000000,0.000000,0.000000,0.000000,1.123165,0.000000,0.000000,-1.470235,0.000000,0.000000,0.135977,2.027985,133.967667,1.000000;;;

  }

  { body }

 }

 

 

 

回到D3D

1.描述骨骼层级关系的数据结构,即与Frame结构对应

typedef struct _D3DXFRAME

{

    LPSTR                   Name; //描述此节点的名称

    D3DXMATRIX              TransformationMatrix;//对应.x文件FrameTransformMatrix 

 

    LPD3DXMESHCONTAINER     pMeshContainer; //此节点的网格模型结构

 

    struct _D3DXFRAME       *pFrameSibling; //兄弟节点

    struct _D3DXFRAME       *pFrameFirstChild; //父亲节点

} D3DXFRAME, *LPD3DXFRAME;

 

 

2.记录模型顶点信息结构,与.x文件中的Mesh,Material,Skin等信息对应

typedef struct _D3DXMESHCONTAINER

{

    LPSTR                   Name; //描述此节点的名称

 

    D3DXMESHDATA            MeshData; //节点模型的网格数据

 

    LPD3DXMATERIAL          pMaterials; //材质信息

    LPD3DXEFFECTINSTANCE    pEffects; //实例效果信息

    DWORD                   NumMaterials; //材质数

    DWORD                  *pAdjacency; //邻接缓冲区指针

 

    LPD3DXSKININFO          pSkinInfo; //骨骼皮肤信息

 

    struct _D3DXMESHCONTAINER *pNextMeshContainer;

} D3DXMESHCONTAINER, *LPD3DXMESHCONTAINER;

 

 

3.网格数据(上面的_D3DXMESHCONTAINER中有用到)

typedef struct _D3DXMESHDATA

{

    D3DXMESHDATATYPE Type; //Mesh的类型

 

    // current mesh data interface

    union

    {

        LPD3DXMESH              pMesh; //正常的Mesh

        LPD3DXPMESH             pPMesh; //渐进的Mesh  (可以实现LOD)

        LPD3DXPATCHMESH         pPatchMesh;  //面片式的Mesh  (这种Mesh暂时没有用过)

    };

} D3DXMESHDATA, *LPD3DXMESHDATA;

 

 

 

代码结构

class CAllocateHierarchy : public ID3DXAllocateHierarchy

{

public:

    STDMETHOD( CreateFrame )( THIS_ LPCSTR Name, LPD3DXFRAME *ppNewFrame );

    STDMETHOD( CreateMeshContainer )( THIS_

        LPCSTR Name,

        CONST D3DXMESHDATA *pMeshData,

        CONST D3DXMATERIAL *pMaterials,

        CONST D3DXEFFECTINSTANCE *pEffectInstances,

        DWORD NumMaterials,

        CONST DWORD *pAdjacency,

        LPD3DXSKININFO pSkinInfo,

        LPD3DXMESHCONTAINER *ppNewMeshContainer );

    STDMETHOD( DestroyFrame )( THIS_ LPD3DXFRAME pFrameToFree );

    STDMETHOD( DestroyMeshContainer )( THIS_ LPD3DXMESHCONTAINER pMeshContainerBase );

 

    CAllocateHierarchy()

    {

    }

};

 

D3DXLoadMeshHierarchyFromX  的内部的回调依赖于接口ID3DXAllocateHierarchy的几个函数实现,D3DXLoadMeshHierarchyFromX 其中一个参数就是CAllocateHierarchy 实例化的指针

D3DXLoadMeshHierarchyFromX() //此函数内部基本过程

{

CreateFrame() //创建骨骼的回调函数

CreateMeshContainer() //创建模型的回调函数

{

GenerateSkinnedMesh() //生成蒙皮信息

{

ConvertToIndexedBlendedMesh() //转换顶点格式,将模型转换成显卡可以渲染的模型

}

}

}

OnCreateDevice()

{

D3DXLoadMeshHierarchyFromX() //加载模型和骨骼的继承结构

SetupBoneMatrxPointers() //设置骨骼矩阵

{

SetupBoneMatrixPointersOnMesh()

}

D3DXFrameCalculateBoundingSphere() //计算骨骼的范围

}

 

Update()

{

UpdateFrameMatrices() //更新每个骨骼

}

 

OnFrameRender()

{

DrawFrame()

{

DrawMeshContainer() //渲染模型顶点

DrawFrame() //递归渲染

}

}

 

具体实现

首先,需要扩展D3DXFRAME结构和D3DXMESHCONTAINER

struct D3DXFRAME_DERIVED : public D3DXFRAME

{

    D3DXMATRIXA16 CombinedTransformationMatrix;

};

(子骨骼)CombinedTransformationMatrix = (子骨骼)TransformationMatrix * (父骨骼)CombinedTransformationMatrix 

 

 

struct D3DXMESHCONTAINER_DERIVED : public D3DXMESHCONTAINER

{

    LPDIRECT3DTEXTURE9* ppTextures;       //模型的纹理

    

//皮肤信息

    LPD3DXMESH pOrigMesh; //模型接口

    LPD3DXATTRIBUTERANGE pAttributeTable; //属性接口

    DWORD NumAttributeGroups; //属性个数

    DWORD NumInfl; //受影响的属性个数

    LPD3DXBUFFER pBoneCombinationBuf; //混合骨骼的缓冲区

    D3DXMATRIX** ppBoneMatrixPtrs;

    D3DXMATRIX* pBoneOffsetMatrices; //骨骼矩阵

    DWORD NumPaletteEntries;

    bool UseSoftwareVP; //使用软件渲染

    DWORD iAttributeSW;     // used to denote the split between SW and HW if necessary for non-indexed skinning

};

 

类的头文件及部分函数的实现

class CAnimation

{

private:

CAllocateHierarchy *m_pAlloc;

LPDIRECT3DDEVICE9 m_pDevice;

LPD3DXFRAME m_pFrameRoot;

 

public:

bool m_bPlayAnim;

LPD3DXANIMATIONCONTROLLER m_pAnimController;

 

private:

HRESULT LoadFromXFile(LPCSTR strFileName);

void UpdateFrameMatrices(LPD3DXFRAME pFrameBase,LPD3DXMATRIX pParentMatrix);

void DrawFrame(LPD3DXFRAME pFrame);

void DrawMeshContainer(LPD3DXMESHCONTAINER pMeshContainerBase,LPD3DXFRAME pFrameBase);

public:

HRESULT OnCreate(LPDIRECT3DDEVICE9 pDevice,WCHAR *strFileName);

HRESULT Render(D3DXMATRIXA16 matWorld,float fElapsed);

HRESULT OnDestroy();

 

CAnimation();

virtual ~CAnimation();

};

 

 

void CAnimation::UpdateFrameMatrices( LPD3DXFRAME pFrameBase,LPD3DXMATRIX pParentMatrix )

{

D3DXFRAME_DERIVED *pFrame = (D3DXFRAME_DERIVED*)pFrameBase;

if (pParentMatrix)

{

D3DXMatrixMultiply(&pFrame->CombinedTransfromationMatrix,&pFrame->TransformationMatrix,pParentMatrix);

}

else

pFrame->CombinedTransfromationMatrix = pFrame->TransformationMatrix;

 

if (pFrame->pFrameSibling)

{

UpdateFrameMatrices(pFrame->pFrameSibling,pParentMatrix);

}

if (pFrame->pFrameFirstChild)

{

UpdateFrameMatrices(pFrame->pFrameFirstChild,&pFrame->CombinedTransfromationMatrix);

}

}

 

void CAnimation::DrawFrame( LPD3DXFRAME pFrame )

{

LPD3DXMESHCONTAINER pMeshContainer = pFrame->pMeshContainer;

 

while (pMeshContainer)

{

DrawMeshContainer(pMeshContainer,pFrame);

pMeshContainer = pMeshContainer->pNextMeshContainer;

}

 

if (pFrame->pFrameSibling)

{

DrawFrame(pFrame->pFrameSibling);

}

if (pFrame->pFrameFirstChild)

{

DrawFrame(pFrame->pFrameFirstChild);

}

}

 

 

void CAnimation::DrawMeshContainer( LPD3DXMESHCONTAINER pMeshContainerBase,LPD3DXFRAME pFrameBase )

{

D3DXMESHCONTAINER_DERIVED *pMeshContainer = (D3DXMESHCONTAINER_DERIVED*)pMeshContainerBase;

D3DXFRAME_DERIVED *pFrame = (D3DXFRAME_DERIVED*)pFrameBase;

 

UINT iMaterial = 0;

UINT iAtrib = 0;

LPD3DXBONECOMBINATION pBoneComb;

UINT iMatrixIndex;

UINT iPaletteEntry;

D3DXMATRIXA16 matTemp;

if (pMeshContainer->pSkinInfo != NULL)

{

HRESULT hr;

if (pMeshContainer->UseSoftwareVP)

m_pDevice->SetSoftwareVertexProcessing(TRUE);

if (pMeshContainer->NumInfl)

m_pDevice->SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE,TRUE);

if (pMeshContainer->NumInfl == 1)

m_pDevice->SetRenderState(D3DRS_VERTEXBLEND,D3DVBF_0WEIGHTS);

else if (pMeshContainer->NumInfl == 2)

m_pDevice->SetRenderState(D3DRS_VERTEXBLEND,D3DVBF_1WEIGHTS);

else if (pMeshContainer->NumInfl == 3)

m_pDevice->SetRenderState(D3DRS_VERTEXBLEND,D3DVBF_1WEIGHTS);

else if (pMeshContainer->NumInfl == 4)

m_pDevice->SetRenderState(D3DRS_VERTEXBLEND,D3DVBF_1WEIGHTS);

pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>(pMeshContainer->pBoneCombinationBuf->GetBufferPointer());

for (iAtrib = 0; iAtrib < pMeshContainer->NumAttributeGroups; iAtrib++)

{

//设置混合矩阵

for (iPaletteEntry = 0; iPaletteEntry < pMeshContainer->NumPaletteEntries;iPaletteEntry++)

{

iMatrixIndex = pBoneComb[iAtrib].BoneId[iPaletteEntry];

if (iMatrixIndex != UINT_MAX)

{

D3DXMatrixMultiply(&matTemp,&pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],

pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex]);

m_pDevice->SetTransform(D3DTS_WORLDMATRIX(iPaletteEntry),&matTemp);

}

}

//设置材质和纹理

m_pDevice->SetMaterial(&pMeshContainer->pMaterials[pBoneComb[iAtrib].AttribId].MatD3D);

m_pDevice->SetTexture(0,pMeshContainer->ppTextures[pBoneComb[iAtrib].AttribId]);

pMeshContainer->MeshData.pMesh->DrawSubset(iAtrib);

}

//恢复顶点混合状态 m_pDevice->SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE,FALSE);

m_pDevice->SetRenderState(D3DRS_VERTEXBLEND,D3DVBF_DISABLE);

//恢复顶点处理模式

if (pMeshContainer->UseSoftwareVP)

m_pDevice->SetSoftwareVertexProcessing(FALSE);

}

else

{

m_pDevice->SetTransform(D3DTS_WORLD,&pFrame->CombinedTransfromationMatrix);

for (iMaterial = 0; iMaterial < pMeshContainer->NumMaterials;iMaterial++)

{

m_pDevice->SetMaterial(&pMeshContainer->pMaterials[iMaterial].MatD3D);

m_pDevice->SetTexture(0,pMeshContainer->ppTextures[iMaterial]);

pMeshContainer->MeshData.pMesh->DrawSubset(iMaterial);

}

}

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值