【问题标题】:Godot - Bone doesn't rotate on the correct axisGodot - 骨骼不在正确的轴上旋转
【发布时间】:2022-01-05 11:03:00
【问题描述】:

我正在尝试将 Godot 中的骨骼旋转一个角度和一个我已经计算过的轴。我通过旋转网格实例测试了这个角度和轴,它工作得很好。我检查了计算,它们看起来是正确的(通过取构成角度的两个向量的叉积来找到轴)。但是,当我尝试将骨骼旋转相同的值时,它会旋转正确的角度,但轴突然不同(它应该向上旋转,但它向左旋转)。这是我的代码有问题,还是 3d 模型/骨架制作不正确?

这是我的代码:

# the vector is the Mesh Instance, and the rotation is correct
vector.rotate(axis, angle)

# the bone is rotated by the same values, but the axis is wrong
bonePose = skel.get_bone_pose(id)
bonePose = bonePose.rotated(axis, angle)
skel.set_bone_pose(id, bonePose)

【问题讨论】:

  • 想到的是检查其他姿势是否搞砸了(休息姿势和自定义姿势,尤其是休息姿势)。
  • @Theraot 听起来可能是这样。你知道是否可以全局旋转骨骼吗?

标签: godot gdscript


【解决方案1】:

您可以编写全局姿势覆盖:

bonePose = skel.get_bone_global_pose(id)
bonePose = bonePose.rotated(axis, angle)
skel.set_bone_global_pose_override(id, bonePose, 1.0)

第三个​​参数amount 是一个权重。如果为 0,则保持原样,如果为 1.0,则替换它。中间的值是插值。还有第四个可选参数persistent,它指定它是否应该在下次更新骨架时保持姿势覆盖(它应该使用更新的姿势计算插值)。

如果这对你有用,那就太好了。


回答问题对我来说更有趣的是,如果没有set_bone_global_pose_override,如何做到这一点。 将此视为学术练习。

骨骼的全局姿势是父级的全局姿势乘以骨骼的姿势。让我们表达一下:

b.global_pose = parent.global_pose * b.pose

其实如果有自定义pose的话就是:

b.global_pose = parent.global_pose * b.custom_pose * b.pose

如果有休息姿势,那就是:

b.global_pose = parent.global_pose * b.rest_pose * b.custom_pose * b.pose

而且我们要修改全局pose,但是只能控制自定义pose和pose。

其实没有理由去修改pose,因为对pose应用一个transform就相当于设置一个自定义pose。

现在,问题是我们可以设置什么自定义姿势X,因此它相当于将变换T 应用于全局姿势:

T * b.global_pose = parent.global_pose * b.rest_pose * X * b.pose

求解 X。提醒:变换乘法不可交换。

首先在两侧应用父级全局姿势的反转(它们在右侧取消):

inverse(parent.global_pose) * T * b.global_pose = b.rest_pose * X * b.pose

然后是双方的其余姿势:

inverse(b.rest_pose) * inverse(parent.global_pose) * T * b.global_pose = X * b.pose

然后两边都按倒置的姿势:

inverse(b.rest_pose) * inverse(parent.global_pose) * T * b.global_pose * inverse(b.pose) = X

这就是我们需要如何计算新的自定义姿势。代码是这样的:

var inverse_rest = skel.get_bone_rest(id).affine_inverse()

var parent = ske.get_bone_parent(id)
var inverse_parent_global = Transform.IDENTITY
if parent != -1:
    inverse_parent_global = skel.get_bone_global_pose(parent).affine_inverse()

var global_pose = skel.get_bone_global_pose(id)

var inverse_pose = skel.get_bone_pose(id).affine_inverse()

var t = Transform.IDENTITY.rotated(axis, angle) # the operation you want to do

var x = inverse_rest * inverse_parent_global * t * global_pose * inverse_pose

skel.set_bone_custom_pose(id, x)

或者如果你想设置姿势而不是自定义姿势(假设自定义仍然是身份转换):

var inverse_rest = skel.get_bone_rest(id).affine_inverse()

var parent = ske.get_bone_parent(id)
var inverse_parent_global = Transform.IDENTITY
if parent != -1:
    inverse_parent_global = skel.get_bone_global_pose(parent).affine_inverse()

var global_pose = skel.get_bone_global_pose(id)

var t = Transform.IDENTITY.rotated(axis, angle) # the operation you want to do

var x = inverse_rest * inverse_parent_global * t * global_pose

skel.set_bone_pose(id, x)

【讨论】:

  • @Theraot 感谢您长时间的指导性回答!使用.set_bone_global_pose_override() 对我有用,但由于某种原因,骨头现在被拉长了。另外我发现这个方法需要有两个额外的参数.set_bone_global_pose_override(id, bonePose, 1.0, true)否则它不会设置姿势。
  • @DB 添加了对这些参数的提及。我已经忘记了他们。我不知道为什么它会导致骨头伸展。我想可能有一些东西确实缩小了不再起作用的骨头。但是,我希望如果它被缩小,get_bone_global_pose 会反映这一点。也许尝试在一个最小的项目中重现这种情况会有助于弄清楚这一点。
【解决方案2】:

还有另一种方法可以在没有拉伸的情况下使用.set_bone_global_pose_override()。事实证明,轴在计算它的全局空间中是正确的,但对于骨骼来说是错误的,因为它相对于骨架局部旋转。当轴从全局空间转换到骨骼的局部空间时,旋转是我所期望的。我仍然不确定为什么其他方法会导致拉伸。

代码如下:

# transform from global space to local space
var bone_to_global = skel.global_transform * skel.get_bone_global_pose(bone)
var axis_local = bone_to_global.basis.transposed() * axis_global

# rotate the bone
bonePose = skel.get_bone_pose(bone)
bonePose = bonePose.rotated(axis_local.normalized(), angle)
skel.set_bone_pose(bone, bonePose)

【讨论】:

  • 谁想知道axis_global 的来源:您可以将其设置为var axis_global = Vector3.UP 或var axis_global = Vector3.FORWARD 或任何您需要的东西。
猜你喜欢
  • 2021-01-20
  • 2021-01-12
  • 1970-01-01
  • 1970-01-01
  • 2013-11-03
  • 1970-01-01
  • 2020-08-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多