简体   繁体   中英

Android OpenGL ES2 Skeletal animation child bone matrix

If I have a geometry with single bone I can render it with animation but if it has 2 bones the vertices do not animated correctly, I load a JSON format exported from blender, as I understand the child bone relative matrix multiplied by its parent absolute matrix then multiplied by it relative matrix inverse then each bone matrix multiplied by the key frame matrix to get the final matrix then send these matrices to the vertex shader. I set the weight and bone index as attributes with the VBO.

for (int y = 0; y < g.getBones().size(); y++) {
    com.thatulborooj.opengl.objects.Bone bone = new com.thatulborooj.opengl.objects.Bone();
    Bone gBone = g.getBones().get(y);
    float[] t = new float[] { gBone.getPosition().get(0), gBone.getPosition().get(0), gBone.getPosition().get(0) };
    float[] r = new float[] { gBone.getRotation().get(0), gBone.getRotation().get(1), gBone.getRotation().get(2), gBone.getRotation().get(3) };
    float[] s = gBone.getScale() == null ? new float[] { 1, 1, 1 } : new float[] { gBone.getScale().get(0), gBone.getScale().get(1), gBone.getScale().get(2) };
    float[] nMatrix = createMat4(t, r, s);
    float[] inverseMatrix = new float[16];
    if (gBone.getParent() == -1) {
        bone.setBindMatrix(nMatrix);
        invertM(inverseMatrix, 0, nMatrix, 0);
        float[] fMatrix = new float[16];
        multiplyMM(fMatrix, 0, nMatrix, 0, inverseMatrix, 0);
        bone.setFinalMatrix(fMatrix);
    } else {
        float[] pMatrix = mesh.getBones().get(gBone.getParent()).getBindMatrix();
        float[] bMatrix = new float[16];
        multiplyMM(bMatrix, 0, pMatrix, 0, nMatrix, 0);
        bone.setBindMatrix(bMatrix);
        invertM(inverseMatrix, 0, bMatrix, 0);
        float[] fMatrix = new float[16];
        multiplyMM(fMatrix, 0, bMatrix, 0, inverseMatrix, 0);
        bone.setFinalMatrix(fMatrix);
        }
        bone.setName(gBone.getName());
        bone.setParent(gBone.getParent());
        mesh.getBones().add(bone);
    }
}

this is the render code

    if (animated) {
        if (playing) {
            if (curFrame < frames) {
                for (int i = 0; i < bones.size(); i++) {
                    Key key = animations.get(0).getHierarchy().get(i).getKeys().get(curFrame);
                    setIdentityM(aMatrix, 0);
                    multiplyMM(aMatrix, 0, bones.get(i).getFinalMatrix(), 0, key.getMatrix(), 0);
                    for (int q = 0; q < 16; q++) {
                        bs[i][q] = aMatrix[q];
                    }
                }
                curFrame++;
            } else
                curFrame = 0;
            shaderProgram.setBones(bs);
        }
    }

this is the function which make the matrix from position, quaternion and scale:

public static float[] createMat4(float[] t, float[] r, float[] s) {
    float[] mat4 = new float[16];
    float[] T = new float[16];
    float[] R = quaternionToMatrix(r);
    float[] S = new float[16];
    setIdentityM(T, 0);
    setIdentityM(S, 0);
    translateM(T, 0, t[0], t[1], t[2]);
    scaleM(S, 0, s[0], s[1], s[2]);
    float[] temp = new float[16];
    multiplyMM(temp, 0, T, 0, R, 0);
    multiplyMM(mat4, 0, temp, 0, S, 0);
    return mat4;
}

private static float[] quaternionToMatrix(float[] q) {
    float[] m = new float[16];
    final float xx = q[0] * q[0];
    final float xy = q[0] * q[1];
    final float xz = q[0] * q[2];
    final float xw = q[0] * q[3];
    final float yy = q[1] * q[1];
    final float yz = q[1] * q[2];
    final float yw = q[1] * q[3];
    final float zz = q[2] * q[2];
    final float zw = q[2] * q[3];
    // Set matrix from quaternion
    m[0] = 1 - 2 * (yy + zz);
    m[1] = 2 * (xy - zw);
    m[2] = 2 * (xz + yw);
    m[3] = 0;
    m[4] = 2 * (xy + zw);
    m[5] = 1 - 2 * (xx + zz);
    m[6] = 2 * (yz - xw);
    m[7] = 0;
    m[8] = 2 * (xz - yw);
    m[9] = 2 * (yz + xw);
    m[10] = 1 - 2 * (xx + yy);
    m[11] = 0;
    m[12] = 0;
    m[13] = 0;
    m[14] = 0;
    m[15] = 1;
    return m;
}

I set the bones array uniform by this line:

private void setBonesUniforms() {
    for (int i = 0; i < bones.length; i++) {
        int uBonesLocation = glGetUniformLocation(program, "bones[" + i + "]");
        glUniformMatrix4fv(uBonesLocation, 1, false, bones[i], 0);
    }
}

finally this is the vertex shader:

vec4 newVertex=vertexPosition;
vec4 newNormal=vertexNormal;
if(animated==1){
    int index;
    index=int(skinindex.x);
    newVertex = (bones[index]  * skinweight.x) * vertexPosition;
    newNormal = (bones[index]  * skinweight.x) * vertexNormal;

    index=int(skinindex.y);
    newVertex += (bones[index] * skinweight.y) * vertexPosition;
    newNormal += (bones[index] * skinweight.y) * vertexNormal;
}
newVertex=vec4(newVertex.xyz, 1.0);

I noticed that changing the child bone matrix with any floats do not change the result.

您可以使用AssimpLib,它易于使用,并支持大多数格式,包括带有骨骼动画的collada

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM