2016-09-22 4 views
6

최근 뼈 애니메이션 가져 오기 작업을하고 있습니다. 그래서 Iimp 기술로 3D Minecraft와 같은 모델을 만들어 Assimp 애니메이션 가져 오기를 테스트했습니다. 출력 형식은 COLLADA (*. dae)이고 내가 사용한 도구는 블렌더입니다. 프로그래밍 측면에서, 내 환경은 opengl/glm/assimp입니다. 내 문제에 대한 이러한 정보로 충분하다고 생각합니다. 하나는 모델의 애니메이션으로, 나는 assimp 애니메이션을 테스트하기 위해 7 unmove 키 프레임을 기록합니다.Assimp 애니메이션 뼈대 변환

먼저 로컬 변환 부분을 제외하고는 내 변환이 정확하므로 함수가 glm::mat4(1.0f) 만 반환하도록하고 결과는 바인드 포즈 (확실하지 않음) 모델을 표시합니다. (아래 그림 참조)

두 번째로 glm::mat4(1.0f) 값을 bone->localTransform = transform * scaling * glm::mat4(1.0f);으로 되돌리고 모델이 변형됩니다. 믹서기에

테스트 이미지와 모델 (이미지 아래 참조) test and origin (bone->localTransform = glm::mat4(1.0f) * scaling * rotate; :이 이미지는 땅 :(아래) :

void MeshModel::UpdateAnimations(float time, std::vector<Bone*>& bones) 
{ 
    for each (Bone* bone in bones) 
    { 
     glm::mat4 rotate = GetInterpolateRotation(time, bone->rotationKeys); 
     glm::mat4 transform = GetInterpolateTransform(time, bone->transformKeys); 
     glm::mat4 scaling = GetInterpolateScaling(time, bone->scalingKeys); 
     //bone->localTransform = transform * scaling * glm::mat4(1.0f); 
     //bone->localTransform = glm::mat4(1.0f) * scaling * rotate; 
     //bone->localTransform = glm::translate(glm::mat4(1.0f), glm::vec3(0.5f)); 
     bone->localTransform = glm::mat4(1.0f); 
    } 
} 

void MeshModel::UpdateBone(Bone * bone) 
{ 
    glm::mat4 parentTransform = bone->getParentTransform(); 
    bone->nodeTransform = parentTransform 
     * bone->transform // assimp_node->mTransformation 
     * bone->localTransform; // T S R matrix 

    bone->finalTransform = globalInverse 
     * bone->nodeTransform 
     * bone->inverseBindPoseMatrix; // ai_mesh->mBones[i]->mOffsetMatrix 

    for (int i = 0; i < (int)bone->children.size(); i++) { 
     UpdateBone(bone->children[i]); 
    } 
} 

glm::mat4 Bone::getParentTransform() 
{ 
    if (this->parent != nullptr) 
     return parent->nodeTransform; 
    else 
     return glm::mat4(1.0f); 
} 

glm::mat4 MeshModel::GetInterpolateRotation(float time, std::vector<BoneKey>& keys) 
{ 
    // we need at least two values to interpolate... 
    if ((int)keys.size() == 0) { 
     return glm::mat4(1.0f); 
    } 
    if ((int)keys.size() == 1) { 
     return glm::mat4_cast(keys[0].rotation); 
    } 

    int rotationIndex = FindBestTimeIndex(time, keys); 
    int nextRotationIndex = (rotationIndex + 1); 
    assert(nextRotationIndex < (int)keys.size()); 
    float DeltaTime = (float)(keys[nextRotationIndex].time - keys[rotationIndex].time); 
    float Factor = (time - (float)keys[rotationIndex].time)/DeltaTime; 
    if (Factor < 0.0f) 
     Factor = 0.0f; 
    if (Factor > 1.0f) 
     Factor = 1.0f; 
    assert(Factor >= 0.0f && Factor <= 1.0f); 
    const glm::quat& startRotationQ = keys[rotationIndex].rotation; 
    const glm::quat& endRotationQ = keys[nextRotationIndex].rotation; 
    glm::quat interpolateQ = glm::lerp(endRotationQ, startRotationQ, Factor); 
    interpolateQ = glm::normalize(interpolateQ); 
    return glm::mat4_cast(interpolateQ); 
} 

glm::mat4 MeshModel::GetInterpolateTransform(float time, std::vector<BoneKey>& keys) 
{ 
    // we need at least two values to interpolate... 
    if ((int)keys.size() == 0) { 
     return glm::mat4(1.0f); 
    } 
    if ((int)keys.size() == 1) { 
     return glm::translate(glm::mat4(1.0f), keys[0].vector); 
    } 

    int translateIndex = FindBestTimeIndex(time, keys); 
    int nextTranslateIndex = (translateIndex + 1); 
    assert(nextTranslateIndex < (int)keys.size()); 
    float DeltaTime = (float)(keys[nextTranslateIndex].time - keys[translateIndex].time); 
    float Factor = (time - (float)keys[translateIndex].time)/DeltaTime; 
    if (Factor < 0.0f) 
     Factor = 0.0f; 
    if (Factor > 1.0f) 
     Factor = 1.0f; 
    assert(Factor >= 0.0f && Factor <= 1.0f); 
    const glm::vec3& startTranslate = keys[translateIndex].vector; 
    const glm::vec3& endTrabslate = keys[nextTranslateIndex].vector; 
    glm::vec3 delta = endTrabslate - startTranslate; 
    glm::vec3 resultVec = startTranslate + delta * Factor; 
    return glm::translate(glm::mat4(1.0f), resultVec); 
} 

코드 아이디어가 참조 여기에 코드가

Matrix calculations for gpu skinningSkeletal Animation With Assimp

전반적으로 assimp에서 MeshModel까지의 모든 정보를 뼈대 구조에 저장하고, 정보가 괜찮은 것 같아요?

마지막 것은, 내 버텍스 쉐이더 코드 :

#version 330 core 
#define MAX_BONES_PER_VERTEX 4 

in vec3 position; 
in vec2 texCoord; 
in vec3 normal; 
in ivec4 boneID; 
in vec4 boneWeight; 

const int MAX_BONES = 100; 

uniform mat4 model; 
uniform mat4 view; 
uniform mat4 projection; 
uniform mat4 boneTransform[MAX_BONES]; 

out vec3 FragPos; 
out vec3 Normal; 
out vec2 TexCoords; 
out float Visibility; 

const float density = 0.007f; 
const float gradient = 1.5f; 

void main() 
{ 
    mat4 boneTransformation = boneTransform[boneID[0]] * boneWeight[0]; 
    boneTransformation += boneTransform[boneID[1]] * boneWeight[1]; 
    boneTransformation += boneTransform[boneID[2]] * boneWeight[2]; 
    boneTransformation += boneTransform[boneID[3]] * boneWeight[3]; 


    vec3 usingPosition = (boneTransformation * vec4(position, 1.0)).xyz; 
    vec3 usingNormal = (boneTransformation * vec4(normal, 1.0)).xyz; 

    vec4 viewPos = view * model * vec4(usingPosition, 1.0); 
    gl_Position = projection * viewPos; 
    FragPos = vec3(model * vec4(usingPosition, 1.0f)); 
    Normal = mat3(transpose(inverse(model))) * usingNormal; 
    TexCoords = texCoord; 
    float distance = length(viewPos.xyz); 
    Visibility = exp(-pow(distance * density, gradient)); 
    Visibility = clamp(Visibility, 0.0f, 1.0f); 
} 

내 질문에 위의 코드의 부족 또는 막연하게 설명 알려 주시기 바랍니다, 감사합니다!

편집 : (1) 추가에

,이 같은 내 뼈 정보 (코드 가져 오는 부분) :

for (int i = 0; i < (int)nodeAnim->mNumPositionKeys; i++) 
{ 
    BoneKey key; 
    key.time = nodeAnim->mPositionKeys[i].mTime; 
    aiVector3D vec = nodeAnim->mPositionKeys[i].mValue; 
    key.vector = glm::vec3(vec.x, vec.y, vec.z); 
    currentBone->transformKeys.push_back(key); 
} 

일부 형질 전환 벡터를했다, 그래서 glm::mat4 transform = GetInterpolateTransform(time, bone->transformKeys); 위의 내 코드, Absloutely는 얻을 같은 값입니다. 변형 값을 제공하는 nomove 키 프레임 애니메이션을 만들었는지 확실하지 않습니다 (당연히 7 키 프레임이 있습니다).

다음과 같은 키 프레임 내용 (머리 뼈의 디버그) : keyframe from head 다른 키 프레임, 동일한 벡터 값.

편집 : (2)

내 DAE 파일을 테스트하려면, 나는 :) 와서 가져 jsfiddle에 넣어. Unity에서 내 파일이 올바르게 작동하는 또 다른 문제는 내 로컬 변환이 문제를 발생시키지 않는다는 것입니다. 문제는 parentTransform 또는 bone-> transform ...과 같은 다른 문제 일 수 있습니다. 나는 또한 모든 뼈와 함께 로컬 변환 행렬을 추가하지만, 왜 COLLADA에 내 unmove 애니메이션에 대해 이러한 값이 포함되어 있는지 파악할 수 없습니다 ...

답변

4

테스트 금액에 대해서는 결국 UpdateBone() 부분이 발견되었습니다.

필자의 문제점을 지적하기 전에 일련의 행렬 곱셈을 사용하면 혼란 스럽지만, 해결책을 찾았을 때 모든 행렬을 완전히 실현했을뿐입니다 (아마도 90 %).

이 문제는 문서 Matrix calculations for gpu skinning에서 발생합니다. 난 대답 코드가 절대적으로 옳다고 생각하고 더 이상 행렬을 사용해야한다고 생각하지 않는다. 따라서, 오용 행렬은 내 모습을 로컬 변환 행렬에 끔찍하게 전달합니다. 다시 내 질문 섹션의 이미지는 로컬 변환 행렬을 glm::mat4(1.0f)으로 바꿀 때 바인드 포즈입니다.

그래서 변경된 이유는 바인드 포즈를 만드는 것입니다. 나는 문제가 뼈 공간에서 국부 변형이되어야한다고 생각했지만 틀렸어. "

void MeshModel::UpdateBone(Bone * bone) 
{ 
    glm::mat4 parentTransform = bone->getParentTransform(); 
    if (boneName == "Scene" || boneName == "Armature") 
    { 
     bone->nodeTransform = parentTransform 
      * bone->transform // when isn't bone node, using assimp_node->mTransformation 
      * bone->localTransform; //this is your T * R matrix 
    } 
    else 
    { 
     bone->nodeTransform = parentTransform // This retrieve the transformation one level above in the tree 
      * bone->localTransform; //this is your T * R matrix 
    } 

    bone->finalTransform = globalInverse // scene->mRootNode->mTransformation 
     * bone->nodeTransform //defined above 
     * bone->inverseBindPoseMatrix; // ai_mesh->mBones[i]->mOffsetMatrix 

    for (int i = 0; i < (int)bone->children.size(); i++) { 
     UpdateBone(bone->children[i]); 
    } 
} 

나는 assimp_node->mTransformation 전에 내게 줄 모르는, 오직 설명 : 나는이 대답을하기 전에, 아래의 코드를 보면 :

void MeshModel::UpdateBone(Bone * bone) 
{ 
    glm::mat4 parentTransform = bone->getParentTransform(); 
    bone->nodeTransform = parentTransform 
     * bone->transform // assimp_node->mTransformation 
     * bone->localTransform; // T S R matrix 

    bone->finalTransform = globalInverse 
     * bone->nodeTransform 
     * bone->inverseBindPoseMatrix; // ai_mesh->mBones[i]->mOffsetMatrix 

    for (int i = 0; i < (int)bone->children.size(); i++) { 
     UpdateBone(bone->children[i]); 
    } 
} 

그리고 아래와 같이 변경 assimp 문서에서 노드 부모와 관련된 변환 "을 참조하십시오. 일부 테스트의 경우, mTransformation은 본 노드를 뼈 노드에 사용하면 현재 노드가 부모 노드와 관련된 바인드 포즈 행렬이라는 것을 알았습니다. 머리 뼈에 모체를 포착 한 사진을주세요.

Prove

왼쪽 부분 assimp_node->mTransformation 국지적 오른쪽에서 페치되는 transformunmove 애니메이션의 nodeAnim->mPositionKeys, nodeAnim->mRotationKeysnodeAnim->mScalingKeys으로부터 열쇠에 의해 계산된다 localTransform.

내가 한 일을 다시 봐, 내 질문 섹션에있는 이미지

마지막에

, 제가 전에 무슨 짓을했는지 보여 드리겠습니다 :) 다만 별도 아니라 스파게티를 볼 수 있도록, 두 번 어트 변환 포즈 결합했다 애니메이션 이동 테스트 및 올바른 애니메이션 결과.

Result

(내 개념이 잘못되면 모두 들어! 들으 날을 지적 해주십시오.)