2016-09-22 6 views
6

Kürzlich arbeite ich an Knochenanimation Import, also habe ich ein 3D-Minecraft-like-Modell mit einigen IK-Technik, Assimp Animation Import zu testen. Das Ausgabeformat ist COLLADA (*. Dae), und das Werkzeug, das ich verwendete, ist Blender. Auf der Programmierseite ist meine Umgebung opengl/glm/assimp. Ich denke, dass diese Informationen für mein Problem genug sind. Eine Sache, die Animation des Modells, ich notiere gerade 7 unmoove keyframe für das Testen von assimp Animation.Assimp Animation Knochen Transformation

Zuerst denke ich, dass meine Transformation außer lokalen Transformationsteil korrekt ist, also lassen Sie die Funktion nur glm::mat4(1.0f) zurückgeben, und das Ergebnis zeigt das Bind Pose (nicht sicher) Modell. (Siehe Bild unten)

Zweitens den Wert glm::mat4(1.0f)-bone->localTransform = transform * scaling * glm::mat4(1.0f); Kehre, dann das Modell deformieren. (Siehe Bild unten)

Testbild und das Modell in Blender: test and origin (bone->localTransform = glm::mat4(1.0f) * scaling * rotate;: Dieses Bild ist unter der Erde :()

Der Code hier:

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

Der Code Idee verwiesen wird Matrix calculations for gpu skinning und Skeletal Animation With Assimp

Insgesamt fekt ich alle Informationen von Assimp zu MeshModel und speichern Sie es in der Knochenstruktur, also denke ich, dass die Information in Ordnung ist?

Das letzte, was, mein Vertex-Shader-Code:

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

Wenn meine Frage oben, Mangel an Code oder vage beschreiben, lassen Sie es mich wissen, Dank!

Edit: (1)

In weiteren, meine Knochen Informationen wie diese (Code Holen Teil):

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

einige Transformationsvektor hatte, so meinen Code über glm::mat4 transform = GetInterpolateTransform(time, bone->transformKeys);, Absloutely, erhalten die gleichen Wert von ihm. Ich bin mir nicht sicher, ob ich eine Nomove-Keyframe-Animation gemacht habe, die die Transformationswerte liefert oder nicht (natürlich hat sie 7 Keyframes).

Ein Keyframe Inhalt wie folgt (Debug auf Kopf Knochen): keyframe from head 7 verschiedene Keyframe, gleichen Vektor Wert.

Edit: (2)

Wenn Sie meine dae-Datei zu testen, habe ich es in jsfiddle, kommen und es nehmen :). Eine andere Sache, in Unity meine Datei funktioniert korrekt, so denke ich, vielleicht nicht meine lokale Transformation tritt das Problem, es scheint, das Problem könnte eine andere wie ParentTransform oder bone-> transform ... etc? Ich aslo hinzufügen lokaler Transformationsmatrix mit allen Knochen, kann aber nicht herausfinden, warum COLLADA diesen Wert für meine unmove Animation enthält ...

Antwort

4

Für Mengen von Tests und fand schließlich das Problem ist der UpdateBone() Teil.

Bevor ich auf mein Problem hinweisen, muss ich sagen, die Reihe der Matrixmultiplikation lässt mich verwirren, aber als ich die Lösung gefunden habe, macht es mich total (vielleicht nur 90%), die ganze Matrix zu realisieren. Das Problem kommt von dem Artikel Matrix calculations for gpu skinning. Ich nahm an, der Antwort-Code ist absolut richtig und denke nicht, dass mehr über die Matrix verwendet werden sollte. Die missbräuchliche Verwendung der Matrix überträgt somit mein Aussehen in die lokale Transformationsmatrix. Zurück zu dem Ergebnisbild in meinem Frageabschnitt ist Bind Pose, wenn ich die lokale Transformationsmatrix ändere, um glm::mat4(1.0f) zurückzugeben.

Die Frage ist also, warum die veränderten die Bindung darstellen? Ich nahm an, das Problem müsse lokale Transformation im Knochenraum sein, aber ich liege falsch. Bevor ich die Antwort geben, schauen unten auf den Code:

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

Und ich die Änderung wie folgt:

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

Ich weiß nicht, was die assimp_node->mTransformation mir vor geben, nur die Beschreibung " Die Transformation relativ zum Elternknoten "in der Assimp-Dokumentation. Für einige Tests habe ich festgestellt, dass die mTransformation die Bindehaltung Matrix ist, die den aktuellen Knoten relativ zu Eltern, wenn ich diese auf Knochenknoten verwenden. Lassen Sie mich ein Bild geben, das die Matrix am Kopfknochen erfasst.

Prove

Der linke Teil ist die transform, die von assimp_node->mTransformation .Der rechten Teil geholt ist mein unmove-Animation localTransform, die von den Schlüssel aus nodeAnim->mPositionKeys, nodeAnim->mRotationKeys und nodeAnim->mScalingKeys berechnet wird.

Schauen Sie zurück, was ich tat, machte ich eine bind tranformation Pose zweimal, so dass das Bild in meiner Frage Abschnitt nur seperate aussehen, aber nicht Spaghetti :)

Am letzten, lassen Sie mich zeigen, was ich zuvor das Testen der unbewegten Animation und das korrekte Animationsergebnis.

Result

(Für alle, Wenn mein Konzept falsch ist, zeigen Sie mich raus! Thx.)

Verwandte Themen