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