You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何逆运算线性混合蒙皮(Linear Blend Skinning)获取原始点位置?

线性混合蒙皮(LBS)逆运算:还原原始绑定点位置

先明确正向蒙皮的数学逻辑

线性混合蒙皮的核心公式(齐次坐标下):

P_skinned = Σ( w_i * M_current_i * M_bind_inv_i * P_bind )

其中:

  • P_bind:原始绑定点位置(目标求解值)
  • P_skinned:蒙皮后点位置(已知值)
  • M_current_i:第i个关节的当前世界变换矩阵(已知)
  • M_bind_inv_i:第i个关节绑定姿态世界矩阵的逆(已知,即你代码中的preBindMatrix)
  • w_i:第i个关节对该点的权重(已知,且Σw_i=1)

正向逻辑是:将原始点通过关节绑定逆矩阵转到关节本地空间,再通过当前关节矩阵转到世界空间,最后按权重混合得到蒙皮点。

逆运算的正确方法

逆运算不能直接对单个关节矩阵求逆后加权混合(这是你之前尝试的错误逻辑,因为线性混合的逆不满足逆的线性分配律),需分两种场景处理:

1. 硬绑定(单关节权重=1)

当点仅受一个关节j影响时,逆运算非常直接:

P_bind = M_bind_j * M_current_j^{-1} * P_skinned

逻辑:先把蒙皮点通过当前关节的逆变换,回到关节绑定姿态的本地空间,再通过绑定姿态的关节矩阵,转换回原始绑定空间的位置。

2. 软绑定(多关节加权)

此时需将问题转化为线性方程组求解,步骤如下:

  1. 整理正向公式为线性方程组
    将正向公式展开(忽略齐次坐标w=1的项),得到关于P_bind(x,y,z)的三元一次方程组:

    X = Σ(w_i * (M_i[0][0]x + M_i[0][1]y + M_i[0][2]z + M_i[0][3]))
    Y = Σ(w_i * (M_i[1][0]x + M_i[1][1]y + M_i[1][2]z + M_i[1][3]))
    Z = Σ(w_i * (M_i[2][0]x + M_i[2][1]y + M_i[2][2]z + M_i[2][3]))
    

    其中M_i = M_current_i * M_bind_inv_i,X/Y/Z是P_skinned的坐标。

  2. 构建Ax = b形式

    • 系数矩阵A:提取所有M_i左上3x3部分的加权和
      A = [
          [Σ(w_i*M_i[0][0]), Σ(w_i*M_i[0][1]), Σ(w_i*M_i[0][2])],
          [Σ(w_i*M_i[1][0]), Σ(w_i*M_i[1][1]), Σ(w_i*M_i[1][2])],
          [Σ(w_i*M_i[2][0]), Σ(w_i*M_i[2][1]), Σ(w_i*M_i[2][2])]
      ]
      
    • 常量向量b:蒙皮点坐标减去所有M_i平移项的加权和
      b = [
          X - Σ(w_i*M_i[0][3]),
          Y - Σ(w_i*M_i[1][3]),
          Z - Σ(w_i*M_i[2][3])
      ]
      
    • 未知向量x:就是P_bind(x,y,z)
  3. 求解方程组

    • 如果A可逆,直接计算x = A^{-1} * b
    • 如果A不可逆(比如权重导致矩阵奇异),用最小二乘法求解最优近似解(Maya内置线性代数工具或第三方库可实现)

伪代码实现示例

// 计算整体加权变换矩阵与平移项
MMatrix M_skin;
MVector T_skin(0, 0, 0);
for (int i = 0; i < jointCount; ++i) {
    MMatrix M_current = getCurrentJointMatrix(i);
    MMatrix M_bind_inv = getPreBindMatrix(i);
    MMatrix M_i = M_current * M_bind_inv; // 正确的皮肤矩阵顺序
    float w = getWeight(i);

    M_skin += w * M_i;
    T_skin += w * MVector(M_i[0][3], M_i[1][3], M_i[2][3]);
}

// 构建系数矩阵A(取左上3x3)
MMatrix A = M_skin;
A[0][3] = A[1][3] = A[2][3] = 0;
A[3][0] = A[3][1] = A[3][2] = A[3][3] = 0;

// 构建常量向量b
MVector P_skinned = getSkinnedPoint();
MVector b = P_skinned - T_skin;

// 求解原始点
MVector P_bind;
if (A.isInvertible()) {
    P_bind = A.inverse() * b;
} else {
    // 用最小二乘法求解(示例:Maya的MPxMatrixSolver或第三方库)
    P_bind = solveLeastSquares(A, b);
}

你之前方法错误的核心原因

你修改的mat = preBindMatrix.inverse() * mat.inverse()是对单个关节的变换矩阵求逆,再尝试按原权重混合。但LBS是先变换、再加权,逆运算不能反过来先逆变换、再加权——线性混合的逆操作不满足分配律,这种思路从数学逻辑上就不成立。

内容的提问来源于stack exchange,提问作者MyHornCanPierceTheSky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 18:55:09