An almost monolog on stackoverflow helped me to get skeleton animations at least usably working in render demo that I’ve put up in android market. By the way on how the shader computes mat4 * vec4 can be found in the opengles shading language specification.
All right I’ve got a mesh with around 8000+ vertices, it’s sekelon with up to 128 bones and all it’s bone keyframe transformations up to 400 each animation.
Ahri Skeleton
On desktop I could use this in vertex shader in order to compute the skinning:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | ... uniform mat4 u_BoneTransform[128]; in vec3 in_Position; in vec3 in_Normal; in vec4 in_BoneID; in vec4 in_Weights; ... void main(void) { vec3 position = (in_Weights[0] * (u_BoneTransform[ int(in_BoneID[0]) ] * vec4(in_Position, 1.0)).xyz)+ (in_Weights[1] * (u_BoneTransform[ int(in_BoneID[1]) ] * vec4(in_Position, 1.0)).xyz)+ (in_Weights[2] * (u_BoneTransform[ int(in_BoneID[2]) ] * vec4(in_Position, 1.0)).xyz)+ (in_Weights[3] * (u_BoneTransform[ int(in_BoneID[3]) ] * vec4(in_Position, 1.0)).xyz); vec3 normal = (in_Weights[0] * (u_BoneTransform[ int(in_BoneID[0]) ] * vec4(in_Normal, 0.0)).xyz) + (in_Weights[1] * (u_BoneTransform[ int(in_BoneID[1]) ] * vec4(in_Normal, 0.0)).xyz) + (in_Weights[2] * (u_BoneTransform[ int(in_BoneID[2]) ] * vec4(in_Normal, 0.0)).xyz) + (in_Weights[3] * (u_BoneTransform[ int(in_BoneID[3]) ] * vec4(in_Normal, 0.0)).xyz); ... } |
As you can see there are a lot of bones. On android I don’t have the luxury of wasting variables to make simple lookups. I’d need 128 mat4 * 4 vec4 = 512 shader uniform vector variables and my samsung galaxy s2 only supports 304. Anyway I can think of 2 solutions:
- divide the mesh into an ‘ok’ amount of bones and render the mesh with less bone matrices
- do the skinning in software and update the vertex/normal buffers
I’d prefer the first solution, because I’d have to update merely the bone matrices uniform each frame instead of the entire vertex buffer. At this point however I have no idea how to divide the mesh because each vertex can have up to 4 bone influences and so I implemented the 2nd choice for now. Work in progress…
Here is the equivalent java code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | /** * skinning * @param w weights == in_Weights * @param i bone influence indices == in_BoneID * @param m bone transformation matrix == u_BoneTransform[128] * @param t target vector == in_Position * @param f flag 1 == in_Position, 0 == in_Normal */ public static void marryBonesWithVector(float[] w, int[] i, Matrix4[] m, Vector3 t, float f) { t.set( // x influence for bone 1 w[0] * (m[i[0]].values[0] * t.x + m[i[0]].values[4] * t.y + m[i[0]].values[8] * t.z + m[i[0]].values[12] * f) + // x influence for bone 2 w[1] * (m[i[1]].values[0] * t.x + m[i[1]].values[4] * t.y + m[i[1]].values[8] * t.z + m[i[1]].values[12] * f) + // x influence for bone 3 w[2] * (m[i[2]].values[0] * t.x + m[i[2]].values[4] * t.y + m[i[2]].values[8] * t.z + m[i[2]].values[12] * f) + // x influence for bone 4 w[3] * (m[i[3]].values[0] * t.x + m[i[3]].values[4] * t.y + m[i[3]].values[8] * t.z + m[i[3]].values[12] * f), // y influence for bone 1 w[0] * (m[i[0]].values[1] * t.x + m[i[0]].values[5] * t.y + m[i[0]].values[9] * t.z + m[i[0]].values[13] * f) + // y influence for bone 2 w[1] * (m[i[1]].values[1] * t.x + m[i[1]].values[5] * t.y + m[i[1]].values[9] * t.z + m[i[1]].values[13] * f) + // y influence for bone 3 w[2] * (m[i[2]].values[1] * t.x + m[i[2]].values[5] * t.y + m[i[2]].values[9] * t.z + m[i[2]].values[13] * f) + // y influence for bone 4 w[3] * (m[i[3]].values[1] * t.x + m[i[3]].values[5] * t.y + m[i[3]].values[9] * t.z + m[i[3]].values[13] * f), // z influence for bone 1 w[0] * (m[i[0]].values[2] * t.x + m[i[0]].values[6] * t.y + m[i[0]].values[10] * t.z + m[i[0]].values[14] * f) + // z influence for bone 1 w[1] * (m[i[1]].values[2] * t.x + m[i[1]].values[6] * t.y + m[i[1]].values[10] * t.z + m[i[1]].values[14] * f) + // z influence for bone 1 w[2] * (m[i[2]].values[2] * t.x + m[i[2]].values[6] * t.y + m[i[2]].values[10] * t.z + m[i[2]].values[14] * f) + // z influence for bone 1 w[3] * (m[i[3]].values[2] * t.x + m[i[3]].values[6] * t.y + m[i[3]].values[10] * t.z + m[i[3]].values[14] * f) ); } |
Major thankies for the article.Thanks Again. Much obliged.
I just want to say I
Excellent read, I just passed this onto a friend who was doing some research on that. And he just bought me lunch as I found it for him smile Thus let me rephrase that: Thanks for lunch! “Any man would be forsworn to gain a kingdom.”