我用来剥皮我的网格的公式是直接的:
weighted; for (i = 0; i < joint_influences; i++) { weighted += joint[joint_index[i]]->parent->local_matrix * joint[joint_index[i]]->local_matrix * skin->inverse_bind_pose[joint_index[i]] * position * skin->weight[j]; } position = weighted;
就文献而言,这是正确的公式.现在,COLLADA指定了关节的两种旋转方式:局部和全局.您必须将旋转连接在一起以获得关节的局部变换.
COLLADA文档没有区别的是联合的本地轮换和联合的全局旋转.但是在我看到的大多数模型中,旋转可以具有rotate(全局)或jointOrient(local)的id.
当我忽略全局旋转,只使用本地旋转时,我得到模型的绑定姿势.但是,当我将全球轮转加入联合地方转型时,奇怪的事情开始发生.
这是没有使用全球轮换:
这是全球轮转:
在两个屏幕截图中,我使用线绘制骨架,但是在第一个视图中,它是不可见的,因为关节在网格内.在第二个屏幕截图中,顶点遍布当地!
为了比较,这是第二个截图应该是这样的:
很难看到,但是您可以看到关节在第二个截图中位于正确的位置.
但现在奇怪的是.如果我忽略由COLLADA指定的反向绑定姿势,而是采用联合的父局部变换的倒数乘以联合局部变换,我得到以下内容:
在这个截图中,我从每个顶点绘制一条线到有影响的关节.我得到绑定姿势的事实不是很奇怪,因为公式现在变成:
world_matrix * inverse_world_matrix * position * weight
但是它导致我怀疑COLLADA的反绑定姿势是错误的空间.
所以我的问题是:COLLADA在什么空间中指定了其反绑定姿势?而且如何将逆绑定姿势转换到我需要的空间?
解决方法
最终我最终在SceneAnimator :: GetBoneMatrices中包含以下内容:
// Bone matrices transform from mesh coordinates in bind pose to mesh coordinates in skinned pose // Therefore the formula is offsetMatrix * currentGlobalTransform * inverseCurrentMeshTransform for( size_t a = 0; a < mesh->mNumBones; ++a) { const aiBone* bone = mesh->mBones[a]; const aiMatrix4x4& currentGlobalTransform = GetGlobalTransform( mBoneNodesByName[ bone->mName.data ]); mTransforms[a] = globalInverseMeshTransform * currentGlobalTransform * bone->mOffsetMatrix; }
globalInverseMeshTransform始终是身份,因为网格不会转换任何东西. currentGlobalTransform是绑定矩阵,联合的父本的本地矩阵与联合的局部矩阵相连.而mOffsetMatrix是直接来自皮肤的反向绑定矩阵.
我检查了这些矩阵的值到我自己的(哦,是的,我在一个手表窗口比较它们),它们是完全一样的,可能是0.0001%,但这是微不足道的.那么为什么Assimp的版本工作和我的,即使公式是一样的?
这是我得到的:
当Assimp终于将矩阵上传到皮肤着色器时,它们执行以下操作:
helper->piEffect->SetMatrixTransposeArray( "gBoneMatrix",(D3DXMATRIX*)matrices,60);
Waaaaait一秒.他们上传他们转置?这不是那么容易.没门.
对.
还有一件事我做错了:在应用皮肤矩阵之前,我将坐标转换成正确的系统(厘米到米).这导致完全失真的模型,因为矩阵是为原始坐标系设计的.
未来GOOGLERS
>按您收到的顺序读取所有节点变换(旋转,转换,缩放等).
将它们连接到一个关节的局部矩阵.
采取联合的父母,并将其与本地矩阵相乘.
>将其存储为绑定矩阵.
>阅读皮肤信息.
>存储关节的逆绑定姿势矩阵.
>存储每个顶点的关节权重.
将具有反向绑定姿势矩阵的绑定矩阵相乘并将其转置,称之为剥皮矩阵.
>将占位矩阵乘以关节重量,并将其添加到加权位置.
>使用加权位置进行渲染.
完成!