顶点蒙皮过程
骨骼的运动和顶点蒙皮是不同的两个过程,骨骼的运动比较简单,就不详细说了。
先贴一下顶点计算公式:
1 VertexPos = MJ-0 * weight[index0].pos * weight[index0].weight+ ... + MJ-N * weight[indexN].pos * weight[indexN].weight;
先解释一下变量意思:
MJ-0:权重关联到的骨骼的矩阵
weight[index0].pos 权重偏移量
weight[index0].weight 权重比率
一个顶点可以关联到N个权重,一般我们在m5模型中会限制在4个以内,所有权重值的和为1,权重偏移量就是相对的关联骨骼的位置。具体实现代码:
1 VGAVertex renderVertex = { 0,0,0, static_cast<float>(uv[0]),static_cast<float>(uv[1]) }; 2 3 for (int j = 0; j < pMd5Vertex->weightCount; j++) 4 { 5 shared_ptr<Weight> weight = weights[pMd5Vertex->weightIndex + j]; 6 7 // shared_ptr<FrameJoint> joint = frame->jointVec[weight->jointIndex]; 8 9 shared_ptr<Md5Joint> md5Joint = jointVec[weight->jointIndex]; 10 11 //FbxVector4 wv = joint->m_worldMatrix.MultT(weight->weightPos); 12 13 FbxVector4 wv = md5Joint->bindMatrix.MultT(weight->weightPos); 14 15 //wv += joint.m_Pos; 16 17 wv *= weight->weight; 18 19 renderVertex.x += static_cast<float>(wv[0]);//joint.m_Pos[0] + wv[0] 20 renderVertex.y += static_cast<float>(wv[1]); 21 renderVertex.z += static_cast<float>(wv[2]); 22 }
这里传入bindpose下或动画中的矩阵就能分别显示不同的模型,这是第一种算法。
第二种算法使用bindpose下的顶点来计算当前顶点。假定顶点关联的权重只有一个的时候,最终顶点 = 骨骼矩阵*权重偏移量,所以根据公式,我们需要先求出权重偏移量,权重偏移量 = 反转骨骼矩阵 * 最终顶点,代入上面的公式就是:
VertexPos = (MJ-0 * MJ-0(bindpose)-1)* VertexPosbindpose * weight[index0].weight+ ... + (MJ-N * MJ-N(bindpose)-1)* VertexPosbindpose * weight[indexN].weight ;
这种情况下,我们就不需要权重偏移量,需要最终顶点和bindpose下的骨骼反向矩阵。
第一种如果要显示静态模型,也得当按动画一样来计算,而且不需要保存最终顶点。而第二种在不需要动画的时候能显示静态模型,但是计算量会增加,因为多了一个骨骼逆矩阵,away3d中就是这样的计算方法。
1 for (size_t i = 0; i < vertexCount; i++) 2 { 3 shared_ptr<Md5Vertex> pMd5Vertex = vertices[i]; 4 5 FbxVector2 uv; 6 //if (useIndex) 7 // uv = pMesh->GetElementUV()->GetDirectArray().GetAt(pMd5Vertex->uvIndex); 8 //else 9 uv = pMd5Vertex->uv; 10 11 VGAVertex bindposeVertex = m_pVertices[i]; 12 13 FbxVector4 bindposeVertexVec4 = FbxVector4(bindposeVertex.x, bindposeVertex.y, bindposeVertex.z);//bindpose最终顶点 14 15 VGAVertex renderVertex = { 0,0,0, bindposeVertex.u,bindposeVertex.v }; 16 17 18 for (int j = 0; j < pMd5Vertex->weightCount; j++) 19 { 20 shared_ptr<Weight> weight = weights[pMd5Vertex->weightIndex + j]; 21 22 shared_ptr<FrameJoint> joint = frame->jointVec[weight->jointIndex]; 23 24 shared_ptr<Md5Joint> md5Joint = jointVec[weight->jointIndex]; 25 26 FbxAMatrix invMatrix = md5Joint->bindMatrix.Inverse(); 27 FbxVector4 wv = invMatrix.MultT(bindposeVertexVec4); 28 29 //wv += joint.m_Pos; 30 31 wv = joint->m_worldMatrix.MultT(wv); //此处wv = weight->weightPos 32 33 //wv = joint->m_worldMatrix.MultT(weight->weightPos); 34 35 wv *= weight->weight; 36 37 renderVertex.x += static_cast<float>(wv[0]);//joint.m_Pos[0] + wv[0] 38 renderVertex.y += static_cast<float>(wv[1]); 39 renderVertex.z += static_cast<float>(wv[2]); 40 } 41 42 *pVertices = renderVertex; 43 pVertices++; 44 }
可以将骨骼当前矩阵先乘上bindpose的逆矩阵,
1 FbxAMatrix invMatrix = md5Joint->bindMatrix.Inverse(); 2 FbxVector4 wv = (joint->m_worldMatrix * invMatrix).MultT(bindposeVertexVec4);