C++ 骨架动画

C++ 骨架动画,c++,skinning,skeletal-mesh,C++,Skinning,Skeletal Mesh,我不熟悉骨骼动画的术语,我读到过,要使用骨骼制作网格动画,我必须使用骨骼层次,我在场景图中制作了每个骨骼节点,然后要变形网格,我必须在插值之前获得骨骼的逆绝对矩阵(我相信这就是他们所说的姿势)乘以该值,对于骨骼的插值绝对矩阵,以获得过渡矩阵和变形网格,我必须将顶点乘以过渡矩阵乘以权重,并将所有这些结果与变形相同顶点的任何其他骨骼相加 为了更具体地获得每根骨骼的绝对值,我这样做 void CNode::update() { if(this->Parent!=null) ab

我不熟悉骨骼动画的术语,我读到过,要使用骨骼制作网格动画,我必须使用骨骼层次,我在场景图中制作了每个骨骼节点,然后要变形网格,我必须在插值之前获得骨骼的逆绝对矩阵(我相信这就是他们所说的姿势)乘以该值,对于骨骼的插值绝对矩阵,以获得过渡矩阵和变形网格,我必须将顶点乘以过渡矩阵乘以权重,并将所有这些结果与变形相同顶点的任何其他骨骼相加

为了更具体地获得每根骨骼的绝对值,我这样做

void CNode::update()
{
   if(this->Parent!=null)
      absMatrix = this->Parent->absMatrix * relativeMatrix;
   else
     absMatrix = RelativeMatrix;

   for(int n=0; n<childs.count; n++)
   {
      childs[n]->update();
   }
}
void CNode::update()
{
如果(此->父项!=null)
absMatrix=此->父->absMatrix*相对矩阵;
其他的
absMatrix=相对矩阵;
for(int n=0;nupdate();
}
}
现在,我得到了这个Abs矩阵的逆矩阵,在任何只改变相对矩阵一次的插值之前 所以顶点变形就是这个函数

void CMesh::skinMesh(){
     CVec3 vec;
     for(int n=0; n < vertex.count; n++)
     {        
        vec.clear();
        for(int i=0; i < vertex[n].weight.count && vertex[n].bindBone[i] ; i++)
        {
            vec+= vertex[n].vec * vertex[n].bindBone[i]->transitionMatrix * vertex[n].weight[i];
        }
        outVertex[n] = vec;
}
void CMesh::skinMesh(){
CVec3-vec;
对于(int n=0;n传递矩阵*顶点[n]。权重[i];
}
外顶点[n]=vec;
}
现在这对我来说不起作用了,因为每个顶点都围绕网格轴的中心旋转,而不是骨骼的父骨骼,即使顶点变形的骨骼,考虑到这一点,我认为这是合乎逻辑的 transition=InverAbs*AbsoluteMatrix将获得骨骼因插值而获得的旋转量,因此假设它旋转20度,顶点将从顶点原点旋转20度,因此我想我缺少一些使顶点围绕正在变形的骨骼的父对象旋转的内容,请帮助我

这就是我做插值的方法,不是绝对矩阵,而是相对矩阵,我更新的代码绝对值就是上面的代码

//CAnimateObject is a decendent class of CNode
void CAnimateObject::UpdateFrame(unsigned AniNum, float Time)
    {
        float fInterp;

        if(m_AniCount > AniNum)
        {
            CKeyFrame *CurAni = &Ani[ AniNum ];
            if(CurAni->Pos.list.count>0)
            {
                CFrame<CVector3>::CKEY *begin, *end;

                if( CurAni->Pos.getBetweenKeys(&begin, &end, Time, fInterp)){
                    m_Pos.x = begin->Object->x + (end->Object->x - begin->Object->x) * fInterp;
                    m_Pos.y = begin->Object->y + (end->Object->y - begin->Object->y) * fInterp;
                    m_Pos.z = begin->Object->x + (end->Object->z - begin->Object->z) * fInterp;
                }
            }
            if(CurAni->Scale.list.count>0)
            {
                CFrame<CVector3>::CKEY *begin, *end;
                if( CurAni->Scale.getBetweenKeys(&begin, &end, Time, fInterp)){
                    m_Scale.x = begin->Object->x + (end->Object->x - begin->Object->x) * fInterp;
                    m_Scale.y = begin->Object->y + (end->Object->y - begin->Object->y) * fInterp;
                    m_Scale.z = begin->Object->x + (end->Object->z - begin->Object->z) * fInterp;
                }
            }
            if(CurAni->Rot.list.count > 1)
            {
                CFrame<CQuaternion>::CKEY *begin, *end;

                if( CurAni->Rot.getBetweenKeys(&begin, &end, Time, fInterp)){
                    m_Qrel.SLERP(*begin->Object, *end->Object, fInterp);
                }
            }else
                if(CurAni->Rot.list.count==1)
                    m_Qrel = *(CQuaternion*)CurAni->Rot.list.start[0].Object;
        }
                CMatrix4 tm, scale;
    scale.identity();
    tm.identity();
    scale.Scale(m_Scale.Get());
    tm.fromQuaternion(m_Qrel);
    m_Rel = tm * scale;
    m_Rel.Translate(m_Pos);

    }
//CAnimateObject是CNode的一个Decent类
void CAnimateObject::UpdateName(无符号单位,浮点时间)
{
浮动fInterp;
if(m_AniCount>AniNum)
{
克耶夫拉姆*库拉尼=&Ani[AniNum];
如果(库拉尼->位置列表计数>0)
{
CFrame::CKEY*开始,*结束;
if(CurAni->Pos.getBetweenKeys(&begin,&end,Time,fInterp)){
m_Pos.x=begin->Object->x+(end->Object->x-begin->Object->x)*fInterp;
m_Pos.y=begin->Object->y+(end->Object->y-begin->Object->y)*fInterp;
m_Pos.z=begin->Object->x+(end->Object->z-begin->Object->z)*fInterp;
}
}
如果(CurAni->Scale.list.count>0)
{
CFrame::CKEY*开始,*结束;
if(CurAni->Scale.getBetweenKeys(&begin,&end,Time,fInterp)){
m_Scale.x=begin->Object->x+(end->Object->x-begin->Object->x)*fInterp;
m_Scale.y=begin->Object->y+(end->Object->y-begin->Object->y)*fInterp;
m_Scale.z=begin->Object->x+(end->Object->z-begin->Object->z)*fInterp;
}
}
如果(CurAni->Rot.list.count>1)
{
CFrame::CKEY*开始,*结束;
if(CurAni->Rot.getBetweenKeys(&begin,&end,Time,fInterp)){
m_Qrel.SLERP(*开始->对象,*结束->对象,fInterp);
}
}否则
如果(CurAni->Rot.list.count==1)
m_Qrel=*(CQuaternion*)CurAni->Rot.list.start[0]。对象;
}
CMATRIX4TM,标度;
scale.identity();
tm.identity();
scale.scale(m_scale.Get());
tm.来自四元数(m_Qrel);
m_Rel=tm*刻度;
m_Rel.Translate(m_Pos);
}
是的,我做了这项工作,将骨骼乘以它的绝对倒数,然后乘以绝对矩阵,它工作得很好,但是很多乘法都在发生,比如

//inversePose is the absoluteMatrix before applying the interpolation of the relative matrix
void CMesh::skinMesh(){
     CVec3 vec;
     for(int n=0; n < vertex.count; n++)
     {            
        outVertex[n].clear();
        for(int i=0; i < vertex[n].weight.count && vertex[n].bindBone[i] ; i++)
        {
            vec = vertex[n].vec3 * vertex[n].bindBone[i]->inversePose; 
            vec = vec * vertex[n].bindBone[i]->absMatrix * vertex[n].weight[i];
            outVertex[n]+= vec;
        }

}
//inversePose是应用相对矩阵插值之前的绝对矩阵
void CMesh::skinMesh(){
CVec3-vec;
对于(int n=0;ninversePose;
vec=vec*顶点[n].bindBone[i]>absMatrix*顶点[n].weight[i];
外顶点[n]+=vec;
}
}

现在代码正常了,看起来我的矩阵乘法顺序不对,因为它像inverPose*absoluteMatrx那样执行,现在是absoluteMatrx*inverPose,效果很好

你试过应用“骨骼的逆绝对矩阵”吗直接绘制顶点并绘制它们,以确保此变换按预期进行。此操作应将顶点带入“骨骼空间”,即“靠近骨骼”的顶点应以“接近原点”结束。确认此变换按预期进行可能有助于缩小问题范围。此外,您能否简要解释you计算“插值绝对矩阵”?如果我遵循,该矩阵应仅包含“中性”姿势和骨骼当前插值姿势之间的变换,在网格空间中指定;这是否正确?当我在插值之前将顶点乘以骨骼的绝对值,它会使顶点靠近原点,当我在插值之前将顶点乘以绝对值时,它会使顶点靠近原点呃插值,然后再加上变化,这样动画就可以正常工作了,但是那样的话,我必须对每个顶点进行乘法。我仍然有点困惑;你在你的介绍中说,你是通过乘以逆EPO来计算过渡矩阵的