glTF Are bone matrices specified in local or model space?

落爺英雄遲暮 提交于 2021-01-28 21:23:53

问题


So the official documentation says:

Note that the node transform is the local transform of the node relative to the joint, like any other node in the glTF node hierarchy as described in the Transformation section.

I am a bit fuzzy as to what exactly the distinction is between node and joint here.

Assuming I can conflate the 2, go to the SimpleSkin exmaple int the models And look at the json:

 "nodes" : [ {
    "skin" : 0,
    "mesh" : 0
  }, {
    "children" : [ 2 ],
    "translation" : [ 0.0, 1.0, 0.0 ]
  }, {
    "rotation" : [ 0.0, 0.0, 0.0, 1.0 ]
  } ],

  "skins" : [ {
    "inverseBindMatrices" : 4,
    "joints" : [ 1, 2 ]
  } ],

joint 1 is the parent of joint 2. So if I treat that hierarchy like any other node hierarchy, the transform of node1 is to be applied to node 2 as well, given the values, at rest node 1 and node 2 rest in the same position.

But when you look at the diagrams for that example:

They are not in the same position. If, however you consider that the transforms are independent of each other (i.e that the transform of joint 1 does not affect joint 2), then joint 1 sits above joint 2, which is at minimum consistent with what the diagrams show (although not consistent with what the animation describes, since it's the top bone that should rotate and in this configuration it's the bottom one that will).


回答1:


Oh man, you're finding all kinds of flaws in the tutorials.

In general yes, "node" and "joint" are the same thing, except they are indexed separately. glTF 2.0 uses array index numbers as identification for many things, and there are pros and cons to that approach. But in this case:

nodes: [ { node index 0 }, { node index 1 }, { node index 2 } ]

Followed by:

joints: [ 1, 2 ]

This means that joint index 0 is represented by node index 1, and joint 1 by node 2. Every joint is a node, but not every node is a joint. This becomes important when reading the JOINTS_0 array, as those will be joint indices. But more importantly, it is written this way because the inverseBindMatrix array has the same length as the joints array, so there is a 1:1 mapping guarantee there.

Let's talk about the jointMatrix for a minute. The tutorial's implementation section has this bit of pseudocode:

jointMatrix(j) =
  globalTransformOfNodeThatTheMeshIsAttachedTo^-1 *
  globalTransformOfJointNode(j) *
  inverseBindMatrixForJoint(j);

But in case you're feeling a little burned by some of the pseudo-ness, here's the corresponding block of code in the sample viewer. It basically amounts to:

jointMatrix[j] = skinnedNode.inverseWorldTransform * 
                 jointNode.worldTransform * inverseBindMatrix[j]

The nodes' transformations are relative to their parents, but the inverseBindMatrix part is specified relative to this glTF model's world. So the host app needs to calculate the node's full transformation relative to glTF scene root, and the result of this calculation will change if any ancestor is animated. Likewise we need to know the inverse of the full world transform for a different node, one that holds the mesh with the skin on it (apparently mis-labeled "parentNode" in the sample viewer, it should be called "skinnedNode" or similar, see call site).

What does this do? We need the "inverseWorldTransform" of the skinned node itself, in case some foolish artist has moved that node somewhere. It needs to come back to the origin for these binding calculations. We of course need the world transform of the joint's node itself, the actual bone, which may be animated or may have been moved by a parent joint's animation, or both. And finally, we need the inverseBindMatrix for the joint, which if the joint hasn't moved itself away from the rest position, will cancel out the joint's position. This all typically runs on the CPU, at most once per frame, or at least whenever the animation system or other outside force causes some joints to move.

Part of the problem being solved here is this: The node that holds the skinned mesh may have a particular place in the scene hierarchy, and may have been moved somewhere. The root of the skeleton might be a different node in the hierarchy, and could be elsewhere. The vertex shader drawing the skinned mesh is trying to draw it where the skinned node says it should be, not where the skeleton root says it should be. That's a problem. We need to get out of the skin's location, get back to the origin, then go down to the joint's world location, and bind the joint there. The results coming out of the vertex shader need to place the skin around the joints, at the joint's location, regardless of where the skinned node thought it was.

The glTF Validator will complain if you place a skinned mesh somewhere other than the root of a scene, with any kind of transform. When a skin is in use, all of the location information comes from the joint locations, not the skin's location. The skin's location is thrown away by including its world inverse in the above calculations. So if the skinned node sits at the origin with no transform on it, that's a whole term you can optimize away from this equation.

But, many glTF models do include skins at non-root locations, so the inverse of the skinned mesh location is included because the vertex shader will be called from that location. Each vertex will make its way over to where the joints actually are, and then further move itself by the difference between the joint's current position and the joint's rest position.




回答2:


Just to complement the answer, this is what the bones are actually supposed to look like:



来源:https://stackoverflow.com/questions/64745393/gltf-are-bone-matrices-specified-in-local-or-model-space

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!