2016-09-22 199 views
6

最近我正在進行骨骼動畫導入,所以我用一些IK技術製作了一個3d類似Minecraft的模型來測試Assimp動畫導入。輸出格式是COLLADA(*。dae),我使用的工具是Blender。在編程方面,我的環境是opengl/glm/assimp。我認爲這些信息對於我的問題已經足夠了。一件事,模型的動畫,我只記錄了7個關鍵幀,用於測試assimp動畫。Assimp動畫骨骼轉換

首先,我想我的轉換除了局部轉換部分是正確的,所以讓函數只返回glm::mat4(1.0f),結果顯示綁定姿勢(不確定)模型。 (見下圖)

二,將數值glm::mat4(1.0f)調回bone->localTransform = transform * scaling * glm::mat4(1.0f);,然後模型變形。 (見下圖)

測試圖像和模型攪拌機: test and originbone->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 7個不同的關鍵幀,相同的矢量值。

編輯:(2)

如果你想測試我的DAE文件,我把它放在jsfiddle,來把它:)。另一件事,在Unity我的文件工作正常,所以我認爲也許不是我的本地變換髮生的問題,似乎問題可能是一些其他像parentTransform或骨 - >變換...等?我還要添加所有骨骼的局部變換矩陣,但無法弄清楚爲什麼COLLADA爲我的unmove動畫包含這些值...

回答

4

對於測試量,最後發現問題是UpdateBone()部分。在我指出我的問題之前,我需要說一系列矩陣乘法讓我感到困惑,但是當我找到解決方案時,它只是讓我完全(也許只有90%)意識到所有矩陣都在做。

問題來自文章Matrix calculations for gpu skinning。我認爲答案代碼是絕對正確的,不要再認爲應該使用矩陣。因此,濫用矩陣極大地將我的注意力轉移到局部變換矩陣中。回到我的問題部分的結果圖像是當我改變局部變換矩陣返回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]); 
    } 
} 

我做如下改變:

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之前給我,只說明「 assimp文檔中相對於節點父節點的轉換「。對於某些測試,我發現mTransformation是當前節點相對於父節點的綁定姿態矩陣,如果我在骨骼節點上使用它們。讓我給出一張照片,記錄頭骨上的矩陣。

Prove

左側部分爲transform這是從assimp_node->mTransformation .The右邊取我unmove動畫的localTransform這是由按鍵從nodeAnim->mPositionKeysnodeAnim->mRotationKeysnodeAnim->mScalingKeys計算。

回頭看我做什麼,我做了一個綁定姿勢穿越 - 兩次,所以在我的問題部分的圖像看起來只是單獨而不是意大利麪條:)

在最後,讓我展示一下我之前unmove動畫測試和正確的動畫結果。

Result

(對於每個人,如果我的觀念是錯誤的,請點我出去!THX。)