2010-11-23 10 views
7

私は自分のプログラムにアニメーションを追加しようとしています。OpenGLスケルトンアニメーション

私は、骨格アニメーションを使用してBlenderで作成された人間のモデルを持っており、キーフレームをスキップしてモデルウォーキングを見ることができます。

私はモデルをXML(Ogre3D)形式にエクスポートしました。このXMLファイルでは、特定の時間(t = 0.00000、t = 0.00040、 ...など)

どの骨組みにどの頂点が割り当てられているかがわかります。今私は、これらの頂点のそれぞれに骨のために定義された変形を適用するだけでよいと仮定しています。これは正しいアプローチですか?私のOpenGLの描画()関数で

(ラフ擬似コード):

for (Bone b : bones){ 
    gl.glLoadIdentity(); 

    List<Vertex> v= b.getVertices(); 
    rotation = b.getRotation(); 
    translation = b.getTranslation(); 
    scale = b.getScale(); 

    gl.glTranslatef(translation); 
    gl.glRotatef(rotation); 
    gl.glScalef(scale); 

    gl.glDrawElements(v); 
} 

答えて

5

頂点は、通常、複数の骨の影響を受けている - あなたは、リニアブレンドスキニング後にしているように聞こえます。私のコードのC++では残念ながら、うまくいけば、それはあなたのアイデアをあげる:実世界の実装は通常、私の知る限りGPU上でこの種のものを行うことを渡すことで

void Submesh::skin(const Skeleton_CPtr& skeleton) 
{ 
    /* 
    Linear Blend Skinning Algorithm: 

    P = (\sum_i w_i * M_i * M_{0,i}^{-1}) * P_0/(sum i w_i) 

    Each M_{0,i}^{-1} matrix gets P_0 (the rest vertex) into its corresponding bone's coordinate frame. 
    We construct matrices M_n * M_{0,n}^-1 for each n in advance to avoid repeating calculations. 
    I refer to these in the code as the 'skinning matrices'. 
    */ 

    BoneHierarchy_CPtr boneHierarchy = skeleton->bone_hierarchy(); 
    ConfiguredPose_CPtr pose = skeleton->get_pose(); 
    int boneCount = boneHierarchy->bone_count(); 

    // Construct the skinning matrices. 
    std::vector<RBTMatrix_CPtr> skinningMatrices(boneCount); 
    for(int i=0; i<boneCount; ++i) 
    { 
     skinningMatrices[i] = pose->bones(i)->absolute_matrix() * skeleton->to_bone_matrix(i); 
    } 

    // Build the vertex array. 
    RBTMatrix_Ptr m = RBTMatrix::zeros();  // used as an accumulator for \sum_i w_i * M_i * M_{0,i}^{-1} 

    int vertCount = static_cast<int>(m_vertices.size()); 
    for(int i=0, offset=0; i<vertCount; ++i, offset+=3) 
    { 
     const Vector3d& p0 = m_vertices[i].position(); 
     const std::vector<BoneWeight>& boneWeights = m_vertices[i].bone_weights(); 
     int boneWeightCount = static_cast<int>(boneWeights.size()); 

     Vector3d p; 
     if(boneWeightCount != 0) 
     { 
      double boneWeightSum = 0; 

      for(int j=0; j<boneWeightCount; ++j) 
      { 
       int boneIndex = boneWeights[j].bone_index(); 
       double boneWeight = boneWeights[j].weight(); 
       boneWeightSum += boneWeight; 
       m->add_scaled(skinningMatrices[boneIndex], boneWeight); 
      } 

      // Note: This is effectively p = m*p0 (if we think of p0 as (p0.x, p0.y, p0.z, 1)). 
      p = m->apply_to_point(p0); 
      p /= boneWeightSum; 

      // Reset the accumulator matrix ready for the next vertex. 
      m->reset_to_zeros(); 
     } 
     else 
     { 
      // If this vertex is unaffected by the armature (i.e. no bone weights have been assigned to it), 
      // use its rest position as its real position (it's the best we can do). 
      p = p0; 
     } 

     m_vertArray[offset] = p.x; 
     m_vertArray[offset+1] = p.y; 
     m_vertArray[offset+2] = p.z; 
    } 
} 

void Submesh::render() const 
{ 
    glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); 
    glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT); 

    glEnableClientState(GL_VERTEX_ARRAY); 
    glVertexPointer(3, GL_DOUBLE, 0, &m_vertArray[0]); 

    if(m_material->uses_texcoords()) 
    { 
     glEnableClientState(GL_TEXTURE_COORD_ARRAY); 
     glTexCoordPointer(2, GL_DOUBLE, 0, &m_texCoordArray[0]); 
    } 

    m_material->apply(); 

    glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(m_vertIndices.size()), GL_UNSIGNED_INT, &m_vertIndices[0]); 

    glPopAttrib(); 
    glPopClientAttrib(); 
} 

注意を。

1

あなたのコードでは、各ボーンに独立した変換行列があると仮定しています(各ループ反復の開始時に行列をリセットします)。しかし、実際には、骨はレンダリングの際に保持しなければならない階層構造を形成します。あなたの上腕が回転するとき、あなたの前腕はそれが付いているので、一緒に回転することを考慮してください。前腕はそれ自身の回転を有することができるが、それは上腕と共に回転された後に適用される。

スケルトンのレンダリングは再帰的に行われます。ここにいくつかの疑似コードがあります:

function renderBone(Bone b) { 
    setupTransformMatrix(b); 
    draw(b); 
    foreach c in b.getChildren() 
     renderBone(c); 
} 

main() { 
    gl.glLoadIdentity(); 
    renderBone(rootBone); 
} 

私はこれが役立つことを望みます。