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

标签 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_matrix[index1]是一根骨骼的矩阵,bone_matrix[index2]是另一个矩阵。 weight指定vertex_in是这些骨头的成员(member)。问题是权重越接近 0.5,应用旋转时肘部的直径收缩得越多。我已经用大约 10,000 个顶点的圆柱体形状(具有权重梯度)对其进行了测试。结果看起来就像弯曲花园软管。

我从这些来源得到了我的加权方法。它实际上是我能找到的唯一方法:
http://www.opengl.org/wiki/Skeletal_Animation
http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html
http://blenderecia.orgfree.com/blender/skinning_proposal.pdf

initial_ugly_good

左边是形状开始的方式,中间是上述方程如何旋转它,右边是我的目标。中点加权 0.5 .它越弯曲只会变得更糟,在 180 度时它的直径为零。
  • 我已经尝试在着色器上组装矩阵,以便我可以将权重应用于旋转而不是结果顶点。它看起来很完美,就像右图中的那个,但它需要为每个顶点组装矩阵(昂贵)
  • 我已经研究过四元数,但 glsl 本身并不支持它们(如果我错了,请纠正我)并且它们令人困惑。这是我需要做的吗?
  • 我考虑过每个关节有三块骨头,并在每块骨头之间添加一个“膝盖骨”。这不会消除问题,但会减轻它。
  • 我正在考虑在旋转后将顶点投影到与轴的原始距离。这会在 180 度时失败,但会(相对)便宜。

  • 所以考虑我可能没有考虑过的选项或其他选项,其他人如何避免这种挤压效应?

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

    最佳答案

    问题

    Levans 中的图说明了您所看到的原因answer .但是,要了解发生了什么,请考虑执行代码时发生的情况:

    如果第一点vert1有坐标(p, 0) vert2的坐标将是 (p cos(α), p sin(α))哪里α是两个骨骼之间的角度(如果进行适当的坐标变换,这总是可能的)。使用适当的权重将这些加在一起 ​​w1-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 (见 graph)。新向量的长度变小了,这就是你感知到的收缩效果。这样做的原因是我们实际上是沿一条直线对两个顶点进行插值。如果我们希望保持相同的长度 p我们实际上需要沿着围绕旋转中心的圆进行插值。一种可能的方法是重新归一化结果向量,保留关节处的宽度。

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

    我不是骨骼动画专家,但在我看来,您描述的原始解决方案是使用小骨骼角度(收缩效果最小)的近似值。

    替代方法

    另一种方法是插入旋转而不是顶点。例如参见 slerp wiki pagethis paper .

    SLERP

    slerp 技术类似于我上面描述的技术,因为它也保留了关节处的宽度,但是它直接沿着围绕关节的圆形路径进行插值。一般公式为:
    gl_Position = [sin((1-w)α)*vert1 + sin(wα)*vert2]/sin(α)
    

    鉴于上述观点 vert1 = (p, 0)vert2 = (p cos(α), p sin(α))应用 SLERP 公式产生 result = (x, y)和:
    x = p [sin((1-w)α) + sin(wα) cos(α)]/sin(α)
    y = p sin(wα) sin(α)/sin(α) = p sin(wα)
    

    计算余弦 cos θ vert1之间的夹角和 result产量:
    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)α)
    
    vert2之间的夹角和 result是:
    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α)
    

    这意味着 θ/φ = (1-w)/w这表达了 SLERP 以恒定径向速度进行插值的事实。使用 3D 旋转矩阵时,我们可以表达旋转变换 vert1进入 vert2M = inverse(A)*B = transpose(A)*B以便我们可以表达旋转角度α作为:
    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
    

    四元数LERP

    使用四元数时,SLERP 的一个很好的近似方法是直接对四元数进行线性插值,然后对结果进行重新归一化。这给出了与 SLERP 中相同的插值曲线,但是插值不会在恒定径向速度下发生。

    如果你真的想完全避免这些问题,你总是可以在关节处拆分网格并分别旋转它们。

    关于matrix - 将权重应用于矩阵和顶点(骨骼旋转),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25481176/

    相关文章:

    python - Python 类中的 Numpy 矩阵表现出链接行为?

    c++ - 用 block 稀疏矩阵求解大型线性系统

    c - 在 C 中使用多线程的矩阵乘法

    c++ - 动态数组作为纹理 GLSL

    c++ - 骨骼动画 : interpolation between transformation matrices (collada)

    c# - 使用 Assimp 在 OpenGL (GLSL) 中进行骨骼动画

    Matlab:在 ode45 中使用 interp2

    javascript - Threejs数组索引错误: Index expression must be constant

    opengl - 为了向后兼容,您是否应该使用 ARB 扩展而不是核心调用?

    Directx 11 骨骼动画