由 SMPL 和 blender 对应关系引起的,发现理顺并不简单,这里总结下。
基于2D旋转的辅助理解
下面是我用于调试的代码
def f_2():
r1, r2, r3 = get_R2d(-45), get_R2d(45), get_R2d(90)
fixp=np.array([1, 0]).reshape(2, 1)
augp = np.array([1, 0, 1]).reshape(3, 1)
a = augp
# 下面这些 t 其实都是父骨骼的相对平移值
m1 = to3d(r1, None) # t = [0, 0]
m2 = to3d(r2, fixp)
m3 = to3d(r3, fixp)
a = m1.dot(m2.dot(m3.dot(a)))
print( a )
’‘’[[1.70710678]
[0.29289322]
[1. ]]‘’‘
def to3d(x, y=None):
A=np.eye(3)
if x is None: return A
if y is not None:
A[:2, :2] = x
A[:2, 2:] = y
return A
a, b = x.shape
if a==2 and b ==1:
A[:2, 2:] = x
return A
if a==2 and b==2:
A[:2, :2] = x
return A
def get_R2d(theta):
theta = np.deg2rad(theta)
c, s = np.cos(theta), np.sin(theta)
return np.array( [c, -s, s, c] ).reshape(2, 2) # 逆时针
解释一下f_2是主要部分,就是画了三个骨骼
然后第一个骨骼旋转-45度,第二个接着转 45,最后一个转90。
然后代码中使用的是相对旋转,可以看到用的是左乘变换矩阵,而且最后用的是相对位置。
就是先绕骨骼根部旋转,然后加上初始时相对父骨骼的距离。
(理解时也可以按照,尾节点是最终待求的点的位置,然后绕着头节点转,加的平移是头节点在父骨骼上相对于对父骨骼中头节点的距离)
(上述理解也就是为什么主要关注的是骨骼数目而不是关节点数,因为最终求的是变换矩阵,叶子节点是要被网格顶点代替的)
当然也可以用绝对位置理解,比如所有点绕着根节点旋转,然后接着往下,这样旋转矩阵乘的顺序就会相反,期间也要记录相关joint的位置。
blender 中,顶点与骨骼的对应关系
下面两张图,说明在 blender 中其实 bone[n].head 与 显示的 joint[n] 是一致的,但是bone[n].tail 却因为建模的原因还有自己的一个点
blender 程序操作的注意事项
首先要知道我们一般用的是 ob.pose.bones 这是个dict 通过 nm 索引。
bone=ob.pose.bones[bnm]
bone.location # 这个描述的是在edit模式下定义了骨骼后,从那个位置开始移动多少
bone.matrix #描述的是相对于父节点的变换(bone.parent)
bone.matrix.translation # 距离父节点的距离
# 推导
bone.location = bone.matrix.translation # 就说明要对齐到父节点的骨骼上
# 假如要让骨骼对齐到世界坐标的某个点 joint,而且这个骨骼只有一个父节点
bone.location = joint - bone.matrix.translation
# 但是上面这个的前提是
armature.matrix_world # 骨骼到世界坐标系的变换