如何逆运算线性混合蒙皮(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. 软绑定(多关节加权)
此时需将问题转化为线性方程组求解,步骤如下:
整理正向公式为线性方程组
将正向公式展开(忽略齐次坐标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的坐标。构建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)
- 系数矩阵
求解方程组
- 如果
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
相关产品推荐
相关产品推荐

