2010-11-23 4 views
7

내 프로그램에 애니메이션을 추가하려고합니다.OpenGL 스켈레톤 애니메이션

나는 뼈대 애니메이션을 사용하여 블렌더에서 만든 인간 모델을 가지고 있으며, 키 프레임을 스킵하여 모델 보행을 볼 수 있습니다.

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

이 정보가 도움이되기를 바랍니다.

관련 문제