美文网首页
捏脸系统中的差异矩阵推导

捏脸系统中的差异矩阵推导

作者: duanoldfive | 来源:发表于2022-06-15 18:49 被阅读0次

    在上篇文章<<Unity 人物捏脸的实现>>中,最后留了一个悬念,就是

    差异矩阵
    这个矩阵是怎么计算来的。这里给大家补上。

    几何意义

    Mesh绑定(图1)

    还是以上图为例,蓝色小球Vertex是Mesh上的一个顶点,它绑定骨骼BN_20(在BN_20本地坐标空间下,坐标值保持不变)。捏脸的时候不可能逐顶点调整位置,那就要调整骨骼,因为存在绑定关系,所以调整骨骼的时候,顶点小球的世界坐标也会跟着变化,以保证在骨骼本地坐标下位置不变。有点绕口。好好理一下就明白了。
    假设调整骨骼到一个新的位置,比如我们把骨骼BN_10绕Z轴旋转90度(会带动BN_20移动,进而影响顶点蓝色小球)


    骨骼旋转后顶点的新位置(图2)

    现在顶点已经到了新位置,我们的目的是要计算新位置(图2中的篮球位置)相对于调整前的骨骼(图1中的BN_20)的Bindpose(从模型空间到骨骼本地坐标的转换矩阵),这样,骨骼动画把骨骼重置位置后,小球就能保证还在调整后的位置了。我们为了达到下图的效果:


    目的效果(图3)

    还记得上篇文章中的公式吧:


    移动顶点,得到新的坐标(图4)
    这里这个M_translation就是一个矩阵,原来的骨骼乘上这个矩阵,就会到新的位置(红色骨骼从图1的位置到图2的位置),同样,顶点位置乘上这个矩阵,也会到新的位置(由图1到图2中篮球位置).所以我们可以叫它差异矩阵。

    差异矩阵推导

    假设原本(图1中)BN_20本地空间内的一点A 两边都乘上差异矩阵就会到新位置(图2中).
    A的新位置,即可以用原来骨骼的本地到世界矩阵乘上差异矩阵来求出,也可以用新姿态下骨骼的本地到世界矩阵来求出,则推导出
    其中骨骼的新旧姿态下的本地到世界矩阵都是已知量,则差异矩阵就可以求出来了。 图5
    第一行,可以看做等号两边的尾部都乘上原骨骼的本地到世界矩阵的逆矩阵,则左边成了差异矩阵乘以单位矩阵,右边就是结果。其中本地到世界的逆矩阵就是世界到本地矩阵,所以推出第二行。这里的M_delta就是图4中的M_translation,命名有点混乱。上篇文章说了图4中下面一行括号中的就是新的bindpose,把图5结果带入图4的括号中,则
    新的Bindpose
    对应的代码如下:
    Matrix4x4 newBindPose = oldBone.transform.worldToLocalMatrix * newBone.transform.localToWorldMatrix * oldBone.transform.worldToLocalMatrix * mesh.localToWorldMatrix;
    

    至此骨骼的新Bindpose已经计算完毕,下面给Mesh应用上,这样骨骼位置不变,但是顶点相对于骨骼的位置发生了改变,骨骼动画驱动骨骼不停变化过程中,顶点始终和骨骼保持新的相对位置,从而达到捏脸的效果。应用新的bindpose代码入下:

        //给模型应用新的BindPose
        private static void ApplyNewBindpose(GameObject meshObj, Dictionary<string, Matrix4x4> bindposes)
        {   
            SkinnedMeshRenderer smr = meshObj.GetComponent<SkinnedMeshRenderer>();
            if (smr == null)
            {
                Debug.LogError("Not found SkinnedMeshRenderer " + meshTransform.name);
                return;
            }
            //实例化一份新的mesh,因为要修改mesh的数据,原始的mesh不要动,只读
            Mesh mesh = GameObject.Instantiate<Mesh>(smr.sharedMesh);
            
            Matrix4x4[] bindposes = mesh.bindposes;
            Transform[] bones = smr.bones;
            for (int i = 0; i < bones.Length; ++i)
            {
                if (bindposes.ContainsKey(bones[i].name))
                {
                    bindposes[i] = bindposes[bones[i].name];
                }
            }
    
            mesh.bindposes = bindposes;
            smr.sharedMesh = mesh;
        }
    

    今天的内容都是数学公式,比较枯燥,大家如果不喜欢推导过程,可以直接拿结果去用,bindpose_new那个公式。
    好了,捏脸系统的完整思路就是这样了,欢迎大家指出错误和不足之处,共同进步。
    【转载请注明出处】

    相关文章

      网友评论

          本文标题:捏脸系统中的差异矩阵推导

          本文链接:https://www.haomeiwen.com/subject/ldpxvrtx.html