2010-11-23 37 views
7

Estoy tratando de agregar animación a mi programa.Animación de esqueleto OpenGL

Tengo un modelo humano creado en Blender con animación esquelética, y puedo omitir los fotogramas clave para ver el modelo caminando.

Ahora exporté el modelo a un formato XML (Ogre3D) y en este archivo XML puedo ver la rotación, la traducción y la escala asignadas a cada hueso en un momento específico (t = 0.00000, t = 0.00040, ... etc.)

Lo que he hecho es encontrar qué vértices se asignan a cada hueso. Ahora supongo que todo lo que necesito hacer es aplicar las transformaciones definidas para el hueso a cada uno de estos vértices. ¿Es este el enfoque correcto?

En mi función draw OpenGL() (en bruto pseudo-código):

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); 
} 

Respuesta

5

vértices son generalmente afectados por más de un hueso - Parece que usted está después de desollar mezcla lineal. Mi código de C++ en desgracia, pero es de esperar que te dará la idea:

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(); 
} 

Nota de paso, que las implementaciones del mundo real suelen hacer este tipo de cosas en la GPU a lo mejor de mi conocimiento.

1

Su código asume que cada hueso tiene una matriz de transformación independiente (restablece su matriz al comienzo de cada iteración de bucle). Pero en realidad, los huesos forman una estructura jerárquica que debes preservar cuando haces tu renderización. Tenga en cuenta que cuando gira la parte superior del brazo, el antebrazo gira, porque está unido. El antebrazo puede tener su propia rotación, pero se aplica después de rotarlo con la parte superior del brazo.

La representación del esqueleto se realiza de forma recursiva. Aquí hay un pseudocódigo:

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

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

Espero que esto ayude.

Cuestiones relacionadas