模拟实现ID3DXSkinInfo::UpdateSkinnedMesh

    技术2022-05-11  64

    模拟实现ID3DXSkinInfo::UpdateSkinnedMesh

     

    作者:cywater2000

    日期: 2006-1-5

     

    一.公式:

    FinalPos = MeshPos + ( Difference_i * Weight_i)

    = MeshPos + ( (NewMeshPos_i - MeshPos) * Weight_i )

    = MeshPos + ( (MeshPos × OffsetMatix_i × CombinedMatrix_i - MeshPos) * Weight_i ) [1]

     

    其中:

    MeshPos                   某顶点在mesh中的原始位置

    NewMeshPos_i       此顶点受某骨骼i影响后变换到的新位置

    Difference_i              两者之间的位移差值

    Weight_i:                   此顶点受某骨骼i的影响权重

    OffsetMatix_i            骨骼i的偏移矩阵

    CombinedMatrix_i:     骨骼i经过层次更新后的混合矩阵

    ∑:                                求和(对所有影响该顶点的骨骼)

    FinalPos                    此顶点的最终位置

     

    法线计算同理。

     

    二.模拟代码(没有优化):

    HRESULT UpdateSkinnedMesh(ID3DXSkinInfo *pSkinInfo,

                      const D3DXMATRIX *pBoneTransforms,

                      const D3DXMATRIX *pBoneInvTransposeTransforms, //not use(原函数也没用)

                      LPCVOID pVerticesSrc,

                      PVOID pVerticesDst,

    //注意下面增加的两个变量是原函数在调用时没有的,因为ID3DXSkinInfo的内部机制可以获得

                      DWORD numTotalVerts, //add 1,指mesh的顶点个数

                      DWORD dwStride //add 2,指mesh每个顶点的间距,即每个顶点结构的大小

                      )

    {

        DWORD *pVertsIndic = NULL;

        float *pVertsWeigh = NULL;

        DWORD dwNumVerts;

        DWORD offsetByte;

     

        BYTE *pDest = (BYTE*)pVerticesDst; //目标顶点缓冲

        const BYTE *pSrc = (BYTE*)pVerticesSrc; //源顶点缓冲

     

        memcpy(pDest, pSrc, numTotalVerts * dwStride);

     

        for(DWORD i = 0; i < pSkinInfo->GetNumBones(); i++)

        {

           dwNumVerts = pSkinInfo->GetNumBoneInfluences(i); //得到受影响的顶点个数

           if(dwNumVerts <= 0)

               continue;

     

           pVertsIndic = new DWORD[dwNumVerts];

           pVertsWeigh = new float[dwNumVerts];

           pSkinInfo->GetBoneInfluence(i, pVertsIndic, pVertsWeigh);

     

           while(dwNumVerts--)

           {

               DWORD index = pVertsIndic[dwNumVerts]; //当前受影响的顶点索引

               float weight = pVertsWeigh[dwNumVerts]; //当前受影响顶点的权重

               offsetByte = index * dwStride;

     

               D3DXVECTOR3 vecPos = *(D3DXVECTOR3 *)(pSrc + offsetByte); //位置

               D3DXVECTOR3 vecNor = *(D3DXVECTOR3 *)(pSrc + offsetByte + sizeof(D3DXVECTOR3)); //法线

     

               D3DXVECTOR3 vecPos2, vecNor2;

               D3DXVec3TransformCoord(&vecPos2, &vecPos, &pBoneTransforms[i]);

               D3DXVec3TransformNormal(&vecNor2, &vecNor, &pBoneTransforms[i]);

     

               D3DXVECTOR3 *pV = (D3DXVECTOR3 *)(pDest + offsetByte);

               D3DXVECTOR3 *pN = (D3DXVECTOR3 *)(pDest + offsetByte + sizeof(D3DXVECTOR3));

     

               D3DXVECTOR3 diff = (vecPos2 - vecPos) * weight;

               *pV += diff;

     

               diff = (vecNor2 - vecNor) * weight;

               *pN += diff;

           }

     

           delete[] pVertsIndic;

           delete[] pVertsWeigh;

        }

     

        return S_OK;

    }

        注意在调用UpdateSkinnedMesh前,pBoneTransforms已经是OffsetMatixCombinedMatrix的连接矩阵了(ID3DXSkinInfo::UpdateSkinnedMesh也是这么要求的)

    重要更新:

    骨骼动画属于Geometry Blending,因此标准做法应该是:  [2] 即FinalPos =∑(NewMeshPos_i  * Weight_i) + NewMeshPos_n * (1 - ∑Weight_i ) , i=0,1..n-1 =∑(MeshPos × OffsetMatix_i × CombinedMatrix_i * Weight_i) + (MeshPos × OffsetMatix_n × CombinedMatrix_n) * (1 - ∑Weight_i ) , i=0,1..n-1 [3]   (显然只有当影响顶点的 所有权重之和等于1时,公式[1]与[3]才等价)

    最新回复(0)