I'm trying to implement GPU skinning using md5 format. But after many hours of trying to figure out what's wrong I still get incorrect results for animation.
Firstly, I calculate Bind Pose and Inverse Bind Pose matrices for each bone:
[source lang="cpp"]Mat4x4 trans; trans.BuildTranslation(joints[i].posObjectSpace); //build translation from joint position in object spaceMat4x4 rot = joints[i].rot.BuildMatrix(); //joints[i].rot is unit quaternionMat4x4 bindPoseTransform = rot *trans;(**ppBones)[i].bindPoseTransform = rot * trans;(**ppBones)[i].inverseBindPoseTransform = bindPoseTransforms.Inverse();[/source]
After that I calculate vertex positions in object space and fill vertex buffer with positions, indices and weights
[source lang="cpp"]for (int j = 0; j < mesh.numVertices; j++) { //calculate position Vec3 pos(0.0f, 0.0f, 0.0f); md5_vertex vertex = mesh.vertices[j]; for (int k = 0; k < vertex.numWeights; k++) { md5_weight weight = mesh.weights[vertex.startWeight+k]; md5_joint joint = joints[weight.jointId]; //calculate vertex position in object space temp = Vec4(weight.posJointSpace); temp.w = 0.0f; Quaternion q = joint.rot; Quaternion revq = joint.rot.Inverse(); Vec3 temp3 = q * temp * revq; temp3 = temp3 + joint.posObjectSpace; temp3 *= weight.weight; pos += temp3;// * weight.weight; }//finally fill vertex structure with data v[j].pos = pos; v[j].texCoord = vertex.texCoords; for (int k = 0; k < vertex.numWeights; k++) { v[j].bonesIndices[k] = mesh.weights[vertex.startWeight+k].jointId; } v[j].bonesWeights = Vec4( vertex.numWeights > 0 ? mesh.weights[vertex.startWeight].weight : 0.0f, vertex.numWeights > 1 ? mesh.weights[vertex.startWeight+1].weight : 0.0f, vertex.numWeights > 2 ? mesh.weights[vertex.startWeight+2].weight : 0.0f, vertex.numWeights > 3 ? mesh.weights[vertex.startWeight+3].weight : 0.0f); assert( fabs(v[j].bonesWeights.x + v[j].bonesWeights.y + v[j].bonesWeights.z + v[j].bonesWeights.w - 1.0f) < 0.001f);[/source]
Next step is to calculate joints transforms in object space for each frame in animation:
[source lang="cpp"]//pos - current joint position relative to the parent (if have any)//rot - quaternion representng rotation of current joint//if we have parent joint... if (joint.id >= 0) { md5_joint parent = joints[joint.id]; //calculate joint pos Quaternion parentOrient = parent.rot; Vec4 temp( pos.x, pos.y, pos.z, 0.0f); pos = parentOrient * temp * parentOrient.Inverse(); pos = pos + parent.posObjectSpace; //concatenate rotation rot = parentOrient * rot'; rot.Normalize(); } //update joint position and orientation joints[j].posObjectSpace = pos; joints[j].rot = rot; Mat4x4 trans; trans.BuildTranslation(pos); Mat4x4 rotation = rot.BuildMatrix(); Mat4x4 transform = rotation * trans; newFrame.m_positions.push_back(pos); newFrame.m_rotations.push_back(rot); newFrame.m_transforms.push_back(transform); //use just fixed frames for debugging, no interpolation for now...[/source]
//Finally I render animation!
[source lang="cpp"]SkinningBuffer skinningBuffer;for (int j = 0; j < m_numBones; j++) { Mat4x4 world = pScene->GetWorld(); //world transform. identity for now Mat4x4 inverseBindPose = m_pBones[j].m_inverseBindPose; //inverse bind pose matrixMat4x4 curObjectSpace = m_pAnimations[0].GetTransform(j); //current object space transform for given bone skinningBuffer.bonesTransforms[j] = Transpose(inverseBindPose * curObjectSpace * world); } //next frame... m_pAnimations[0].NextFrame(); //update constant buffer for shader m_pSkinningBuffer->UpdateAndSet(&amp;skinningBuffer, ST_Vertex, 3);[/source]
Here is my vertex shader code:
[source lang="cpp"]//////////////////////////////////////////////////Constant Buffers////////////////////////////////////////////////cbuffer MatrixBuffer : register(b0){ float4x4 m_ViewProjection;}cbuffer LightBuffer : register(b1){ float4 ambientColor : packoffset(c0); float4 diffuseColor : packoffset(c1); float3 lightDir : packoffset(c2.x); float padb1 : packoffset(c2.w);};cbuffer CameraBuffer : register(b2){ float3 eyePos : packoffset(c0.x); float padb2 : packoffset(c0.w);};cbuffer SkinningBuffer : register(b3){ float4x4 m_BoneWorld[50];};/////////////////////////////////////////////////Texture Resources and Sampler States///////////////////////////////////////////////Texture2D objTexture : register(t0);Texture2D bumpMap : register(t1);Texture2D heightMap : register(t2);SamplerState LinearSampler : register(s0);/////////////////////////////////////////////////Shaders' input/output layouts///////////////////////////////////////////////struct vs_input{ float3 pos : POSITION; uint4 bones : BONEIDS; float4 weights : BONEWEIGHTS; float2 texCoord : TEXCOORD; float3 normal : NORMAL; float3 tangent : TANGENT; float3 binormal : BINORMAL;};struct ps_input{ float4 pos : SV_POSITION; float2 texCoord : TEXCOORD; float4 color : COLOR; float3 normal : NORMAL; float3 tangent : TANGENT; float3 binormal : BINORMAL;};////////////////////////////////////////////////Vertex Shader//////////////////////////////////////////////ps_input VS(vs_input input){ ps_input output; //apply bone transforms to position output.pos = mul(float4(input.pos, 1.0f), m_BoneWorld[input.bones.x] * input.weights.x); output.pos += mul(float4(input.pos, 1.0f), m_BoneWorld[input.bones.y] * input.weights.y); output.pos += mul(float4(input.pos, 1.0f), m_BoneWorld[input.bones.z] * input.weights.z); output.pos += mul(float4(input.pos, 1.0f), m_BoneWorld[input.bones.w] * input.weights.w); output.pos = mul(output.pos, m_ViewProjection); //apply bone transforms to normal output.normal = mul(float4(input.normal, 1.0f), m_BoneWorld[input.bones.x]) * input.weights.x; output.normal += mul(float4(input.normal, 1.0f), m_BoneWorld[input.bones.y]) * input.weights.y; output.normal += mul(float4(input.normal, 1.0f), m_BoneWorld[input.bones.z]) * input.weights.z; output.normal += mul(float4(input.normal, 1.0f), m_BoneWorld[input.bones.w]) * input.weights.w; //apply bone transforms to tangent output.tangent = mul(float4(input.tangent, 1.0f), m_BoneWorld[input.bones.x]) * input.weights.x; output.tangent += mul(float4(input.tangent, 1.0f), m_BoneWorld[input.bones.y]) * input.weights.y; output.tangent += mul(float4(input.tangent, 1.0f), m_BoneWorld[input.bones.z]) * input.weights.z; output.tangent += mul(float4(input.tangent, 1.0f), m_BoneWorld[input.bones.w]) * input.weights.w; //apply bone transforms to normal output.binormal = mul(float4(input.binormal, 1.0f), m_BoneWorld[input.bones.x]) * input.weights.x; output.binormal += mul(float4(input.binormal, 1.0f), m_BoneWorld[input.bones.y]) * input.weights.y; output.binormal += mul(float4(input.binormal, 1.0f), m_BoneWorld[input.bones.z]) * input.weights.z; output.binormal += mul(float4(input.binormal, 1.0f), m_BoneWorld[input.bones.w]) * input.weights.w; //transfer texture coordinates output.texCoord = input.texCoord; //output result return output;}[/source]
I'm pretty sure, that vertex object space positions are calculated correctly and shaders code works ok as I get correct results for Bind Pose. But when animation starts it doesn't work:


The model is taken from http://www.katsbits.com/download/models/ .
To simplify debugging, I created simple model from 2 joints and 4 vertices with two frames of animation and still it doesn't work, but it seems that i checked everything...

This is capture of the second frame(of two). The first frame is rendering correctly showing this black plane lies straight on the floor. In the second frame I just have left part lifted up a bit. Obvisously, I renders incorrectly for me. I attached file with this plane to my post.
Could you give me some ideas why I get wrong result? If you already have implemented GPU skinning could you type your matrices for inverseBindPose of each bone and curObjectTransform of bones for each frame, so that i can compare them with mine(for that black plane model)? I would really appreciate any help!
Thanks!
Attached Files
Edited by DgekGD, 13 November 2012 - 05:15 PM.







