模拟实现ID3DXSkinInfo::UpdateSkinnedMesh

一.公式:

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, 
//  指mesh的顶点个数
                  DWORD dwStride 
//  指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已经是OffsetMatix与CombinedMatrix的连接矩阵了(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]才等价)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值