Matrix 将权重应用于矩阵和顶点(骨骼旋转)

Matrix 将权重应用于矩阵和顶点(骨骼旋转),matrix,glsl,skeletal-animation,Matrix,Glsl,Skeletal Animation,我正在旋转一个低多边形3D图形的网格内骨架的骨骼。在顶点着色器上,其应用如下。 glsl: vec4 vert1 = (bone_matrix[index1]*vertex_in)*weight; vec4 vert2 = (bone_matrix[index2]*vertex_in)*(1-weight); gl_Position = vert1+vert2; bone\u-matrix[index1]是一块骨骼的矩阵,bone\u-matrix[index2]是另

我正在旋转一个低多边形3D图形的网格内骨架的骨骼。在顶点着色器上,其应用如下。
glsl:

    vec4 vert1 = (bone_matrix[index1]*vertex_in)*weight;
    vec4 vert2 = (bone_matrix[index2]*vertex_in)*(1-weight);
    gl_Position =  vert1+vert2;
bone\u-matrix[index1]
是一块骨骼的矩阵,
bone\u-matrix[index2]
是另一块骨骼的矩阵<代码>权重在的成员身份中为这些骨骼指定顶点。问题是重量越接近0.5,应用旋转时弯头的直径收缩越大。我用大约10000个顶点的圆柱体形状(权重梯度)对其进行了测试。结果就像弯曲了花园里的软管

我从这些来源得到了我的加权方法。这实际上是我能找到的唯一方法:


左边是形状如何开始,中间是上面等式如何旋转它,右边是我的目标。中点加权
0.5
。弯曲度越大,情况就越糟,180度时,直径为零

  • 我尝试在着色器上组装矩阵,以便可以将权重应用于旋转,而不是结果顶点。它看起来很像右图中的那个,但它需要为每个顶点组装矩阵(昂贵)
  • 我研究过四元数,但glsl并不支持它们(如果我错了,请纠正我),而且它们令人困惑。这就是我需要做的吗
  • 我考虑过每个关节有三块骨头,在每根骨头之间加一块“膝盖骨”。这不会消除问题,但会缓解问题
  • 我正在考虑在旋转顶点后,将其投影到与轴的原始距离。这将在180度时失效,但(相对)便宜
因此,考虑到我可能没有考虑过的选项或其他选项,其他人如何避免这种挤压效应?


编辑:我让SLERP使用四元数,但我选择不使用它,因为GLSL本身不支持它。我无法让几何SLERP像汤姆描述的那样工作。我让NLERP为前90度工作,所以我在每个关节之间添加了一个额外的“骨骼”。因此,要将前臂弯曲40度,我将肘部和前臂分别弯曲20度。这以骨骼数量翻倍为代价消除了挤压效应,这不是一个理想的解决方案。

免责声明:我不是一个很喜欢3D的人,所以我建议你一种数学方法,可能会对你有所帮助

首先,让我把这个小模式放在这里,这样我们就可以确定我们谈论的是同一件事:

蓝色和绿色图形是原始骨骼,使用
骨骼矩阵[index1]
骨骼矩阵[index2]
完全旋转。红点是旋转的中心,橙色是你想要的,黑色是你拥有的

所以,你认为被构建为蓝色和绿色的加权平均值,在这张图上我们看到了(感谢灰线),为什么它会这样收缩

你需要以某种方式补偿这种收缩,我建议从你的旋转中心缩放点,我们需要在骨骼之间的连接处缩放值2,在末端缩放值1

scale_矩阵
成为预先计算的矩阵:以旋转中心(红点)为中心的振幅2的缩放

最终将使用此着色器:

vec4 vert1 = (bone_matrix[index1]*vertex_in)*weight;
vec4 vert2 = (bone_matrix[index2]*vertex_in)*(1-weight);
vec4 inter =  vert1+vert2;
vec4 scaled1 = inter*(1-2*min(weight, 1-weight));
vec4 scaled2 = (scale_matrix*inter)*(2*min(weight, 1-weight));
gl_Position =  scaled1+scaled2;

恐怕我现在无法测试它(我对GLSL了解不多),但我认为如果某些东西不适合,您可以根据您的情况调整它。

根据您的实际应用,您可能会喜欢此变体:您可以在部件之间添加额外的带,如:

重量以绿色/绿色显示。但是,这需要一些骨骼技巧,因此当你向右弯曲时,你可以使用右侧骨骼并将旋转中心设置为右侧,当向左弯曲时,可以使用左侧骨骼并将旋转中心设置为左侧。

问题出在哪里 你所看到的原因由中的图说明。然而,为了理解正在发生的事情,考虑执行代码时发生的事情:

如果第一个点
vert1
具有坐标
(p,0)
vert2
的坐标将是
(p cos(α),p sin(α))
,其中
α是两个骨骼之间的角度(在适当的坐标变换下,这总是可能的)。使用适当的权重
w
1-w
将它们相加,我们得到以下坐标:

x = w p + (1-w) p cos(α)
y = (1-w) p sin(α)
该向量的长度为:

length^2 = x^2 + y^2
         = (w p + (1-w) p cos(α))^2 + (1-w)^2 p^2 sin(α)^2
         = p^2 [w^2 + 2 w (1-w) cos(α) + (1-w)^2 cos(α)^2 + (1-w)^2 sin(α)^2]
         = p^2 [w^2 + (1-w)^2 + 2 w (1-w) cos(α)]
例如,当
w=1/2
时,这将简化为:

length^2 = p^2 (1/2 + 1/2 cos(α)) = p^2 cos(α/2)^2
length=p | cos(α/2)|
,而原始向量的长度是
p
(参见)。新向量的长度变小,这是您感知到的收缩效果。原因是我们实际上是沿着一条直线插值两个顶点。如果我们想要保持相同的长度
p
,我们实际上需要沿着旋转中心的圆进行插值。一种可能的方法是重新规范化生成的向量,保持关节处的宽度

这意味着我们需要将得到的顶点坐标除以
|cos(α/2)|
(或任意权重的更一般结果)。当然,这有一个副作用,即每当角度正好为180°时,除以零(出于同样的原因,使用您的技术,关节处的宽度为零)

我不是骨骼动画专家,但在我看来,正如您所描述的,原始解决方案是处理小骨骼角度(收缩效果最小)的近似方法

替代办法 另一种方法是插值旋转而不是顶点。例如,请参见和

SLERP

slerp技术与我描述的技术类似
gl_Position = [sin((1-w)α)*vert1 + sin(wα)*vert2]/sin(α)
x = p [sin((1-w)α) + sin(wα) cos(α)]/sin(α)
y = p sin(wα) sin(α)/sin(α) = p sin(wα)
cos(θ) = vert1*result/(|vert1| |result|) = vert1*result/p^2
       = p^2 [sin(wα) + sin((1-w)α) cos(α)]/sin(α)/p^2
       = [sin(α) cos((1-w)α) - cos(α) sin((1-w)α) + sin((1-w)α) cos(α)]/sin(α)
       = cos((1-w)α)
cos(φ) = vert2*result/p^2
       = [sin(wα) cos(α) + sin((1-w)α) cos(α)^2 + sin((1-w)α) sin(α)^2]/sin(α)
       = [sin(wα) cos(α) + sin((1-w)α) cos(α)]/sin(α)
       = [sin(wα) cos(α) + sin(α) cos(wα) - cos(α) sin(wα)]/sin(α)
       = cos(wα)
cos(α) = (tr(M) - 1)/2 = (tr(transpose(A)*B) - 1)/2
       = (A[0][0]*B[0][0] + A[0][1]*B[1][0] + A[0][2]*B[2][0] + 
          A[1][0]*B[0][1] + A[1][1]*B[1][1] + A[1][2]*B[2][1] + 
          A[2][0]*B[0][2] + A[2][1]*B[1][2] + A[2][2]*B[2][2] - 1)/2